Keycloak 在反应式Spring网关安全性中检索密钥斗篷角色

Keycloak 在反应式Spring网关安全性中检索密钥斗篷角色,keycloak,spring-cloud-gateway,security-roles,Keycloak,Spring Cloud Gateway,Security Roles,我从Zuul网关迁移到Spring网关。这迫使我放弃了Webflux的servlet。我使用keydove和keydove角色进行身份验证和授权 没有官方的反应式keydove实现,所以我使用SpringOAuth2。除了检索角色外,它还可以正常工作 我不能使用servlet拦截器,因为WebFlux不允许使用servlet。另外,SpringGateway似乎一般不允许拦截响应体 因此,我的问题仍然存在:如何在SpringGateway中检索keyClope角色,以便其安全性可以使用它们 以下

我从Zuul网关迁移到Spring网关。这迫使我放弃了Webflux的servlet。我使用keydove和keydove角色进行身份验证和授权

没有官方的反应式keydove实现,所以我使用SpringOAuth2。除了检索角色外,它还可以正常工作

我不能使用servlet拦截器,因为WebFlux不允许使用servlet。另外,SpringGateway似乎一般不允许拦截响应体

因此,我的问题仍然存在:如何在SpringGateway中检索keyClope角色,以便其安全性可以使用它们

以下是我使用的一些示例代码: 在SecurityConfig.java类中:

@Bean public security webfilterchain springsecurity filterchain(ServerHttpSecurity http){http.csrf().disable().authorizeExchange(exchanges->exchanges.pathMatchers(“/**”).hasAnyRole(“DIRECTOR”);}

application.yml:


spring.security.oauth2.client.provider.keydove.issuer-uri:..../realms/default
我自己也有同样的问题。我遇到的问题之一是,获取诸如
JWT
标记之类的内容的副本,即
keydape
具有编码设置的文本

    @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());
   }
}

正如您所看到的,
keydape
支持两种不同类型的角色令牌,但它们不是在顶层定义的,而是在
realm\u access
resource\u access
下定义的,不同之处在于资源访问定义了作为资源一部分的角色,而real\u access定义了跨所有领域定义的角色

要定义这些值,需要定义映射器,如下所示

要将这些值加载到Spring security,您需要定义一个
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());
        }
}
@戴夫 谢谢你提醒我这个问题。此后,我在WebFlux中找到了一个解决方法。我已重写ReactiveOAuth2UserService。默认情况下,它有两种风格:OAuth风格和Oidc风格。就我而言,我已经覆盖了Oidc:

@Component public class ReactiveKeycloakUserService extends OidcReactiveOAuth2UserService {
 @Override
 public Mono<OidcUser> loadUser(OidcUserRequest userRequest) throws ... {
  // Call super and then replace result with roles
 }
}
@Component公共类reactivekeydeposeurservice扩展了OidcReactiveOAuth2UserService{
@凌驾
公共Mono loadUser(OidcUserRequest userRequest)抛出{
//调用super,然后将结果替换为角色
}
}
Spring将注入我的实例,而不是默认的实例。从userRequest可以检索角色,在调用超类上的相同方法后,可以截取结果并在其上添加角色

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'
@Component public class ReactiveKeycloakUserService extends OidcReactiveOAuth2UserService {
 @Override
 public Mono<OidcUser> loadUser(OidcUserRequest userRequest) throws ... {
  // Call super and then replace result with roles
 }
}