天天看點

Keycloak 入門使用第一篇

Keycloak入門使用第一篇

    • 簡介
    • 安裝 & 啟動伺服器
    • 專有名詞 & 基本使用
    • 內建Keycloak
    • 了解運作流程
    • Reference

簡介

Keycloak 為現代應用和分布式服務提供了一套完整的認證授權管了解決方案,它是開源的,是一個獨立的認證授權伺服器。它主要是基于OAuth2協定的實作,同時提供了多種語言庫,讓我們可以很快速地根據我們的需求将Keycloak內建到我們的項目中去使用。

這裡主要介紹Keycloak的一些基本使用實踐,在學習Keycloak之前,最好要先了解OAuth2的協定流程,否則比較難以了解keycloak的認證過程。在熟悉了OAuth2協定以後去學習Keycloak,其實就很簡單了。

若想了解OAuth2協定,可以看這篇 了解OAuth2協定

下面列出的是我所知道的Keycloak相關的内容,下面會詳細介紹其中的某一些:

  • 是一個獨立的認證授權伺服器,提供完整的認證授權解決方案
  • 主要基于OpenID-Connect & SAML協定
  • 基本的登入注冊,以及登入注冊頁面主題自定義
  • 很人性化的使用者界面管理,比如使用者、角色、session、Clients等等的管理
  • 具有獨立的資料庫,用于存儲使用者等認證授權資料
  • 支援聯合資料存儲,比如內建Ldap伺服器;提供SPI擴充,比如user Storage SPI,可以讓使用者的一部分資料存儲在你自己的資料庫,一部分存儲在keycloak自己的資料庫
  • 提供多種語言庫內建keycloak
  • 提供管理API,用于管理keycloak中所有的認證授權對象
  • Docker-compose一鍵安裝,同時windows解壓版解壓後即可使用

安裝 & 啟動伺服器

  • 解壓版安裝

    – 下載下傳位址: https://downloads.jboss.org/keycloak/11.0.3/keycloak-11.0.3.zip

    https://www.keycloak.org/downloads

    – 預設是使用H2資料庫存儲資料,如果你想要換成其他資料庫可以參考:

    https://www.cnblogs.com/ifme/p/12588910.html

    添加連結描述

    Keycloak 入門使用第一篇

    解壓後可以看到上面的檔案目錄。

    Keycloak提供多種模式啟動,standalone單機啟動,domain叢集啟動。我們這裡使用單機啟動,進去到bin目錄,找到standalone.bat, 輕按兩下啟動。啟動後通路 http://localhost:8080,正常通路說明啟動成功,如下:

    Keycloak 入門使用第一篇
    點選Administration Console, 第一次需要給admin使用者建立密碼。
  • docker-compoe安裝

    – Docker安裝: https://github.com/keycloak/keycloak-containers/tree/master/docker-compose-examples

    安裝使用很簡單,這裡就累述了。

專有名詞 & 基本使用

當你啟動的keyclaok伺服器,然後使用admin賬号登入以後,進去到keycloak管理頁面:

Keycloak 入門使用第一篇

這是Master Realm的settings頁面,Master Realm是keycloak預設有的realm,顧名思義就是用于管理的realm,例如admin這個使用者就是屬于這個realm:

Keycloak 入門使用第一篇

那Realm是什麼意思呢?Realm字面意思是領域,指的是在某一個軟體業務領域中所涉及的使用者認證授權管理相關的對象,在這個realm下有使用者、角色、會話(session)等等用于認證授權管理的對象。

