使用spring boot(安全)和keycloak启用角色身份验证?

Posted

技术标签:

【中文标题】使用spring boot(安全)和keycloak启用角色身份验证?【英文标题】:Enable role authentication with spring boot (security) and keycloak? 【发布时间】:2019-10-06 11:21:02 【问题描述】:

我正在尝试做一件简单的事情。

想要向单个端点发出请求并发送不记名令牌(来自客户端),我希望验证此令牌并取决于在我的端点上的 keycloak 接受/拒绝请求上分配的角色。

我学习了很多教程甚至书籍,但大多数我根本不懂。

按照这个设置我的 keycloak 信息(领域、角色、用户) https://medium.com/@bcarunmail/securing-rest-api-using-keycloak-and-spring-oauth2-6ddf3a1efcc2

所以,

我基本上用一个客户端设置了我的密钥斗篷,一个具有特定角色“用户”的用户并像这样配置它:

@Configuration
@KeycloakConfiguration
//@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class SecurityConf extends KeycloakWebSecurityConfigurerAdapter

    /**
     * Registers the KeycloakAuthenticationProvider with the authentication manager.
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception 
        auth.authenticationProvider(keycloakAuthenticationProvider());
    

    /**
     * Defines the session authentication strategy.
     */
    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() 
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    

    @Bean
    public KeycloakSpringBootConfigResolver KeycloakConfigResolver() 
        return new KeycloakSpringBootConfigResolver();
    

    @Bean
    public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean(
            KeycloakAuthenticationProcessingFilter filter) 
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
        registrationBean.setEnabled(false);
        return registrationBean;
    

    @Bean
    public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean(
            KeycloakPreAuthActionsFilter filter) 
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
        registrationBean.setEnabled(false);
        return registrationBean;
    


    @Override
    protected void configure(HttpSecurity http) throws Exception
    
        super.configure(http);
        http
                .authorizeRequests()
                .antMatchers("/user/*").hasRole("admin")
                .antMatchers("/admin*").hasRole("user")

    

我不明白为什么在许多教程中我看到这个(作为最后一条规则):

.anyRequest().permitAll();

基本上当我设置我没有安全性时,我可以在没有承载令牌的情况下调用端点。

但是当我将此作为最后一条规则添加时

 .anyRequest().denyAll();

我总是得到 403。

调试我发现了这个:

请求是处理身份验证

f.KeycloakAuthenticationProcessingFilter : Attempting Keycloak authentication
o.k.a.BearerTokenRequestAuthenticator    : Found [1] values in authorization header, selecting the first value for Bearer.
o.k.a.BearerTokenRequestAuthenticator    : Verifying access_token
o.k.a.BearerTokenRequestAuthenticator    : successful authorized
a.s.a.SpringSecurityRequestAuthenticator : Completing bearer authentication. Bearer roles: [] 
o.k.adapters.RequestAuthenticator        : User 'testuser' invoking 'http://localhost:9090/api/user/123' on client 'users'
o.k.adapters.RequestAuthenticator        : Bearer AUTHENTICATED
f.KeycloakAuthenticationProcessingFilter : Auth outcome: AUTHENTICATED
o.s.s.authentication.ProviderManager     : Authentication attempt using org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider
o.s.s.core.session.SessionRegistryImpl   : Registering session 5B871A0E2AF55B70DC8E3B7436D79333, for principal testuser
f.KeycloakAuthenticationProcessingFilter : Authentication success using bearer token/basic authentication. Updating SecurityContextHolder to contain: org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken@355f68d6: Principal: testuser; Credentials: [PROTECTED]; Authenticated: true; Details: org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount@5d7a32a9; Not granted any authorities
[nio-9090-exec-3] o.s.security.web.FilterChainProxy        : /api/user/123 at position 8 of 15 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
nio-9090-exec-3] o.s.s.w.s.DefaultSavedRequest            : pathInfo: both null (property equals)
[nio-9090-exec-3] o.s.s.w.s.DefaultSavedRequest            : queryString: both null (property equals)

好像我没有承载角色...

我的依赖:

        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-spring-boot-starter</artifactId>
            <version>6.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-spring-security-adapter</artifactId>
            <version>6.0.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

我的问题?

我请求发送访问令牌:

client_id -> my client from keycloak
username -> my user from keycloak
password -> my password from keycloak
grant_type -> password
client_secret -> from keycloak

我得到一个令牌,然后我用它来请求我的应用程序 endoint。 无论我使用什么端点(具有用户角色或管理员角色的端点),我的请求始终有效。

在我的房产中,我有这样的东西:

keycloak:
  auth-server-url: http://localhost:8080/auth/
  resource: users-api
  credentials:
    secret : my-secret
  use-resource-role-mappings : true
  realm: my-realm
  realmKey:  my-key
  public-client: true
  principal-attribute: preferred_username
  bearer-only: true

知道在这种情况下如何实际启用角色吗?

我是否必须配置客户端才能使用 JWT?有什么想法吗?

我还在端点上添加了注释

@Secured("admin")
@PreAuthorize("hasAnyAuthority('admin')")

但他们似乎什么都不做......

-- 编辑--

修复 url 以匹配资源后,我仍然得到 403。

"realm_access": 
    "roles": [
      "offline_access",
      "admin",
      "uma_authorization"
    ]
  ,
  "resource_access": 
    "account": 
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    
  ,

resource_access 是否与我的问题有关?

【问题讨论】:

