代码之家  ›  专栏  ›  技术社区  ›  HaVonTe

如何将Spring Security RemoteTokenService与KeyClope结合使用

  •  9
  • HaVonTe  · 技术社区  · 7 年前

    我设置了一个keyclope服务器。配置了领域和客户端等。 我成功地用“org.keydepeat:keydepeat-springbootstarter”编写了一个Spring引导服务,并保护了我的RestController。工作起来很有魅力。

    但是,当我尝试使用Spring安全性(没有KeyClope特定的依赖项)时,我被卡住了。

    这是我的gradle:

    dependencies {
    compile('org.springframework.boot:spring-boot-starter-security')
    compile('org.springframework.security.oauth:spring-security-oauth2')
    
    compile('org.springframework.boot:spring-boot-starter-web')
    compileOnly('org.projectlombok:lombok')
    testCompile('org.springframework.boot:spring-boot-starter-test')
    testCompile('org.springframework.security:spring-security-test')
    

    }

    这是我的安全配置:

    @Configuration
    @EnableResourceServer
    public class ResourceServerConfiguration extends 
    ResourceServerConfigurerAdapter {
    
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/person/**").hasRole("DEMO_SPRING_SECURITY")
            .anyRequest().authenticated()
            .and().formLogin().disable();
    }
    
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
    
        resources.resourceId("demo-client");
        RemoteTokenServices tokenServices = new RemoteTokenServices();
        tokenServices.setCheckTokenEndpointUrl(
            "http://localhost:8280/auth/realms/demo-realm/protocol/openid-connect/token/introspect");
        tokenServices.setClientId("demo-client");
        tokenServices.setClientSecret("80e19056-7770-4a4a-a3c4-06d8ac8792ef");
        resources.tokenServices(tokenServices);
    }
    }
    

    现在我尝试访问服务器:

    1. 获取访问令牌(通过REST客户端) 解码后的JWT如下所示:
    {
    "jti": "78c00562-d80a-4f5a-ab08-61ed10cb575c",
    "exp": 1509603570,
    "nbf": 0,
    "iat": 1509603270,
    "iss": "http://localhost:8280/auth/realms/demo-realm",
    "aud": "demo-client",
    "sub": "6ee90ba4-2854-49c1-9776-9aa95b6ae598",
    "typ": "Bearer",
    "azp": "demo-client",
    "auth_time": 0,
    "session_state": "68ce12fb-3b3f-429d-9390-0662f0503bbb",
    "acr": "1",
    "client_session": "ec0113e1-022a-482a-a26b-e5701e5edec1",
    "allowed-origins": [],
    "realm_access": {
      "roles": [
        "demo_user_role",
        "uma_authorization"
      ]
    },
    "resource_access": {
      "account": {
        "roles": [
          "manage-account",
          "manage-account-links",
          "view-profile"
        ]
      }
    },
    "name": "Jim Panse",
    "preferred_username": "demo-user",
    "given_name": "Jim",
    "family_name": "Panse",
    "email": "user@dmoain.com"
    }
    

    但我得到了一个AccessDeniedException。

    2017-11-02 07:18:05.344 DEBUG 17637 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated:
    

    负责人:演示客户;凭据:[受保护];已验证:true; 详细信息:remoteAddress=127.0.0.1,tokenType=BeareTokenValue=; 未授予任何权限2017-11-02 07:18:05.348调试17637--- [nio-8080-exec-1]o.s.s.access。选票基于肯定的:选民: 组织。springframework。安全网状物通道表示WebExpressionVoter@14032696, 返回:-1 2017-11-02 07:18:05.353调试17637---[nio-8080-exec-1] 非匿名);授权给AccessDeniedHandler

    组织。springframework。安全通道AccessDeniedException:访问是 否认

    我调试了RemoteTokenService,发现KeyClope使用完全相同的accesstoken进行响应。这很好。 但是比 DefaultAccessTokenConverter 尝试从字段中读取用户角色 authorities 这是不存在的。和 OAuth2WebSecurityExpressionHandler 评估用户不具有任何角色--&燃气轮机;拒绝访问

    所以我的问题是:

    2 回复  |  直到 5 年前
        1
  •  11
  •   Boomer    7 年前

    通过KeyClope管理控制台,您可以创建类型为的令牌映射器 用户领域角色 您的客户“演示客户”的索赔名称为“授权”。 然后,访问令牌包含此属性中的角色名称,并且没有自定义标记 DefaultAccessTokenConverter 是必需的。

        2
  •  5
  •   HaVonTe    7 年前

    在这里阐述了这个问题之后,我自己找到了一个解决方案。有时,试图表达一个问题会有所帮助。

    解决方案是重写DefaultAccessTokenConverter,教他如何读取“realm\u access”字段。它很难看,但很管用:

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
    
        resources.resourceId("demo-client");
        RemoteTokenServices tokenServices = new RemoteTokenServices();
        tokenServices.setCheckTokenEndpointUrl(
            "http://localhost:8280/auth/realms/demo-realm/protocol/openid-connect/token/introspect");
        tokenServices.setClientId("demo-client");
        tokenServices.setClientSecret("80e19056-7770-4a4a-a3c4-06d8ac8792ef");
        tokenServices.setAccessTokenConverter(new KeycloakAccessTokenConverter());
        resources.tokenServices(tokenServices);
    
    }
    private class KeycloakAccessTokenConverter extends DefaultAccessTokenConverter {
    
        @Override
        public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
            OAuth2Authentication oAuth2Authentication = super.extractAuthentication(map);
            Collection<GrantedAuthority> authorities = (Collection<GrantedAuthority>) oAuth2Authentication.getOAuth2Request().getAuthorities();
            if (map.containsKey("realm_access")) {
                Map<String, Object> realm_access = (Map<String, Object>) map.get("realm_access");
                if(realm_access.containsKey("roles")) {
                    ((Collection<String>) realm_access.get("roles")).forEach(r -> authorities.add(new SimpleGrantedAuthority(r)));
                }
            }
            return new OAuth2Authentication(oAuth2Authentication.getOAuth2Request(),oAuth2Authentication.getUserAuthentication());
        }
    }