iOS Developer的全栈之路 - Keycloak(5)
在上一节中,使用Keycloak保护了SpringBoot的RestAPI,其中的用户和角色都是通过Keycloak内置的web页面进行添加的。在本节中,将使用RestAPI来创建用户并为用户分配角色。
Create User Endpoint
创建用户的Endpoint:http://localhost:8080/auth/admin/realms/{realmName}/users
,在我的demo中realmName为springboot-integration
,所以完整的url为:http://localhost:8080/auth/admin/realms/springboot-integration/users
,这是一个POST请求,那么body需要传入是什么呢?通过官方文档,需要的body类型为UserRepresentation
,body如下所示:
{
"firstName": "xyz",
"lastName": "xyz",
"username": "xyz",
"email": "demo@gmail.com",
"enabled": "true",
"credentials": [
{
"type": "password",
"value": "123456",
"temporary": false
}
],
"realmRoles": [
"user"
]
}
通过如上的POST请教会得到一个401的错误,回看url,可以发现只有admin的用户才可创建用户。
获取Token
获取admin的token和上节中获取普通用户的token是相同的,url:http://localhost:8080/auth/realms/master/protocol/openid-connect/token
,并使用x-www-form-urlencoding编码方式发生post请求,如下图所示:
获得access token后,重新发起创建用户的请求,当传入用户名不存在时,便可成功创建用户,成功的response是一个空body,status code为201,表示创建成功。
通过Keycloak的web登录后,会发现,请求中带有的realmRoles并没有生效,查阅想过issue时,发现这是官方暂不支持的一个参数,链接:https://issues.redhat.com/browse/KEYCLOAK-12460,从comment中看到:
The rest endpoints have never supported adding roles as part of creating a new user, so changing this to a enhancement request. A contribution to this if tests are included would be welcome.
若需要这个功能看来只能fork过来改一下了,退而求其次,只能再多发一个请求RoleMappers
,这个有点尴尬,如果第一个请求成功,第二请求失败,应该如何处理这个结果呢。希望有时间可以创建这个PR~
Map a Role To a User
官方给出的endpoint:http://localhost:8080/auth/admin/realms/{realmName}/users/{userId}/role-mappings/realm
其中需要一个UserId,而在创建时,返回的response为空,那么又需要一个request来获取UserId:
http://localhost:8080/auth/admin/realms/springboot-integration/users?username=xyz
这是一个GET请求,得到的response如下所示:
[
{
"id": "15c16b3d-dafe-496e-8c9e-98933b4cd518",
"createdTimestamp": 1578820379997,
"username": "xyz",
"enabled": true,
"totp": false,
"emailVerified": false,
"firstName": "xyz",
"lastName": "xyz",
"email": "demo2@gmail.com",
"disableableCredentialTypes": [],
"requiredActions": [],
"notBefore": 0,
"access": {
"manageGroupMembership": true,
"view": true,
"mapRoles": true,
"impersonate": true,
"manage": true
}
}
]
终于拿到了userId,继续添加role:
http://localhost:8080/auth/admin/realms/springboot-integration/users/15c16b3d-dafe-496e-8c9e-98933b4cd518/role-mappings/realm
需要的body,如下所示:
{
"roles": [
{
"name": "user",
"clientRole": false
}
]
}
本以为,可以成功添加,可是直接报错,真的是要吐槽Keycloak的官方文档了。通过chrome的network调试工具,看看在网页端是如何发起添加role的请求的,发现playload的格式是这样的:
[{
"id": "66633ad6-7412-4176-aa25-0c1ff67a2180",
"name": "user",
"description": "normal user",
"composite": false,
"clientRole": false,
"containerId": "springboot-integration"
}]
根据文档所述,这些role的字段都不是必须得,如果当前只知道role name,是否可以呢?body中只留下name,发送请求,报错:"error": "Role not found"
,不禁感叹文档有何用
。
所以这些字段都是需要的,那么问题又来了,我们如何找到需要的role呢?这次直接忽略文档,chrome network调试工具走起。
GET请求http://localhost:8080/auth/admin/realms/springboot-integration/roles
可获得当前realm下的所有role。
[
{
"id": "195d5f3e-e805-4f53-88c0-e3589b9172c4",
"name": "admin",
"description": "administrator, super user",
"composite": true,
"clientRole": false,
"containerId": "springboot-integration"
},
{
"id": "814d6190-3d69-4e9b-aaae-ef60ae12159e",
"name": "Member",
"composite": false,
"clientRole": false,
"containerId": "springboot-integration"
},
{
"id": "ac6827fb-51b9-436c-8cb8-852babd4816b",
"name": "offline_access",
"description": "${role_offline-access}",
"composite": false,
"clientRole": false,
"containerId": "springboot-integration"
},
{
"id": "98107db5-7f81-4fd9-beb0-ea4fd3d0fccd",
"name": "uma_authorization",
"description": "${role_uma_authorization}",
"composite": false,
"clientRole": false,
"containerId": "springboot-integration"
},
{
"id": "66633ad6-7412-4176-aa25-0c1ff67a2180",
"name": "user",
"description": "normal user",
"composite": false,
"clientRole": false,
"containerId": "springboot-integration"
},
{
"id": "13bc2555-a3d8-4975-9cce-ae2251bca1b2",
"name": "Librarian",
"composite": true,
"clientRole": false,
"containerId": "springboot-integration"
}
]
总结
当需要创建一个用户时,首先需要admin用户的token,之后需要一系列的请求:
- 创建用户:
http://localhost:8080/auth/admin/realms/springboot-integration/users
- 根据用户名,获取当前用户的完整信息:
http://localhost:8080/auth/admin/realms/springboot-integration/users?username=xyz
- 获取当前realm下所有的role:
http://localhost:8080/auth/admin/realms/springboot-integration/roles
- 为用户添加role:
http://localhost:8080/auth/admin/realms/springboot-integration/users/15c16b3d-dafe-496e-8c9e-98933b4cd518/role-mappings/realm
真的是一口老血 ~