.antMatchers("/user/*").hasRole("admin") .antMatchers("/admin*").hasRole("user") 他们被颠倒了,不是吗? 保留是什么意思? 我的意思是.antMatchers("/user/*").hasRole("user") .antMatchers("/admin*").hasRole("admin") 首先,您是否从令牌中获得了角色?[调试]您是否调试过您的安全角色?如果是,那么您的前缀有问题,如果不是,那么您的 Keycloak 本身有问题,因为您的令牌中没有角色?如果可能的话,只用配置分享你的 git repo 简单的一个,所以我可以帮助你@jpganz18 【参考方案1】:

我知道这是一篇旧文章,但我只是写这篇文章以供将来参考,以防其他人有同样的问题。

如果您查看日志,Keycloak 成功验证了访问令牌,但没有任何授予权限。这就是 Spring 不授权请求并且您得到 HTTP 403 Forbidden 的原因:

f.KeycloakAuthenticationProcessingFilter : Authentication success using bearer token/basic authentication. Updating SecurityContextHolder to contain: org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken@355f68d6: Principal: testuser; Credentials: [PROTECTED]; Authenticated: true; Details: org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount@5d7a32a9; Not granted any authorities

这是因为 Keycloak 适配器配置为使用资源(即客户端级别)角色映射,而不是领域级别角色映射:

use-resource-role-mappings:如果设置为 true,适配器将在令牌内部查找用户的应用程序级角色映射。如果为 false,它将查看域级别的用户角色映射。这是可选的。默认值为 false。

Here 是关于适配器配置的链接。

所以,如果你想通过领域角色获得授权,属性应该是这样的:

keycloak:
  auth-server-url: http://localhost:8080/auth/
  resource: users-api
  credentials:
    secret : my-secret
  use-resource-role-mappings : false
  realm: my-realm
  realmKey:  my-key
  public-client: true
  principal-attribute: preferred_username
  bearer-only: true

注意:如果你想同时使用领域级别和客户端级别的角色映射,那么你应该重写 KeycloakAuthenticationProvider.authenticate() 方法,通过自己组合它们来提供必要的角色。 p>

【讨论】:

【参考方案2】:

迟到的答案,但希望它能帮助其他面临同样问题的人。 我遇到了和你完全相同的问题,对我来说,在配置类中,我必须通过设置授予的权限映射器来更改默认的 keycloakAuthenticationProvider(@Override 方法仅用于调试):

@Bean
public KeycloakSpringBootConfigResolver KeycloakConfigResolver() 
  KeycloakAuthenticationProvider keycloakAuthenticationProvider = new KeycloakAuthenticationProvider() 
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException 
      System.out.println("===========+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> authenticate ");
      KeycloakAuthenticationToken token = (KeycloakAuthenticationToken) authentication;
      for (String role : token.getAccount().getRoles()) 
        System.out.println("===========+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Role : " + role);
      

      return super.authenticate(authentication);
    
  ;
  keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
  auth.authenticationProvider(keycloakAuthenticationProvider);

【讨论】:

【参考方案3】:

    您是否尝试不使用 @Configuration ?我认为您只需要在 SecurityConf 类上添加 @KeycloakConfiguration 注释。

    您的 antMatchers 是否区分大小写?

 http
    .csrf().disable()
    .authorizeRequests()
    .antMatchers("/api/user/**").hasRole("user")
    .antMatchers("/api/admin/**").hasRole("admin")
    .anyRequest().authenticated();
    也请尝试此配置,以删除 Java 定义的 ROLE_* 约定:
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) 
        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        // SimpleAuthorityMapper is used to remove the ROLE_* conventions defined by Java so
        // we can use only admin or user instead of ROLE_ADMIN and ROLE_USER
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    
    如果你所有的端点都有相同的逻辑,安全配置应该足够了,你不需要其他注释。但是,如果您有另一个具有管理员角色的端点,它不在您的“/api/admin”控制器中,您可以尝试:
@PreAuthorize("hasRole('admin')")

【讨论】:

【参考方案4】:

在调试堆栈中:我看到您正在调用 /api/user/123 并且在您的安全配置中您正在保护 /user/* 这不一样,请将您的安全更改为:

.antMatchers("/api/user/*").hasRole("user")
                .antMatchers("/api/admin*").hasRole("admin")

P.S:你不需要注册KeycloakAuthenticationProcessingFilterKeycloakPreAuthActionsFilter

【讨论】:

为什么不需要注册? 它已经被 KeycloakWebSecurityConfigurerAdapter 处理好了 我仍然有同样的错误,知道吗?我更新了我的问题【参考方案5】:

permitAll:

当您希望允许任何请求访问特定资源/URL 时,您可以使用 permitAll。例如,登录 URL 应该可供所有人访问。

全部拒绝:

无论何时你想阻止特定 URL 的访问,无论请求来自何处或谁发出请求(管理员)

您还与 URL 和角色不匹配(您将带有管理员的 URL 授予 USER,反之亦然)。 (将角色用作 ROLE_ADMIN 或 ADMIN 或 USER 是一个很好的做法)形成您的堆栈我可以看到 未授予任何权限所以请重新检查权限与代码

 http
         .csrf().disable()
         .authorizeRequests()
         .antMatchers("/api/user/**").hasRole("ADMIN")
         .antMatchers("/api/admin/**").hasRole("USER")
         .anyRequest().authenticated();

【讨论】:

这次更新后还是报错,你能看看我的版本吗?

以上是关于使用spring boot(安全)和keycloak启用角色身份验证?的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot 和 Spring Actuator - 禁用安全性

Spring Boot + Spring Boot 安全启动错误

上手spring boot项目之spring boot整合shiro安全框架

上手spring boot项目之spring boot整合shiro安全框架

使用 EntityManager 进行 Spring Boot 和安全集成测试

使用 Spring Boot 的微服务中的安全问题