Chuyển đến nội dung chính

第 5 課:角色、權限和存取控制

領域角色、客戶端角色、複合角色、使用者和群組的角色映射、預設角色、服務帳戶角色。細粒度管理權限V2、領域管理委派、特定資源的權限、策略和權限評估。

Keycloak RBAC & Fine-grained Permissions

Keycloak 中的 RBAC 和細微管理員權限 V2 模型

1.Keycloak中的角色概述

Keycloak 中的角色是分散存取的主要機制。應用程式檢查使用者的角色(透過令牌中的聲明)來決定允許使用者執行哪些操作。 Keycloak supports two types of roles:領域角色和客戶角色.

領域角色與客戶端角色

特徵領域角色客戶角色
範圍整個領域僅針對特定客戶
使用案例一般角色(管理員、使用者、經理)應用程式特定角色(編輯者、檢視者)
命名空間領域獨一無二在客戶端獨一無二
代幣領取領域訪問角色resources_access.{client}.roles

2. 領域角色

2.1 預設領域角色

Keycloak 提供了許多領域角色:

  • 預設角色-{領域}— 複合角色包含新使用者的預設角色

  • 離線訪問— 允許取得離線令牌(長期令牌刷新)

  • uma_授權— 允許使用 UMA(使用者管理存取)

2.2 創建領域角色

透過管理控制台:

  1. 點選領域角色在側邊欄中

  2. 點選創建角色

  3. 進入:

    • 角色名稱: 行政。行政
    • 描述: 完全管理員存取權限
  4. 點選節省

透過管理 CLI:

# Tạo realm roles
bin/kcadm.sh create roles -r my-company -s name=admin -s description="Full administrator access"
bin/kcadm.sh create roles -r my-company -s name=manager -s description="Manager with limited admin access"
bin/kcadm.sh create roles -r my-company -s name=user -s description="Regular user"
bin/kcadm.sh create roles -r my-company -s name=viewer -s description="Read-only access"

Xem danh sách realm roles

bin/kcadm.sh get roles -r my-company --fields name,description

透過 REST API:

# Tạo realm role
curl -s -X POST \
  "http://localhost:8080/admin/realms/my-company/roles" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "admin",
    "description": "Full administrator access",
    "composite": false
  }'

Lấy danh sách realm roles

curl -s -X GET
"http://localhost:8080/admin/realms/my-company/roles"
-H "Authorization: Bearer $ACCESS_TOKEN" | jq '.[].name'

3. 客戶角色

當應用程式需要自己的角色時,使用客戶端角色 - 例如,CMS 應用程式具有角色編輯, 作者, 審稿人與HR應用程式的角色不同。

3.1 建立客戶端角色

透過管理控制台:

  1. 進入客戶→ 選擇客戶端(例如:我的網頁應用程式)

  2. 選項卡角色

  3. 點選創建角色

  4. 輸入名稱和描述

透過 CLI:

# Lấy client ID
CLIENT_UUID=$(bin/kcadm.sh get clients -r my-company -q clientId=my-web-app --fields id --format csv --noquotes)

Tạo client roles

bin/kcadm.sh create clients/$CLIENT_UUID/roles -r my-company
-s name=editor -s description="Can create and edit content"

bin/kcadm.sh create clients/$CLIENT_UUID/roles -r my-company
-s name=author -s description="Can create content"

bin/kcadm.sh create clients/$CLIENT_UUID/roles -r my-company
-s name=reviewer -s description="Can review and approve content"

bin/kcadm.sh create clients/$CLIENT_UUID/roles -r my-company
-s name=content-admin -s description="Full content management"

Xem client roles

bin/kcadm.sh get clients/$CLIENT_UUID/roles -r my-company --fields name,description

透過 REST API:

curl -s -X POST \
  "http://localhost:8080/admin/realms/my-company/clients/$CLIENT_UUID/roles" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "editor",
    "description": "Can create and edit content"
  }'

3.2 Token 中的客戶端角色

客戶端角色出現在宣告下的存取權杖中資源訪問:

