Keycloak 的简单使用

1. 下载并安装 Keycloak

2. 创建 Realm,Client,User

  • Keycloak 有一个默认的 Master Realm,自己再创建一个 Test Realm
  • 在 Test Realm 下创建一个命名为 resource_01 的 client
    1. 进入 resource_01 的 Settings 页,做以下配置 (1) 将 Access Type 改为 confidential (2) 改为 confidential 后会多出 Authorization Enabled 选项,将其设置为 ON (3) Valid Redirect URIs 填 *
      配好后点击 Save 保存
    2. 进入 resource_01 的 Credentials 页,记下 Secret 的值,后面访问这个 client 的时候会用到
    3. 进入 resource_01 的 Roles 页,添加两个 roles,分别命名为 admin 和 operator
  • 在 Test Realm 下创建一个 user_01 的 user
    1. 进入 user_01 的 Credentials 页,将 Temporary 设置为 OFF,然后重置密码
  • 将 client 的 role 和 user 关联
    1. 进入 user_01 的 Role Mappings 页,在 Client Roles 选 resource_01,在 Available Roles 选择 admin,点 Add Selected,意味着 user_01 可以访问 resource_01,并且是 admin 权限,可以再创建一个 user_02 将 role 选为 operator,意味着 user_02 可以访问 resource_01,但只有做基本操作的权限(创建 client 的 role 的时候名字是可以随便写的,严格来讲 resource_01,admin 和 operator 都是什么含义,应该由 server 决定)
  • client 的 role 也可以和其他 client 关联 (这篇文章里没用上)
    1. 进入 client 的 Service Account Roles 页,在 Client Roles -> Available Roles 选择

3. 通过 Keycloak 做验证

一般场景是三个节点:客户端,服务端,Keycloak
客户端通过 Keycloak 获取一个 Token,再用这个 Token 访问服务端,服务端对 Token 做验证

这里用 Postman 模拟客户端,假设要访问的服务端的 URL 是 192.168.1.1:9090/api/server-name/v1/service-01

  1. 在 Authorization -> TYPE,选 OAuth 2.0
  2. 点 Get New Access Token,各项配置如下
    • Grant Type:Password Credentials
    • Access Token URL:http://localhost:8080/auth/realms/Test/protocol/openid-connect/token
    • Username:user_01
    • Password:user_01 的密码
    • Client ID:resource_01
    • Client Secret:在 resource_01 的 Credentials 页下找
    • Scope:Realm Setting -> General -> Endpoints 点 OpenID Endpoint Configurations 找到 scopes_supported 随便选一个填比如 openid
    • Client Authentication :Send as Basic Auth Header (意思是 Postman 拿到 Token 后是放在 Header 发给服务端)

      点 Request Token,成功获取后点 Use Token
  3. 发送请求给服务端

    实际上发送请求的时候,加上了一个 name 为 Authorization,value 为 Bearer ${token} 的 Header 项

以上操作用命令行执行的话命令如下

curl -k -X POST -d "grant_type=password&username=user_01&password=123456&scope=%22openid%22&client_id=resource_01&client_secret=d54d22b0-4941-415d-ba59-79b7ce70498e" http://localhost:8080/auth/realms/Test/protocol/openid-connect/token
curl -X GET -H "X-Request-With: XMLHttpRequest" -H "content-type: application/json" -H "Authorization: Bearer xxx" -H "api_key: queryOverdueInfo" http://192.168.1.1:9090/api/server-name/v1/service-01

4. Token 的内容

JWT (Json Web Token) 由三部分组成:header,payload,signature
从 Keycloak 获取到的 token = base64(header) + "." + base64(payload) + "." + signature
其中 signature = encrypt( base64(header) + base64(payload) , privateKey)

可以看到,token 的 header 和 payload 相当于是明文的,只要对相应部分进行解码就可以看到

服务器端主要用 publicKey 对 signature 部分解密,然后和 header,payload 部分对比,确保 token 是合法的

每一个 Realm 会有自己的 privateKey 和 publicKey

对 header 部分和 payload 部分解码,内容如下

header