假設一個公司A使用一個ERP系統,那麼就可以給這個公司A設定一個Realm,用于該公司所有員工的授權管理。那麼如果另一個公司B也使用這個ERP系統(假設這個ERP系統是第三方提供給所有公司使用的一個ERP服務,就需要給公司B也建立一個Realm,用于公司B員工的授權管理。

是以Realm之間的互相隔離的一個業務領域概念。

  • 建立一個Realm
    Keycloak 入門使用第一篇

    設定一個名字,然後你就可以給你自己的realm設定各種對象了。比如建立使用者,建立一個角色,給使用者授予角色等等。

    例如:我建立一個USER的角色,建立一個testuser01使用者,然後給這個使用者授予USER角色:

    Keycloak 入門使用第一篇
    界面使用非常簡單,大家探索一下就行了。

內建Keycloak

Keycloak 入門使用第一篇

假設我們有兩個web伺服器,我們需要使用keycloak來對我們的資源進行保護,隻有使用者登入以後才能通路到這兩個伺服器的資源,否則就要跳轉到登入頁面。是以我們要在兩個服務之前加一個gateway層,在這一層對使用者請求進行攔截,驗證使用者是否已經登入(了解OAuth2的話,就知道這裡就是驗證accessToken),如果沒有的話,就要引導使用者去到keycloak登入頁面,認證以後再跳轉回到要通路的頁面。

是以,我們已經啟動了keycloak伺服器,缺少的是怎麼将攔截使用者請求并驗證accessToken這些邏輯加入到我們的應用中。别急,keycloak官方給我們提供了這些庫,它把這個東西叫做 adaptor(connector),是以我們隻要将這些庫安裝到我們的項目中就可以為我們的應用實作認證授權。

這裡呢,我實踐的是給nodejs server內建keycloak認證授權,是以以下介紹的是 Nodejs-keycloak-adaptor.

keycloak-nodejs-adaptor 我也是參考官方的例子去實作的。

另外keycloak官方有一些quickstart的最佳實踐,參考:keycloakQuickstart

  • 注冊一個client

    第一步首先要在你的realm下注冊一個client:

    Keycloak 入門使用第一篇

    選擇clients,點選create,輸入clientId,點選save以後出現該client的settings頁面,Access Type選擇confidential,Valid Redirect URLs必須要填,這個填入你要內建keycloak的那個web伺服器的首頁,例如http://localhost:3000/*即可。在OAuth2協定中,redirectURI是必填的,而在keycloak這裡,意思是回調的redirectURI必須要match上這裡配置的Valid Redirect URLs才可以,否則會報錯。

    例如上面填的是http://localhost:3000/*,http://localhost:3000/users就可以match,而http://localhost:3001/users就不會match,就會報錯。

點選Save,然後切換到最後一欄installation,拿到client的連接配接資訊:

Keycloak 入門使用第一篇
  • 初始化nodejs項目
  1. 使用npm init初始化一個nodejs項目,加入如下依賴:
"body-parser": "^1.13.3",
    "cors": "^2.8.1",
    "express": "^4.13.3",
    "express-session": "^1.14.2",
    "jsonwebtoken": "^8.5.1",
    "keycloak-admin": "^1.13.0",
    "keycloak-connect": "11.0.2",
           

其中keycloak-connect就是內建keycloak的連接配接庫

  1. 建立一個keycloak-config.js,将你的的installation連接配接資訊替換替換對應的屬性,例如上面的資訊就是:
module.exports = {
  realm: 'myRealm',
  authServerUrl: 'http://localhost:8080/auth/',
  clientId: 'testClient',
  credentials: {
    secret: '4434b959-835b-45c9-bed5-607e025e0100'
  },
  bearerOnly: false
}
           

authServerUrl記得最後加上/,這裡是一個坑,不加可能報錯。以上拿到的連接配接資訊,其實resource就是clientId,對應OAuth2中的clientId和clientSecret(credentials.secret)。是以說,如果你熟悉OAuth2,來到這裡很容易就知道看到這就是client必須要有的基本資訊。

  1. 建立一個app-express.js (名字随你)

直接貼出全部代碼:

const express = require('express');
const path = require('path');
const bodyParser = require('body-parser')
const session = require('express-session')
const KeycloakConnect = require('keycloak-connect')
const keycloakConfig = require('./keycloak-config')
const jwt = require('jsonwebtoken')
const  router = express.Router();

// keycloak
const memoryStore = new session.MemoryStore();
const keycloak = new KeycloakConnect({store: memoryStore}, keycloakConfig)

const app = express();
app.use(bodyParser.json());

app.use(session({
    secret: 'some secret',
    resave: false,
    saveUninitialized: true,
    store: memoryStore
}));

app.use(keycloak.middleware({
    logout: '/logout',
    admin: '/'
}));

// api settings
router.get('/service/public', (req, res) => {
    res.json({message: 'public'});
});
router.get('/service/secured', keycloak.protect('realm:USER'), function (req, res) {
    res.json({message: 'secured'});
});

const keycloakProtect = keycloak.protect()
router.get('*', (req, resp, next) => {
    const originalUrl = req.originalUrl
    if (originalUrl.indexOf('abc') > -1 || originalUrl === '/' || originalUrl.indexOf('favicon.ico') > -1) {
        return next()
    }
    return keycloakProtect(req, resp, next)
}, (req, resp, next) => {
    const keycloakToken = req.session['keycloak-token']
    let userId = 'test'
    if (keycloakToken) {
        userId = jwt.decode(JSON.parse(keycloakToken).access_token).preferred_username
    }
    const cookiesOption = {
        maxAge: 1000 * 60 * 60 * 24,
        httpOnly: false
    }
    resp.cookie('user_id', userId, cookiesOption)
    return next()
})

app.use(router)
// static resources
app.use(express.static(path.join(__dirname, '/views')));


app.listen(3001, () => {
    console.log('Started at port 3001');
});
           

這裡是使用express啟動一個nodejs server

const keycloak = new KeycloakConnect({store: memoryStore}, keycloakConfig)

const app = express();
app.use(bodyParser.json());

app.use(session({
    secret: 'some secret',
    resave: false,
    saveUninitialized: true,
    store: memoryStore
}));

app.use(keycloak.middleware({
    logout: '/logout',
    admin: '/'
}));
           

建立KeycloakConnect對象,傳進去sessionStore以及keycloakConfig。注意這裡keycloak的store使用的是express-session的store,因為認證成功以後keycloak就會将所有的token的資訊存儲在session中。

最後就是use keycloak提供的中間件,檢視這些中間源碼你就會知道,這些中間件所做的事情其實就是首先會從session拿出accessToken,然後認證accessToken的有效性等等。

keycloak.protect() 在你的router api中加入這個中間件,就能夠對你這個API進行認證保護,後面的代碼相信大家一看就明白了,這裡不多說。(Talk is cheap, show you the code)

完整代碼檢視:Github (有些代碼沒有放出來)

運作項目,通路http://localhost:3000/,可以正常通路;通路http://localhost:3000/work.html,會跳轉到登入頁面。

Keycloak 入門使用第一篇

了解運作流程

那麼對應上面的這個例子呢,可能你确實能成功保護你的資源了,但是我們有必要去了解一下整一個認證過程,這個驗證過程其實就是OAuth2的授權碼模式:

Keycloak 入門使用第一篇

在第2步,web server将使用者導向keycloak server的重定向連接配接:

http://localhost:8080/auth/realms/myRealm/protocol/openid-connect/auth?client_id=nodejs&state=10abdacb-5e85-4f48-8bf7-b8aedb378567&redirect_uri=http%3A%2F%2Flocalhost%3A3001%2Fwork.html%3Fauth_callback%3D1&scope=openid&response_type=code

可以看到使用的就是OAuth2授權碼模式:response_type=code

在第3步中keycloak-server将使用者導向回web-server的重定向連接配接:

http://localhost:3001/work.html?auth_callback=1&state=10abdacb-5e85-4f48-8bf7-b8aedb378567&session_state=3f47f74a-ef0c-4245-b87e-22acae8b08e9&code=281cd9c6-187e-423f-84ee-9419aa4dab7d.3f47f74a-ef0c-4245-b87e-22acae8b08e9.aa9ee788-cbb7-4370-9ed2-6de8b2b510b7

  • 通過debug檢視存儲在session中的keycloak-token資訊:
    Keycloak 入門使用第一篇

    傳回了三個token:accessToken、refreshToken、idToken

    accessToken 用于每次通路資源伺服器時驗證使用者權限,refreshToken用于在accessToken過期以後可以通過它重新想keycloak-server申請一個新的accessToken,而不需求重新登入。idToken用于識别使用者身份,這個OpenID-Connect協定新加入的一個token。

  • 補充

    這個例子是基于express,如果你的node-server是使用Koa的話,官方是沒有提供Koa相關connector的,如果有需要可以參考我的github,裡面有Koa的keycloak例子。啊哈哈哈哈!!

    Github 代碼

後面還有一篇哦 入門第二篇

Reference

https://github.com/keycloak

https://www.keycloak.org/

繼續閱讀