{
  "realm_access": {
    "roles": ["user", "offline_access"]
  },
  "resource_access": {
    "my-web-app": {
      "roles": ["editor", "author"]
    },
    "my-api": {
      "roles": ["read", "write"]
    },
    "account": {
      "roles": ["manage-account", "view-profile"]
    }
  }
}

4. 複合角色

複合角色是包含一個或多個子角色(領域角色和/或客戶端角色)的角色。當為使用者指派複合角色時,該使用者自動擁有所有子角色。

4.1 創建複合角色

透過管理控制台:

  1. 進入領域角色→ 選擇角色(例如:行政。行政)

  2. 選項卡行動 → 新增關聯角色

  3. 選擇要新增的角色(領域角色和/或客戶端角色)

  4. 點選分配

透過 CLI:

# Tạo composite role: "manager" chứa "user" và "viewer"
bin/kcadm.sh add-roles \
  -r my-company \
  --rname manager \
  --rolename user \
  --rolename viewer

Thêm client roles vào composite role

bin/kcadm.sh add-roles
-r my-company
--rname manager
--cclientid my-web-app
--rolename editor
--rolename reviewer

層次結構範例:

admin (composite)
├── manager (composite)
│   ├── user (realm role)
│   ├── viewer (realm role)
│   ├── my-web-app/editor (client role)
│   └── my-web-app/reviewer (client role)
├── my-web-app/content-admin (client role)
└── account/manage-account (client role)

筆記:當使用者被指派角色時行政。行政,用戶將會擁有它全部樹層次結構中的角色:行政。行政, 主管, 用戶.用戶, 觀眾。觀眾, 編輯, 審稿人, 內容管理, 管理帳號.

4.2 查看複合角色

# Xem roles con của composite role
bin/kcadm.sh get-roles -r my-company --rname admin --effective

REST API

curl -s -X GET
"http://localhost:8080/admin/realms/my-company/roles/admin/composites"
-H "Authorization: Bearer $ACCESS_TOKEN" | jq '.[].name'

5. 角色映射

5.1 為使用者分配角色

透過管理控制台:

  1. 進入使用者→ 選擇用戶

  2. 選項卡角色映射

  3. 點選分配角色

  4. 選擇領域角色或按客戶端過濾以選擇客戶端角色

  5. 點選分配

透過 CLI:

# Gán realm roles cho user
bin/kcadm.sh add-roles \
  -r my-company \
  --uusername john.doe \
  --rolename admin

Gán client roles cho user

bin/kcadm.sh add-roles
-r my-company
--uusername john.doe
--cclientid my-web-app
--rolename editor

Xem roles của user (bao gồm effective roles từ composite và groups)

bin/kcadm.sh get-roles
-r my-company
--uusername john.doe
--effective

透過 REST API:

# Lấy role representation
ROLE_ID=$(curl -s -X GET \
  "http://localhost:8080/admin/realms/my-company/roles/admin" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | jq -r '.id')

ROLE_NAME=$(curl -s -X GET
"http://localhost:8080/admin/realms/my-company/roles/admin"
-H "Authorization: Bearer $ACCESS_TOKEN" | jq -r '.name')

Gán realm role cho user

curl -s -X POST
"http://localhost:8080/admin/realms/my-company/users/$USER_ID/role-mappings/realm"
-H "Authorization: Bearer $ACCESS_TOKEN"
-H "Content-Type: application/json"
-d "[$(curl -s -X GET
"http://localhost:8080/admin/realms/my-company/roles/admin"
-H "Authorization: Bearer $ACCESS_TOKEN")]"

5.2 將角色分配給群組

在為群組指派角色時,所有成員該群組(和子群組)的成員將繼承該角色:

# Qua CLI
bin/kcadm.sh add-roles \
  -r my-company \
  --gname Engineering \
  --rolename user

bin/kcadm.sh add-roles \
  -r my-company \
  --gname Engineering \
  --cclientid my-web-app \
  --rolename viewer

# Qua REST API
curl -s -X POST \
  "http://localhost:8080/admin/realms/my-company/groups/$GROUP_ID/role-mappings/realm" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d "[$(curl -s -X GET \
    "http://localhost:8080/admin/realms/my-company/roles/user" \
    -H "Authorization: Bearer $ACCESS_TOKEN")]"