{
    "alg":"RS256",
    "typ" : "JWT",
    "kid" : "jwQF3Y_V5vCfNSkKtP5XAt9LzdpKKjfjGOEFPZsw8xc"      // key id,密钥 id
}

payload

{
    "jti":"df57c8b5-9801-47b3-9977-74a66e9a8d88",         // jwt id
    "exp":1580829682,                                     // 过期时间
    "nbf":0,                                              // 有效起始时间
    "iat":1580829382,                                     // 发行时间
    "iss":"http://localhost:8080/auth/realms/Test",       // 发行者
    "aud":"account",                                      // 受众
    "sub":"0d61a540-4914-4887-ac28-87b21da91da3",         // 主题 
    "typ":"Bearer",
    "azp":"resource_01",                                  // Authorized party
    "auth_time":0,
    "session_state":"1a8a0680-5e6e-40dc-a150-9a84954dbc55",
    "acr":"1",
    "realm_access": {
        "roles":[
            "offline_access",
            "uma_authorization"
        ]
    },
    "resource_access": {
        "resource_01": {
            "roles":[
                "admin"
            ]
        },
        "account":{
            "roles":[
                "manage-account",
                "manage-account-links",
                "view-profile"
            ]
        }
    },
    "scope":"openid email profile",
    "email_verified":false,
    "preferred_username":"user_01"
}

服务端验证并解码 token 就可以看到,这个 token 的 username 是 user_01,它有 resource_01 这个 client 的 admin 权限,服务端就可以根据 resource_01,admin 决定是否允许做相应的操作,当然也可以使用其它字段,这都是由服务端自己决定的

在请求 token 的时候,client id 不一定要填 user_01 关联的 client,比如可以填上 resource_02 及相应的 secret,这也是允许的,这样得到的 token,resource_access 部分依然是 resource_01 以及 admin(假设 user_01 没有同时关联 resource_02),但 azp 部分会变成 resource_02,这种用法的其中一种场景,是可以有一个统一的 client 做入口,比如有一个 client 就叫 resources,不论哪个 user 要访问哪个 resource,获取 token 的时候 client 都统一填 resources 就可以,因为 token 里的 resource_access 字段依然是 user 真正关联的所有 client

如果请求 token 的时候,Grant Type 选的是 Client Credentials,这样只需要填 client id 和 client secret,不需要填 user 和 password,这样得到的 token,以 resource_01 为例子,会多一个 clientId 字段为 resource_01,而 preferred_username 部分将变成 service-account-resource_01,resource_access 部分还是 resource_01,roles 变成 uma_protection(这是创建 client 时默认就有的 role,如果把这个 role 删除了,那么返回的 token 的 resource_access 字段不会有 resource_01 的信息)

5. Springboot 验证 Token

如果服务端是 Springboot,那么有两种方法验证

  • 继承 ResourceServerConfigurerAdapter 类,这是一个通用的做 OAUTH 2.0 验证的类,验证的 Token 可以不是 Keycloak 发的,只要是符合标准的 Token 就可以,需要配置包括 Realm 的 PublicKey 在内的一些信息,做验证的时候 keycloak 可以没运行,只要 PublicKey 能用于解密就可以
  • 继承 KeycloakWebSecurityConfigurerAdapter 类,这是 springboot 做 keycloak 验证的适配器,需要配 keycloak 的地址和相应的 Realm,不用配 PublicKey,springboot 自己会去拿,第一次验证的时候 keycloak 必须在运行,后续的验证不需要

6. 通过 REST API 操作 Keycloak

可用的 API:https://www.keycloak.org/docs-api/7.0/rest-api/index.html

要通过 API 操作,同样需要通过 user 拿先一个 Token,再用这个 Token 去操作 Keycloak,这个 user 要有相应的权限

  1. 创建一个 user 命名为 admin_user
  2. 在 Role Mappings 的 Client Roles 选择 realm-management,把 Available Roles 都加上
  3. 取 Token 的时候 client_id 填 admin-cli
  4. curl 命令如下
curl -k -X POST -d "grant_type=password&username=admin_user&password=123456&client_id=admin-cli" http://localhost:8080/auth/realms/Test/protocol/openid-connect/token




猜你喜欢

转载自www.cnblogs.com/moonlight-lin/p/12264142.html
今日推荐