Keycloak快速上手指南,只需10分鐘即可接入Spring Boot/Vue前後端分離應用實現SSO單點登入

vigoz發表於2020-06-02

登入及身份認證是現代web應用最基本的功能之一,對於企業內部的系統,多個系統往往希望有一套SSO服務對企業使用者的登入及身份認證進行統一的管理,提升使用者同時使用多個系統的體驗,Keycloak正是為此種場景而生。本文將簡明的介紹Keycloak的安裝、使用,並給出目前較流行的前後端分離應用如何快速接入Keycloak的示例。

Keycloak是什麼

Keycloak是一種面向現代應用和服務的開源IAM(身份識別與訪問管理)解決方案

Keycloak提供了單點登入(SSO)功能,支援OpenID ConnectOAuth 2.0SAML 2.0標準協議,擁有簡單易用的管理控制檯,並提供對LDAP、Active Directory以及Github、Google等社交賬號登入的支援,做到了非常簡單的開箱即用。

Keycloak常用核心概念介紹

首先通過官方的一張圖來了解下整體的核心概念

keycloak-core-concepts

這裡先只介紹4個最常用的核心概念:

  1. Users: 使用者,使用並需要登入系統的物件

  2. Roles: 角色,用來對使用者的許可權進行管理

  3. Clients: 客戶端,需要接入Keycloak並被Keycloak保護的應用和服務

  4. Realms: 領域,領域管理著一批使用者、證照、角色、組等,一個使用者只能屬於並且能登陸到一個域,域之間是互相獨立隔離的, 一個域只能管理它下面所屬的使用者

Keycloak服務安裝及配置

安裝Keycloak

Keycloak安裝有多種方式,這裡使用Docker進行快速安裝

docker run -d --name keycloak \
    -p 8080:8080 \
    -e KEYCLOAK_USER=admin \
    -e KEYCLOAK_PASSWORD=admin \
    jboss/keycloak:10.0.0

訪問http://localhost:8080並點選Administration Console進行登入

keycloak-web-home

建立Realm

建立一個新的realm: demo,後續所有的客戶端、使用者、角色等都在此realm中建立

keycloak-web-add-realm-1

keycloak-web-add-realm-2

keycloak-web-add-realm-3

建立客戶端

建立前端應用客戶端

建立一個新的客戶端:vue-demo,Access Type選擇public

keycloak-web-add-client-1

建立後端應用客戶端

建立一個新的客戶端:spring-boot-demo,Access Type選擇bearer-only

keycloak-web-add-client-2

儲存之後,會出現Credentials的Tab,記錄下這裡的secret,後面要用到

keycloak-web-add-client-3

關於客戶端的訪問型別(Access Type)

上面建立的2個客戶端的訪問型別分別是public、bearer-only,那麼為什麼分別選擇這種型別,實際不同的訪問型別有什麼區別呢?

事實上,Keycloak目前的訪問型別共有3種:

confidential:適用於服務端應用,且需要瀏覽器登入以及需要通過金鑰獲取access token的場景。典型的使用場景就是服務端渲染的web系統。

public:適用於客戶端應用,且需要瀏覽器登入的場景。典型的使用場景就是前端web系統,包括採用vue、react實現的前端專案等。

bearer-only:適用於服務端應用,不需要瀏覽器登入,只允許使用bearer token請求的場景。典型的使用場景就是restful api。

建立使用者和角色

建立角色

建立2個角色:ROLE_ADMIN、ROLE_CUSTOMER

keycloak-web-add-role

建立使用者

建立2個使用者:admin、customer

keycloak-web-add-user

繫結使用者和角色

給admin使用者分配角色ROLE_ADMIN

keycloak-web-add-user-role-1

給customer使用者分配角色ROLE_CUSTOMER

keycloak-web-add-user-role-2

Vue應用整合Keycloak簡明指南

建立vue專案

vue create vue-demo

新增官方Keycloak js介面卡

npm i keycloak-js --save
npm i axios --save

main.js

import Vue from 'vue'
import App from './App.vue'
import Keycloak from 'keycloak-js'

Vue.config.productionTip = false

// keycloak init options
const initOptions = {
  url: 'http://127.0.0.1:8080/auth',
  realm: 'demo',
  clientId: 'vue-demo',
  onLoad:'login-required'
}

const keycloak = Keycloak(initOptions)