5.3 有效角色

使用者的有效角色=直接指派的角色+從群組繼承的角色+複合角色的角色:

# Xem effective realm roles
curl -s -X GET \
  "http://localhost:8080/admin/realms/my-company/users/$USER_ID/role-mappings/realm/composite" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | jq '.[].name'

Xem effective client roles

curl -s -X GET
"http://localhost:8080/admin/realms/my-company/users/$USER_ID/role-mappings/clients/$CLIENT_UUID/composite"
-H "Authorization: Bearer $ACCESS_TOKEN" | jq '.[].name'

6. 預設角色

建立帳戶或註冊時,預設角色會自動指派給每個新使用者。

6.1 配置預設角色

透過管理控制台:

  1. 進入領域角色

  2. 尋找角色預設角色-{領域}(例如:預設角色我的公司)

  3. 點選角色 → 選項卡行動 → 新增關聯角色

  4. 選擇您要設定為預設的角色

透過 CLI:

# Thêm role vào default roles
bin/kcadm.sh add-roles \
  -r my-company \
  --rname default-roles-my-company \
  --rolename user \
  --rolename offline_access

Thêm client role vào default roles

bin/kcadm.sh add-roles
-r my-company
--rname default-roles-my-company
--cclientid my-web-app
--rolename viewer

之後,每個新建立的使用者將自動擁有角色:用戶.用戶, 離線訪問, 我的網頁應用程式/檢視器.

7. 服務帳號角色

服務帳戶用於服務之間的通訊(機器對機器)-無需使用者互動。

7.1 為客戶端啟用服務帳戶

  1. 進入客戶→ 選擇或建立客戶

  2. 選項卡設定:

    • 客戶端認證: 在
    • 服務帳號角色: 在
    • 授權:關閉(除非需要授權服務)
  3. 點選節省

7.2 為服務帳戶分配角色

# Qua Admin Console:
# Clients → chọn client → tab "Service account roles" → Assign role

Qua CLI - lấy service account user

SA_USER_ID=$(bin/kcadm.sh get clients/$CLIENT_UUID/service-account-user -r my-company --fields id --format csv --noquotes)

Gán realm roles

bin/kcadm.sh add-roles
-r my-company
--uid $SA_USER_ID
--rolename admin

Gán client roles (realm-management) cho API access

bin/kcadm.sh add-roles
-r my-company
--uid $SA_USER_ID
--cclientid realm-management
--rolename manage-users
--rolename view-users
--rolename manage-clients

7.3 使用服務帳戶

# Lấy access token cho service account (client credentials grant)
ACCESS_TOKEN=$(curl -s -X POST \
  "http://localhost:8080/realms/my-company/protocol/openid-connect/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=my-backend-service" \
  -d "client_secret=YOUR_CLIENT_SECRET" | jq -r '.access_token')

Sử dụng token để gọi API

curl -s -X GET
"http://localhost:8080/admin/realms/my-company/users"
-H "Authorization: Bearer $ACCESS_TOKEN" | jq '.[].username'

8.細粒度管理者權限V2

細粒度的管理權限 V2(來自 Keycloak 26+)允許對誰可以管理管理控制台中的資源進行精細控制,而不僅僅是使用原始領域管理客戶端角色。

8.1 啟用細粒度管理權限

  1. 進入領域設定 → 一般的

  2. 尋找管理員權限→ 打開細粒度管理員權限(V2)

  3. Keycloak會在realm中建立權限管理資源

筆記:這是 Keycloak 26.x 中的預覽功能。生產中,開機前需仔細評估。

8.2 資源權限

啟用後,您可以為資源建立權限:

使用者權限:

允許描述
看法。看法查看用戶列表和詳細信息
管理建立、編輯、刪除用戶
地圖角色為使用者指派/刪除角色
管理群組成員資格在群組中新增/刪除用戶
模仿冒充用戶

群組權限:

允許描述
看法。看法查看群組
管理建立、編輯、刪除群組
查看會員查看群組成員
管理成員新增/刪除成員
管理會員資格管理群組成員資格

客戶權限:

