HTMLTAG_67__HTMLTAG_68___1。授權服務 — 概論___HTMLTAG_69__HTMLTAG_70
Keycloak 授權服務提供 細粒度授權,允許在資源和範圍層級進行存取控制,而不僅僅是依賴角色。本系統符合 UMA 2.0(使用者管理存取)標準並支援多種策略類型。
HTMLTAG_77__HTMLTAG_78___1.1 主要概念___HTMLTAG_79__HTMLTAG_80
___HTMLTAG_83__HTMLTAG_84___概念___HTMLTAG_85__HTMLTAG_86___說明____HTMLTAG_87__HTMLTAG_88___範例____HTMLTAG_89__HTMLTAG_90___ ___HTMLTAG_93__HTMLTAG_94__HTMLTAG_95___資源伺服器___HTMLTAG_96__HTMLTAG_97__HTMLTAG_98___應用程式需要保護資源(是Keycloak客戶端)___HTMLTAG_99__HTMLTAG_100_____10100___ ___HTMLTAG_103__HTMLTAG_104__HTMLTAG_105___資源___HTMLTAG_106__HTMLTAG_107__HTMLTAG_108___要保護的物件____HTMLTAG_109__HTMLTAG_110___、API 端點、頁數 ___HTMLTAG_113__HTMLTAG_114__HTMLTAG_115___範圍___HTMLTAG_116__HTMLTAG_117__HTMLTAG_118___可以對資源執行的操作___HTMLTAG_119__HTMLTAG_120__HTMLTAG_121___HTMLTAG_119__HTMLTAG_120__HTMLTAG_121___編輯、刪除、發佈___HTMLTAG_128__HTMLTAG_129__HTMLTAG_130___HTMLTAG_128__HTMLTAG_129__HTMLTAG_130___
___HTMLTAG_131__HTMLTAG_132__HTMLTAG_133___權限___HTMLTAG_134__HTMLTAG_135__HTMLTAG_136___將資源/範圍與策略結合____HTMLTAG_137__HTMLTAG_138___「誰可以檢視文件G」___13135____ML_138___
___HTMLTAG_141__HTMLTAG_142__HTMLTAG_143___政策___HTMLTAG_144__HTMLTAG_145__HTMLTAG_146___必須滿足允許存取的條件____HTMLTAG_147__HTMLTAG_148___「使用者必須有編輯者____HTMLTAG_147__HTMLTAG_148___「使用者」必須具有編輯者____HTMLTAG_147__HTMLTAG_148___「使用者」必須有編輯者____HTML410G1047____ML_148___「使用者」。
HTMLTAG_153__HTMLTAG_154___1.2 授權流程___HTMLTAG_155__HTMLTAG_156
預編碼_0
HTMLTAG_157__HTMLTAG_158___2。啟用授權服務___HTMLTAG_159__HTMLTAG_160
在 客戶端層級啟用(非領域):
HTMLTAG_165__HTMLTAG_166___2.1 透過管理控制台___HTMLTAG_167__HTMLTAG_168
預編碼_1
HTMLTAG_169__HTMLTAG_170___2.2 透過 kcadm.sh___HTMLTAG_171__HTMLTAG_172
預編碼_2
啟用後,選項卡 授權 將顯示在客戶端設定上,並附有子選項卡:設定、資源、範圍、策略、權限、評估.
HTMLTAG_177__HTMLTAG_178___3。資源___HTMLTAG_179__HTMLTAG_180
資源代表 要保護的物件。每個資源都可以有 URI、類型、範圍和屬性。
HTMLTAG_185__HTMLTAG_186___3.1 建立資源___HTMLTAG_187__HTMLTAG_188
預編碼_3
HTMLTAG_189__HTMLTAG_190___3.2 資源屬性___HTMLTAG_191__HTMLTAG_192
屬性允許向資源添加元數據,可在策略中使用:
預編碼_4
HTMLTAG_195__HTMLTAG_196___4。範圍___HTMLTAG_197__HTMLTAG_198
作用域定義了 actions__HTMLTAG_201___ 可以對資源執行:
預編碼_5
常見範圍模式:
___HTMLTAG_207__HTMLTAG_208___模式___HTMLTAG_209__HTMLTAG_210___範圍___HTMLTAG_211__HTMLTAG_212___
___HTMLTAG_215__HTMLTAG_216__HTMLTAG_217____CRUD___HTMLTAG_218__HTMLTAG_219__HTMLTAG_220__HTMLTAG_2 21___建立、讀取、更新、 刪除___HTMLTAG_228__HTMLTAG_229__HTMLTAG_230___
___HTMLTAG_231__HTMLTAG_232__HTMLTAG_233___內容管理___HTMLTAG_234__HTMLTAG_235__HTMLTAG_236__HTMLTAG_2 37___查看、編輯、發佈、 存檔___HTMLTAG_244__HTMLTAG_245__HTMLTAG_246___
___HTMLTAG_247__HTMLTAG_248__HTMLTAG_249___API 存取___HTMLTAG_250__HTMLTAG_251__HTMLTAG_252__HTMLTAG_253___讀取____MLTAG_254____ 管理員___HTMLTAG_258__HTMLTAG_259__HTMLTAG_260___
___HTMLTAG_261__HTMLTAG_262__HTMLTAG_263___檔案操作___HTMLTAG_264__HTMLTAG_265__HTMLTAG_266__HTMLTAG_2 67___下載、上傳、共享、 刪除___HTMLTAG_274__HTMLTAG_275__HTMLTAG_276___
HTMLTAG_279__HTMLTAG_280___5。政策___HTMLTAG_281__HTMLTAG_282
策略是決定允許或拒絕存取的條件。 Keycloak 支援多種類型的策略:
HTMLTAG_287__HTMLTAG_288___5.1 基於角色的策略___HTMLTAG_289__HTMLTAG_290
預編碼_6
HTMLTAG_291__HTMLTAG_292___5.2 基於使用者的政策___HTMLTAG_293__HTMLTAG_294
預編碼_7
HTMLTAG_295__HTMLTAG_296___5.3 基於群組的策略___HTMLTAG_297__HTMLTAG_298
預編碼_8
HTMLTAG_299__HTMLTAG_300___5.4 基於客戶端的政策___HTMLTAG_301__HTMLTAG_302
預編碼_9
HTMLTAG_303__HTMLTAG_304___5.5 基於時間的策略___HTMLTAG_305__HTMLTAG_306
預編碼_10
HTMLTAG_307__HTMLTAG_308___5.6 JavaScript 政策___HTMLTAG_309__HTMLTAG_310
___HTMLTAG_311__HTMLTAG_312___注意: JavaScript 策略需要透過 --features=scripts 或上傳 JAR.
啟用 或上傳 JAR. 啟用
// Script-based policy (upload dưới dạng JAR provider)
// Filename: my-policy.js
var context = $evaluation.getContext();
var identity = context.getIdentity();
var attributes = identity.getAttributes();
// Kiểm tra custom attribute
var department = attributes.getValue('department');
if (department && department.asString(0) === 'engineering') {
$evaluation.grant();
} else {
$evaluation.deny();
}
將 JavaScript 原則部署為 JAR:
# Tạo cấu trúc thư mục
mkdir -p META-INF/keycloak-scripts/
# Tạo file descriptor
cat > META-INF/keycloak-scripts/keycloak-scripts.json << 'EOF'
{
"policies": [
{
"name": "Engineering Department Policy",
"fileName": "engineering-policy.js",
"description": "Allow only engineering department"
}
]
}
EOF
# Package JAR
jar cf my-policies.jar META-INF/ engineering-policy.js
# Deploy
cp my-policies.jar /opt/keycloak/providers/
/opt/keycloak/bin/kc.sh build
HTMLTAG_319__HTMLTAG_320___5.7 綜合政策___HTMLTAG_321__HTMLTAG_322
透過決策策略將多個策略合併為單一策略:
{
"name": "Full Access Policy",
"description": "Kết hợp Role + Group + Time policies",
"type": "aggregate",
"logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS",
"policies": [
"Editor Role Policy",
"Engineering Group Policy",
"Business Hours Policy"
]
}
HTMLTAG_325__HTMLTAG_326___6。決策策略___HTMLTAG_327__HTMLTAG_328
___HTMLTAG_331__HTMLTAG_332___策略____HTMLTAG_333__HTMLTAG_334___說明____HTMLTAG_335__HTMLTAG_336___何時使用____HTMLTAG_337__HTMLTAG_338___
___HTMLTAG_341__HTMLTAG_342__HTMLTAG_343___一致___HTMLTAG_344__HTMLTAG_345__HTMLTAG_346___所有政策必須允許___HTMLTAG_347__HTMLTAG_348______ML] 所有政策必須允許___HTMLTAG_347__HTMLTAG_348______ML — 所有條件必須符合嚴格限制
___HTMLTAG_351__HTMLTAG_352__HTMLTAG_353___肯定___HTMLTAG_354__HTMLTAG_355__HTMLTAG_356___至少一項政策允許___HTMLTAG_357__HTMLTAG_358_____ 只滿足滿足一個條件___MLTAGMLTAGMLTAGMLTAGML_4141.
___HTMLTAG_361__HTMLTAG_362__HTMLTAG_363___共識___HTMLTAG_364__HTMLTAG_365__HTMLTAG_366___允許數字>拒絕___HTMLTAG_367__HTMLTAG_368___投票 - ___ML_MLTAGMLTAGMLTAGMLTAG_368___投票 - 7_ML
HTMLTAG_373__HTMLTAG_374___7。權限___HTMLTAG_375__HTMLTAG_376
權限將 資源/範圍與政策結合以建立授權規則。
HTMLTAG_381__HTMLTAG_382___7.1 基於資源的權限___HTMLTAG_383__HTMLTAG_384
# Tạo resource-based permission
curl -X POST "http://localhost:8080/admin/realms/my-realm/clients/${CLIENT_ID}/authz/resource-server/permission/resource" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "Document Access Permission",
"description": "Ai có thể truy cập documents",
"type": "resource",
"logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS",
"resources": ["Document Resource"],
"policies": ["Editor Role Policy", "Business Hours Policy"]
}'
HTMLTAG_385__HTMLTAG_386___7.2 基於範圍的權限___HTMLTAG_387__HTMLTAG_388
# Tạo scope-based permission
curl -X POST "http://localhost:8080/admin/realms/my-realm/clients/${CLIENT_ID}/authz/resource-server/permission/scope" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "Document Delete Permission",
"description": "Chỉ admin mới được xóa documents",
"type": "scope",
"logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS",
"resources": ["Document Resource"],
"scopes": ["delete"],
"policies": ["Admin Role Policy"]
}'
# Permission cho publish scope
curl -X POST "http://localhost:8080/admin/realms/my-realm/clients/${CLIENT_ID}/authz/resource-server/permission/scope" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "Document Publish Permission",
"description": "Editor và Admin được publish",
"type": "scope",
"logic": "POSITIVE",
"decisionStrategy": "AFFIRMATIVE",
"resources": ["Document Resource"],
"scopes": ["publish"],
"policies": ["Editor Role Policy", "Admin Role Policy"]
}'
HTMLTAG_389__HTMLTAG_390___8。 UMA 2.0___HTMLTAG_391__HTMLTAG_392
使用者管理的存取 (UMA) 2.0 允許 資源擁有者管理 對其資源的存取。用戶可以與其他用戶共享資源,無需管理員幹預。
HTMLTAG_397__HTMLTAG_398___8.1 啟用 UMA___HTMLTAG_399__HTMLTAG_400
Clients → my-app → Authorization → Settings:
Resource server settings:
Policy Enforcement Mode: ENFORCING
Decision Strategy: UNANIMOUS
Resources:
Resource → Owner Managed Access: ON
HTMLTAG_401__HTMLTAG_402___8.2 UMA 撥款流程___HTMLTAG_403__HTMLTAG_404
# 1. Client gọi Resource Server → bị deny → nhận permission ticket
# Response 401 với header:
# WWW-Authenticate: UMA realm="my-realm",
# as_uri="http://localhost:8080/realms/my-realm",
# ticket="permission-ticket-value"
# 2. Client exchange permission ticket lấy RPT (Requesting Party Token)
curl -X POST "http://localhost:8080/realms/my-realm/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
-d "ticket=permission-ticket-value" \
-d "client_id=my-app" \
-d "client_secret=my-secret"
# Response chứa RPT (access token with authorization data)
{
"access_token": "eyJhbGciOi...",
"token_type": "Bearer",
"authorization": {
"permissions": [
{
"rsid": "resource-uuid",
"rsname": "Document Resource",
"scopes": ["view", "edit"]
}
]
}
}
HTMLTAG_405__HTMLTAG_406___9。權限API___HTMLTAG_407__HTMLTAG_408
權限 API 允許 以程式設計方式檢查權限,無需 UMA 流程:
# Kiểm tra quyền truy cập cho user hiện tại
curl -X POST "http://localhost:8080/realms/my-realm/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
-d "audience=my-app" \
-d "permission=Document Resource#view" \
-d "response_mode=decision" \
-d "client_id=my-frontend" \
-d "subject_token=${USER_ACCESS_TOKEN}"
# Response:
# { "result": true } → PERMIT
# { "result": false } → DENY
# Kiểm tra nhiều permissions cùng lúc
curl -X POST "http://localhost:8080/realms/my-realm/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
-d "audience=my-app" \
-d "permission=Document Resource#view" \
-d "permission=Document Resource#edit" \
-d "permission=Admin Panel#access" \
-d "response_mode=permissions" \
-d "client_id=my-frontend" \
-d "subject_token=${USER_ACCESS_TOKEN}"
HTMLTAG_413__HTMLTAG_414___10。推送的聲明___HTMLTAG_415__HTMLTAG_416
推送聲明允許客戶 在請求授權時發送額外的上下文資訊,幫助政策擁有更多資料來做出決策:
# Request với pushed claims
curl -X POST "http://localhost:8080/realms/my-realm/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
-d "audience=my-app" \
-d "permission=Document Resource#edit" \
-d 'claim_token={"ip_address":["10.0.0.5"],"device_type":["desktop"],"risk_score":["low"]}' \
-d "claim_token_format=urn:ietf:params:oauth:token-type:jwt" \
-d "client_id=my-frontend" \
-d "subject_token=${USER_ACCESS_TOKEN}"
HTMLTAG_421__HTMLTAG_422___11。聲明資訊點___HTMLTAG_423__HTMLTAG_424
聲明資訊點允許 自動從多個來源收集聲明__HTMLTAG_427___(HTTP 請求、外部服務)以在策略中使用:
預編碼_20
HTMLTAG_429__HTMLTAG_430___12。評估 API___HTMLTAG_431__HTMLTAG_432
Keycloak 管理控制台提供 評估工具,用於在部署之前測試權限。
HTMLTAG_437__HTMLTAG_438___12.1 使用評估工具___HTMLTAG_439__HTMLTAG_440
Clients → my-app → Authorization → Evaluate:
1. Identity Information:
- User: chọn user cần test
- Roles: chọn roles (hoặc tự động từ user)
2. Resources:
- Thêm resources cần evaluate
3. Contextual Information:
- Pushed Claims (JSON)
4. Click "Evaluate"
Results:
┌─────────────────────────────┬────────┐
│ Permission │ Result │
├─────────────────────────────┼────────┤
│ Document Access Permission │ PERMIT │
│ Document Delete Permission │ DENY │
│ Admin Panel Permission │ DENY │
└─────────────────────────────┴────────┘
HTMLTAG_441__HTMLTAG_442___12.2 透過 API 進行評估___HTMLTAG_443__HTMLTAG_444
# Evaluate permissions cho user cụ thể
curl -X POST "http://localhost:8080/admin/realms/my-realm/clients/${CLIENT_ID}/authz/resource-server/policy/evaluate" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"userId": "user-uuid",
"roleIds": [],
"resources": [
{
"name": "Document Resource",
"scopes": ["view", "edit", "delete"]
}
],
"context": {
"attributes": {
"ip_address": ["10.0.0.5"]
}
},
"entitlements": false
}'
HTMLTAG_445__HTMLTAG_446___13。政策執行者___HTMLTAG_447__HTMLTAG_448
策略強制執行器是 Java 程式庫 整合到應用程式中以自動執行授權策略:
HTMLTAG_453__HTMLTAG_454___13.1 Spring Boot 整合___HTMLTAG_455__HTMLTAG_456
<!-- pom.xml -->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-authz-client</artifactId>
<version>26.0.0</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-policy-enforcer</artifactId>
<version>26.0.0</version>
</dependency>
預編碼_24
HTMLTAG_457__HTMLTAG_458___13.2 keycloak.json 設定___HTMLTAG_459__HTMLTAG_460
預編碼_25
HTMLTAG_461__HTMLTAG_462___13.3 Node.js 整合___HTMLTAG_463__HTMLTAG_464
// authorization.ts
import axios from 'axios';
interface PermissionResult {
rsid: string;
rsname: string;
scopes: string[];
}
class KeycloakAuthzClient {
private readonly baseUrl: string;
private readonly realm: string;
private readonly clientId: string;
private readonly clientSecret: string;
constructor(config: {
baseUrl: string;
realm: string;
clientId: string;
clientSecret: string;
}) {
this.baseUrl = config.baseUrl;
this.realm = config.realm;
this.clientId = config.clientId;
this.clientSecret = config.clientSecret;
}
async checkPermission(
userToken: string,
resource: string,
scope: string
): Promise<boolean> {
try {
const response = await axios.post(
`${this.baseUrl}/realms/${this.realm}/protocol/openid-connect/token`,
new URLSearchParams({
grant_type: 'urn:ietf:params:oauth:grant-type:uma-ticket',
audience: this.clientId,
permission: `${resource}#${scope}`,
response_mode: 'decision',
subject_token: userToken,
}),
{
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
auth: {
username: this.clientId,
password: this.clientSecret,
},
}
);
return response.data.result === true;
} catch {
return false;
}
}
async getPermissions(userToken: string): Promise<PermissionResult[]> {
const response = await axios.post(
`${this.baseUrl}/realms/${this.realm}/protocol/openid-connect/token`,
new URLSearchParams({
grant_type: 'urn:ietf:params:oauth:grant-type:uma-ticket',
audience: this.clientId,
response_mode: 'permissions',
subject_token: userToken,
}),
{
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
auth: {
username: this.clientId,
password: this.clientSecret,
},
}
);
return response.data;
}
}
// Express middleware
import { Request, Response, NextFunction } from 'express';
const authzClient = new KeycloakAuthzClient({
baseUrl: 'http://localhost:8080',
realm: 'my-realm',
clientId: 'my-app',
clientSecret: 'secret',
});
function enforcePermission(resource: string, scope: string) {
return async (req: Request, res: Response, next: NextFunction) => {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
const allowed = await authzClient.checkPermission(token, resource, scope);
if (!allowed) {
return res.status(403).json({
error: `Permission denied: ${resource}#${scope}`,
});
}
next();
};
}
// Usage
app.get('/api/documents',
enforcePermission('Document Resource', 'view'),
documentsController.list
);
app.put('/api/documents/:id',
enforcePermission('Document Resource', 'edit'),
documentsController.update
);
app.delete('/api/documents/:id',
enforcePermission('Document Resource', 'delete'),
documentsController.delete
);
HTMLTAG_465__HTMLTAG_466___14。 kcadm.sh — 授權管理___HTMLTAG_467__HTMLTAG_468
預編碼_27
HTMLTAG_469__HTMLTAG_470___15。最佳實務___HTMLTAG_471__HTMLTAG_472
___HTMLTAG_474__HTMLTAG_475___從粗粒度開始→細粒度 — 先使用基於角色的,根據需要添加基於資源的__HTMLTAG_477___
___HTMLTAG_478__HTMLTAG_479___使用資源類型__HTMLTAG_480___ — 將相同類型的資源分組,而不是為每個單獨的資源建立權限
___HTMLTAG_482__HTMLTAG_483___使用評估 API 進行測試 — 在部署到生產環境之前始終測試權限
___HTMLTAG_486__HTMLTAG_487___謹慎決策策略 — 一致更安全,但更限制性 肯定更安全,但更限制性 肯定___HTMLTAG_492__HT
___HTMLTAG_494__HTMLTAG_495___限制 JavaScript 策略 — 優先考慮內建策略類型,僅在真正需要時才使用 JavaScript
___HTMLTAG_498__HTMLTAG_499___監控權限評估效能 — 太多巢狀策略可能會減慢
___HTMLTAG_502__HTMLTAG_503___匯出/匯入授權配置 — 使用 kcadm.sh 進行版本控制授權設定
___HTMLTAG_506__HTMLTAG_507___將權限檢查與業務邏輯分開 - 在中間件/攔截器層級強制