【问题标题】:How to use Spring Security RemoteTokenService with Keycloak如何将 Spring Security RemoteTokenService 与 Keycloak 一起使用
【发布时间】:2018-04-14 15:01:07
【问题描述】:

我设置了一个 Keycloak 服务器。配置领域和客户端等。 我成功地使用“org.keycloak:keycloak-spring-boot-starter”编写了一个 Spring Boot 服务并保护了我的 RestController。像魅力一样工作。

但是当我尝试使用 Spring Security(没有 keycloak 特定依赖项)时,我被卡住了。

这是我的毕业作品:

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:

org.springframework.security.oauth2.provider.OAuth2Authentication@1f3ee7e1: 委托人:demo-client;凭证:[受保护];已认证:真实; 详细信息:remoteAddress=127.0.0.1,tokenType=BearertokenValue=; 未授予任何权限 2017-11-02 07:18:05.348 DEBUG 17637 --- [nio-8080-exec-1] os.s.access.vote.AffirmativeBased :选民: org.springframework.security.web.access.expression.WebExpressionVoter@14032696, 返回:-1 2017-11-02 07:18:05.353 调试 17637 --- [nio-8080-exec-1] o.s.s.w.a.ExceptionTranslationFilter :访问被拒绝(用户是 不是匿名的);委托给 AccessDeniedHandler

org.springframework.security.access.AccessDeniedException: 访问是 拒绝

我调试了 RemoteTokenService,发现 Keycloak 响应的访问令牌完全相同。这很好。 但是DefaultAccessTokenConverter 尝试从不存在的字段authorities 中读取用户角色。 OAuth2WebSecurityExpressionHandler 评估用户没有任何角色。 --> 拒绝访问

所以我的问题:

让 Spring Security 与 Keycloak 访问令牌一起工作需要什么?

【问题讨论】:

  • 即使我也在尝试做同样的事情,请您分享代码
  • @chiranjeevigk 请在这里查看我的最后一个答案。

标签: java spring-security keycloak


【解决方案1】:

通过 keycloak 管理控制台,您可以为您的客户端“demo-client”创建一个声明名称为“authorities”的 User Realm Role 类型的令牌映射器。 然后访问令牌包含此属性中的角色名称,不需要自定义 DefaultAccessTokenConverter

【讨论】:

  • 你是对的。惊人的。如果我早点知道的话:-D
【解决方案2】:

在这里提出这个问题后,我自己找到了解决方案。有时尝试表达问题会有所帮助。

解决方法是重写 DefaultAccessTokenConverter 来教他如何读取“realm_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());
    }
}

【讨论】:

    猜你喜欢
    • 2018-08-21
    • 2014-12-11
    • 2016-09-04
    • 2020-11-02
    • 2020-05-25
    • 2021-09-03
    • 1970-01-01
    • 2011-01-15
    • 2020-02-29
    相关资源
    最近更新 更多