允許描述
看法。看法查看客戶
管理建立、編輯、刪除客戶
配置更改客戶端設定
地圖角色創建/分配客戶角色

角色權限:

允許描述
看法。看法查看角色
管理建立、編輯、刪除角色
地圖角色將角色指派給使用者/群組

8.3 建立權限

  1. 進入領域設定 → 管理員權限

  2. 選擇資源類型(使用者、群組、客戶端、角色)

  3. 點選需要配置的權限(例如:管理對於用戶)

  4. 更多的政策。政策確定誰擁有此權限

8.4 政策

策略定義授予權限的條件。 Keycloak V2支援以下策略:

基於角色的策略:

// Cho phép users có role "hr-admin" quản lý users
{
  "type": "role",
  "name": "HR Admin Policy",
  "description": "Users with hr-admin role",
  "roles": [
    {
      "id": "{role-id-of-hr-admin}",
      "required": true
    }
  ]
}

基於使用者的政策:

// Cho phép specific users
{
  "type": "user",
  "name": "Specific Admin Policy",
  "users": [
    "{user-id-of-admin-1}",
    "{user-id-of-admin-2}"
  ]
}

基於團體的政策:

// Cho phép members của group
{
  "type": "group",
  "name": "Admin Group Policy",
  "groups": [
    {
      "id": "{group-id-of-admins}",
      "extendChildren": true
    }
  ]
}

基於客戶的政策:

// Cho phép specific clients (service accounts)
{
  "type": "client",
  "name": "Backend Service Policy",
  "clients": [
    "{client-id-of-backend-service}"
  ]
}

8.5 實際範例:HR Admin 只管理用戶

要求:使用者有角色人力資源管理員只允許查看和管理用戶,不允許管理客戶端或領域設定。

  1. 創建領域角色 人力資源管理員:

    bin/kcadm.sh create roles -r my-company \
      -s name=hr-admin \
      -s description="HR Administrator - can manage users only"
  2. 啟用細粒度管理員權限V2

  3. 創建基於角色的策略給人力資源管理員:

    • 前往管理員權限 → 策略選項卡
    • 建立策略 → 基於角色
    • 名稱:《人力資源管理政策》
    • 選擇角色:人力資源管理員
  4. 將策略指派給使用者權限:

    • 使用者→權限看法。看法→ 新增策略“HR 管理策略”
    • 使用者→權限管理→ 新增策略“HR 管理策略”
  5. 為使用者指派角色:

    bin/kcadm.sh add-roles -r my-company \
      --uusername hr-manager \
      --rolename hr-admin

現在用戶人力資源經理可以登入管理控制台並且只能看到選單使用者.

8.6 權限評估

您可以使用“評估”選項卡測試權限:

  1. 進入管理員權限 → 評價

  2. 選擇要測試的使用者或客戶端

  3. 選擇資源類型和權限

  4. 點選評價查看結果(允許或拒絕)

9. 專用領域管理控制台

Keycloak 允許為每個領域建立單獨的管理員帳戶 - 無需存取主領域:

9.1 建立領域管理員

# Tạo user trong realm
bin/kcadm.sh create users -r my-company \
  -s username=realm-admin \
  -s [email protected] \
  -s enabled=true \
  -s emailVerified=true

bin/kcadm.sh set-password -r my-company
--username realm-admin
--new-password "RealmAdmin@123"

Gán realm-management client roles

bin/kcadm.sh add-roles -r my-company
--uusername realm-admin
--cclientid realm-management
--rolename realm-admin

9.2 領域管理客戶端角色

客戶領域管理控制管理員權限的可用角色:

角色描述
領域管理領域的完全管理員存取權限
管理用戶管理用戶
查看用戶查看用戶
管理客戶管理客戶
查看客戶查看客戶
管理領域管理領域設定
視界查看領域設置
管理身分提供者管理身分提供者
管理事件管理活動
管理授權授權管理
冒充冒充用戶
查詢用戶搜尋用戶
查詢群組搜尋群組
查詢客戶端尋找客戶
查詢領域搜尋領域

例如:建立一個僅管理使用者和群組的受限管理員:

bin/kcadm.sh add-roles -r my-company \
  --uusername limited-admin \
  --cclientid realm-management \
  --rolename manage-users \
  --rolename view-users \
  --rolename query-users \
  --rolename query-groups

10. 在應用程式中使用角色

10.1 從訪問令牌檢查角色

解碼後的存取權杖包含角色:

{
  "sub": "user-uuid",
  "realm_access": {
    "roles": [
      "admin",
      "manager",
      "user",
      "default-roles-my-company",
      "offline_access",
      "uma_authorization"
    ]
  },
  "resource_access": {
    "my-web-app": {
      "roles": [
        "editor",
        "content-admin"
      ]
    },
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  }
}

10.2 Spring引導範例

// SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/api/admin/**").hasRole("admin")
            .requestMatchers("/api/content/**").hasRole("editor")
            .requestMatchers("/api/public/**").permitAll()
            .anyRequest().authenticated()
        )
        .oauth2ResourceServer(oauth2 -> oauth2
            .jwt(jwt -> jwt
                .jwtAuthenticationConverter(jwtAuthenticationConverter())
            )
        );
    return http.build();
}

@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
    JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
    converter.setAuthoritiesClaimName("realm_access.roles");
    converter.setAuthorityPrefix("ROLE_");
    
    JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
    jwtConverter.setJwtGrantedAuthoritiesConverter(converter);
    return jwtConverter;
}

}

10.3 Node.js (Express) 中的範例

// middleware/auth.js
const jwt = require('jsonwebtoken');

function hasRealmRole(role) { return (req, res, next) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.status(401).json({ error: 'No token provided' });

try {
  const decoded = jwt.decode(token);
  const roles = decoded.realm_access?.roles || [];
  
  if (roles.includes(role)) {
    req.user = decoded;
    next();
  } else {
    res.status(403).json({ error: 'Insufficient permissions' });
  }
} catch (err) {
  res.status(401).json({ error: 'Invalid token' });
}

}; }

function hasClientRole(clientId, role) { return (req, res, next) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.status(401).json({ error: 'No token provided' });

try {
  const decoded = jwt.decode(token);
  const roles = decoded.resource_access?.[clientId]?.roles || [];
  
  if (roles.includes(role)) {
    req.user = decoded;
    next();
  } else {
    res.status(403).json({ error: 'Insufficient permissions' });
  }
} catch (err) {
  res.status(401).json({ error: 'Invalid token' });
}

}; }

// Sử dụng app.get('/api/admin/users', hasRealmRole('admin'), (req, res) => { // Only admin can access });

app.post('/api/content', hasClientRole('my-web-app', 'editor'), (req, res) => { // Only editors can create content });

11.練習練習

  1. 創建領域角色: 超管理員, 主管, 職員, 觀眾。觀眾

  2. 創建客戶角色為客戶我的網頁應用程式: 內容編輯器, 內容審閱者, 內容發佈者

  3. 創建複合角色:

    • 超管理員包含:主管+ 所有客戶角色
    • 主管包含:職員 + 內容審閱者
    • 職員包含:觀眾。觀眾 + 內容編輯器
  4. 將角色指派給群組:

    • 團體工程: 領域角色職員
    • 團體工程/後端:客戶角色內容編輯器
  5. 啟用細粒度管理員權限V2並創建:

    • 基於角色的策略人力資源管理員
    • 將策略指派給使用者檢視/管理權限
    • 使用具有角色的使用者進行測試人力資源管理員
  6. 建立服務帳戶為客戶我的後端服務有角色管理用戶, 查看用戶並使用客戶端憑證授予進行測試

12. 總結

在本課中,您學習了:

  • 區分領域角色和客戶角色

  • 創造複合角色具有分散的層次結構

  • 角色映射對於使用者和群組(直接和舊)

  • 配置預設角色對於新用戶

  • 使用服務帳號角色用於機器對機器通信

  • 細粒度管理員權限V2具有策略(基於角色、基於使用者、基於群組、基於客戶端)

  • 創造專用領域管理員具有有限的權限

  • 檢查應用程式中的角色(Spring Boot、Node.js)

下一篇文章將提供說明客戶端、客戶端範圍和 OpenID Connect在鑰匙斗篷裡。