我自己也有同样的问题。我得到的问题之一是获取JWT 标签之类的副本,即Keycloak 对您的设置进行编码的文本
@GetMapping("/whoami")
@ResponseBody
public Map<String, Object> index(
@RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient,
Authentication auth) {
log.error("XXAuth is {}",auth);
log.error("XXClient is {}", authorizedClient.getClientRegistration());
log.error("XXClient access is {}", authorizedClient.getAccessToken());
log.error("Token {}",authorizedClient.getAccessToken().getTokenValue());
}
此代码将为您提供对话中的一些值,令牌部分是 JWT 令牌,您可以将其复制并粘贴到 jwt.io 并找出 Keycloak 实际发送的内容.
这通常看起来像
{
"exp": 1622299931,
"iat": 1622298731,
"auth_time": 1622298258,
"jti": "635ca59f-c87b-40da-b4ae-39774ed8098a",
"iss": "http://clunk:8080/auth/realms/spring-cloud-gateway-realm",
"sub": "6de0d95f-95b0-419d-87a4-b2862e8d0763",
"typ": "Bearer",
"azp": "spring-cloud-gateway-client",
"nonce": "2V8_3siQjTOIRbfs68BHwzvz3-dWeqXGUultzhJUWrA",
"session_state": "dd226823-90bc-429e-9cac-bb575b7d4fa0",
"acr": "0",
"realm_access": {
"roles": [
"ROLE_ANYONE"
]
},
"resource_access": {
"spring-cloud-gateway-client": {
"roles": [
"ROLE_ADMIN_CLIENT"
]
}
},
"scope": "openid email profile roles",
"email_verified": true,
"preferred_username": "anon"
}
如您所见,Keycloak 支持两种不同类型的 ROLE 令牌,但它们没有在顶层定义,而是在 realm_access 和 resource_access 下定义,不同之处在于资源访问定义了作为资源一部分的 ROLE real_access 定义了在所有领域中定义的角色。
要定义这些值,需要定义一个Mapper,如下
要将这些值加载到 Spring 安全性中,您需要定义一个 userAuthoritiesMapper Bean 并将属性中的设置导出为 SimpleGrantedAuthority,如下所示。
package foo.bar.com;
import lombok.extern.slf4j.Slf4j;
import net.minidev.json.JSONArray;
import net.minidev.json.JSONObject;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class RoleConfig {
@Bean
GrantedAuthoritiesMapper userAuthoritiesMapper() {
String ROLES_CLAIM = "roles";
return authorities -> {
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
for (Object authority : authorities) {
boolean isOidc = authority instanceof OidcUserAuthority;
if (isOidc) {
log.error("Discovered an Oidc type of object");
var oidcUserAuthority = (OidcUserAuthority) authority;
java.util.Map<String, Object> attribMap = oidcUserAuthority.getAttributes();
JSONObject jsonClaim;
for (String attrib : attribMap.keySet()) {
log.error("Attribute name {} type {} ", attrib, attrib.getClass().getName());
Object claim = attribMap.get(attrib);
if (attrib.equals("realm_access")) {
log.error("Define on roles for entire client");
jsonClaim = (JSONObject) claim;
if (!jsonClaim.isEmpty()) {
log.error("JobClaim is {}", jsonClaim);
Object roleStr = jsonClaim.get("roles");
if (roleStr != null) {
log.error("Role String {}", roleStr.getClass().getName());
JSONArray theRoles = (JSONArray) roleStr; //jsonClaim.get("roles");
for (Object roleName : theRoles) {
log.error("Name {} ", roleName);
}
}
}
}
if (attrib.equals("resource_access")) {
log.error("Unique to attrib client");
jsonClaim = (JSONObject) claim;
if (!jsonClaim.isEmpty()) {
log.error("Job is {}", jsonClaim);
String clientName = jsonClaim.keySet().iterator().next();
log.error("Client name {}", clientName);
JSONObject roleObj = (JSONObject) jsonClaim.get(clientName);
Object roleNames = roleObj.get("roles");
log.error("Role names {}", roleNames.getClass().getName());
JSONArray theRoles = (JSONArray) roleObj.get("roles");
for (Object roleName : theRoles) {
log.error("Name {} ", roleName);
}
}
}
}
var userInfo = oidcUserAuthority.getUserInfo();
log.error("UserInfo {}", userInfo);
for (String key : userInfo.getClaims().keySet()) {
log.error("UserInfo keys {}", key);
}
if (userInfo.containsClaim(ROLES_CLAIM)) {
var roles = userInfo.getClaimAsStringList(ROLES_CLAIM);
mappedAuthorities.addAll(generateAuthoritiesFromClaim(roles));
} else {
log.error("userInfo DID NOT FIND A claim");
}
} else {
var oauth2UserAuthority = (SimpleGrantedAuthority) authority;
log.error("Authority name " + authority.getClass().getName());
}
}
return mappedAuthorities;
};
}
private Collection<GrantedAuthority> generateAuthoritiesFromClaim(Collection<String> roles) {
return roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.collect(Collectors.toList());
}
}
请注意,此代码基于OAuth2 Login with custom granted authorities from UserInfo 上的示例
获取属性是我自己的工作。
注意如果没有找到realm_access 或resource_access,将在最高级别生成错误消息,因为我假设想要解码Keycloak 引用是使用的原因这段代码。
正常工作时,会产生如下输出
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Discovered an Oidc type of object
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Attribute name at_hash type java.lang.String
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Attribute name sub type java.lang.String
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Attribute name resource_access type java.lang.String
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Unique to attrib client
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Job is {"spring-cloud-gateway-client":{"roles":["ROLE_ADMIN_CLIENT"]}}
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Client name spring-cloud-gateway-client
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Role names net.minidev.json.JSONArray
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Name ROLE_ADMIN_CLIENT
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Attribute name email_verified type java.lang.String
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Attribute name iss type java.lang.String
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Attribute name typ type java.lang.String
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Attribute name preferred_username type java.lang.String
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Attribute name nonce type java.lang.String
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Attribute name aud type java.lang.String
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Attribute name acr type java.lang.String
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Attribute name realm_access type java.lang.String
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Define on roles for entire client
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : JobClaim is {"roles":["ROLE_ANYONE"]}
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Role String net.minidev.json.JSONArray
2021-05-29 15:32:11.249 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Name ROLE_ANYONE
2021-05-29 15:32:11.250 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Attribute name azp type java.lang.String
2021-05-29 15:32:11.250 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Attribute name auth_time type java.lang.String
2021-05-29 15:32:11.250 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Attribute name exp type java.lang.String
2021-05-29 15:32:11.250 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Attribute name session_state type java.lang.String
2021-05-29 15:32:11.250 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Attribute name iat type java.lang.String
2021-05-29 15:32:11.250 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Attribute name jti type java.lang.String
2021-05-29 15:32:11.250 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : UserInfo org.springframework.security.oauth2.core.oidc.OidcUserInfo@8be9a0b8
2021-05-29 15:32:11.250 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : UserInfo keys sub
2021-05-29 15:32:11.250 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : UserInfo keys email_verified
2021-05-29 15:32:11.250 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : UserInfo keys preferred_username
2021-05-29 15:32:11.250 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : userInfo DID NOT FIND A claim
2021-05-29 15:32:11.252 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Authority name org.springframework.security.core.authority.SimpleGrantedAuthority
2021-05-29 15:32:11.252 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Authority name org.springframework.security.core.authority.SimpleGrantedAuthority
2021-05-29 15:32:11.252 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Authority name org.springframework.security.core.authority.SimpleGrantedAuthority
2021-05-29 15:32:11.252 ERROR 7394 --- [or-http-epoll-5] com.jdriven.gateway.RoleConfig : Authority name org.springframework.security.core.authority.SimpleGrantedAuthority
2021-05-29 15:32:11.252 DEBUG 7394 --- [or-http-epoll-5] o.s.w.r.f.client.ExchangeFunctions : [34ff3355] Cancel signal (to close connection)
2021-05-29 15:32:11.252 DEBUG 7394 --- [or-http-epoll-5] o.s.w.r.f.client.ExchangeFunctions : [1b083d68] Cancel signal (to close connection)
2021-05-29 15:32:11.254 DEBUG 7394 --- [or-http-epoll-5] ebSessionServerSecurityContextRepository : Saved SecurityContext 'SecurityContextImpl [Authentication=OAuth2AuthenticationToken [Principal=Name: [anon], Granted Authorities: [[ROLE_USER, SCOPE_email, SCOPE_openid, SCOPE_profile, SCOPE_roles]], User Attributes: [{at_hash=GCz2JybWiLc-42ACnjLJ6w, sub=6de0d95f-95b0-419d-87a4-b2862e8d0763, resource_access={"spring-cloud-gateway-client":{"roles":["ROLE_ADMIN_CLIENT"]}}, email_verified=true, iss=http://clunk:8080/auth/realms/spring-cloud-gateway-realm, typ=ID, preferred_username=anon, nonce=2V8_3siQjTOIRbfs68BHwzvz3-dWeqXGUultzhJUWrA, aud=[spring-cloud-gateway-client], acr=0, realm_access={"roles":["ROLE_ANYONE"]}, azp=spring-cloud-gateway-client, auth_time=2021-05-29T14:24:18Z, exp=2021-05-29T14:52:11Z, session_state=dd226823-90bc-429e-9cac-bb575b7d4fa0, iat=2021-05-29T14:32:11Z, jti=7d479a85-d76e-4930-9c86-b384a56d7af5}], Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[]]]' in WebSession: 'org.springframework.web.server.session.InMemoryWebSessionStore$InMemoryWebSession@69c3d462'