keycloak.init({ onLoad: initOptions.onLoad, promiseType: 'native' }).then((authenticated) =>{
  if(!authenticated) {
    window.location.reload();
  } else {
    Vue.prototype.$keycloak = keycloak
    console.log('Authenticated')
  }

  new Vue({
    render: h => h(App),
  }).$mount('#app')

  setInterval(() =>{
    keycloak.updateToken(70).then((refreshed)=>{
      if (refreshed) {
        console.log('Token refreshed');
      } else {
        console.log('Token not refreshed, valid for '
            + Math.round(keycloak.tokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds');
      }
    }).catch(error => {
      console.log('Failed to refresh token', error)
    })
  }, 60000)

}).catch(error => {
  console.log('Authenticated Failed', error)
})

HelloWorld.vue

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <div>
      <p>
        current user: {{user}}
      </p>
      <p>
        roles: {{roles}}
      </p>
      <p>
        {{adminMsg}}
      </p>
      <p>
        {{customerMsg}}
      </p>
    </div>
  </div>
</template>

<script>
import axios from 'axios'

export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  data() {
    return {
      user: '',
      roles: [],
      adminMsg: '',
      customerMsg: ''
    }
  },
  created() {
    this.user = this.$keycloak.idTokenParsed.preferred_username
    this.roles = this.$keycloak.realmAccess.roles

    this.getAdmin()
            .then(response=>{
              this.adminMsg = response.data
            })
            .catch(error => {
              console.log(error)
            })

    this.getCustomer()
            .then(response => {
              this.customerMsg = response.data
            })
            .catch(error => {
              console.log(error)
            })
  },
  methods: {
    getAdmin() {
      return axios({
        method: 'get',
        url: 'http://127.0.0.1:8082/admin',
        headers: {'Authorization': 'Bearer ' + this.$keycloak.token}
      })
    },
    getCustomer() {
      return axios({
        method: 'get',
        url: 'http://127.0.0.1:8082/customer',
        headers: {'Authorization': 'Bearer ' + this.$keycloak.token}
      })
    }
  }
}
</script>

getAdmin()getCustomer()這2個方法內部分別請求restful api

Spring Boot應用整合Keycloak簡明指南

新增Keycloak Maven依賴

<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-spring-boot-starter</artifactId>
    <version>10.0.0</version>
</dependency>

Spring Boot配置檔案

官方文件及網上大部分示例使用的都是properties格式的配置檔案,而yaml格式的配置檔案相對更簡潔清晰些,此示例使用yaml格式的配置檔案,內容如下

server:
  port: 8082
keycloak:
  realm: demo
  auth-server-url: http://127.0.0.1:8080/auth
  resource: spring-boot-demo
  ssl-required: external
  credentials:
    secret: 2d2ab498-7af9-48c0-89a3-5eec929e462b
  bearer-only: true
  use-resource-role-mappings: false
  cors: true
  security-constraints:
    - authRoles:
        - ROLE_CUSTOMER
      securityCollections:
        - name: customer
          patterns:
            - /customer
    - authRoles:
        - ROLE_ADMIN
      securityCollections:
        - name: admin
          patterns:
            - /admin

除了幾個必填的配置項外,另外需要注意的幾個配置項如下

credentials.secret:上文新增客戶端後Credentials Tab內對應的內容

bearer-only:設定為true,表示此應用的Keycloak訪問型別是bearer-only

cors:設定為true表示允許跨域訪問

security-constraints:主要是針對不同的路徑定義角色以達到許可權管理的目的

  • /customer:只允許擁有ROLE_CUSTOMER角色的使用者才能訪問
  • /admin:只允許擁有ROLE_ADMIN角色的使用者才能訪問
  • 未配置的路徑表示公開訪問

Controller內容

@RestController
public class HomeController {
    @RequestMapping("/")
    public String index() {
        return "index";
    }

    @RequestMapping("/customer")
    public String customer() {
        return "only customer can see";
    }

    @RequestMapping("/admin")
    public String admin() {
        return "only admin cas see";
    }
}

專案效果演示

分別啟動前後端專案後,本地8081埠對應vue前端專案,本地8082埠對應Spring Boot實現的restful api專案

首次訪問vue前端專案

第一次訪問vue專案會跳轉Keycloak登入頁

keycloak-demo-1

登入admin使用者

keycloak-demo-2

登入customer使用者

keycloak-demo-3

總結

Keycloak部署及接入簡單,輕量的同時功能又不失強大,非常適合企業內部的SSO方案。

本文示例專案地址:keycloak-demo

相關文章