Spring Security中使用Keycloak作为认证授权服务器

服务器
Keycloak对流行的Java应用提供了适配器。在系列文章的上一篇我们演示了针对Spring Boot的安全保护,用的就是适配器的一种。Keycloak同样提供Spring Security的适配器,后续的几篇文章我们就来共同学习Spring Security适配器的使用。

 [[415177]]

Keycloak对流行的Java应用提供了适配器。在系列文章的上一篇我们演示了针对Spring Boot的安全保护,用的就是适配器的一种。Keycloak同样提供Spring Security的适配器,后续的几篇文章我们就来共同学习Spring Security适配器的使用。

Keycloak的安装可参考前面的系列教程。

适配器集成

在Spring 应用中我们集成keycloak-spring-security-adapter:

<dependency> 
    <groupId>org.keycloak</groupId> 
    <artifactId>keycloak-spring-security-adapter</artifactId> 
    <version>15.0.0</version> 
</dependency> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

在Spring Boot中可以这样集成:

<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-security</artifactId> 
</dependency> 
<dependency> 
    <groupId>org.keycloak</groupId> 
    <artifactId>keycloak-spring-boot-starter</artifactId> 
    <version>15.0.0</version> 
</dependency>        
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

然后就能利用Spring Security的特性来集成Keycloak。Keycloak 提供了一个 KeycloakWebSecurityConfigurerAdapter 作为创建WebSecurityConfigurer 实例的方便基类。我们可以编写了一个配置类来定制我们的安全策略,就像这样:

@KeycloakConfiguration 
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter 

    /** 
     *  注册了一个Keycloak的AuthenticationProvider 
     */ 
    @Autowired 
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { 
        auth.authenticationProvider(keycloakAuthenticationProvider()); 
    } 
 
    /** 
     * 定义会话策略 
     */ 
    @Bean 
    @Override 
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { 
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); 
    } 
 
    /** 
     * 常见的Spring Security安全策略 
     */  
    @Override 
    protected void configure(HttpSecurity http) throws Exception 
    { 
        super.configure(http); 
        http 
                .authorizeRequests() 
                .antMatchers("/customers*").hasRole("USER"
                .antMatchers("/admin/**").hasRole("base_user"
                .anyRequest().permitAll(); 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.

注意:上面的配置并不能成功。

配置完上面的然后我们直接启动应用,结果并不像期望的那样:

java.io.FileNotFoundException: Unable to locate Keycloak configuration file: keycloak.json 
  • 1.

抛出找不到 keycloak.json文件的异常。Keycloak支持的每个Java适配器都可以通过一个简单的JSON文件进行配置,我们缺失的就是这个文件。


  "realm" : "demo"
  "resource" : "customer-portal"
  "realm-public-key" : "MIGfMA0GCSqGSIb3D...31LwIDAQAB"
  "auth-server-url" : "https://localhost:8443/auth"
  "ssl-required" : "external"
  "use-resource-role-mappings" : false
  "enable-cors" : true
  "cors-max-age" : 1000, 
  "cors-allowed-methods" : "POST, PUT, DELETE, GET"
  "cors-exposed-headers" : "WWW-Authenticate, My-custom-exposed-Header"
  "bearer-only" : false
  "enable-basic-auth" : false
  "expose-token" : true
  "verify-token-audience" : true
   "credentials" : { 
      "secret" : "234234-234234-234234" 
   }, 
 
   "connection-pool-size" : 20, 
   "socket-timeout-millis": 5000, 
   "connection-timeout-millis": 6000, 
   "connection-ttl-millis": 500, 
   "disable-trust-manager"false
   "allow-any-hostname" : false
   "truststore" : "path/to/truststore.jks"
   "truststore-password" : "geheim"
   "client-keystore" : "path/to/client-keystore.jks"
   "client-keystore-password" : "geheim"
   "client-key-password" : "geheim"
   "token-minimum-time-to-live" : 10, 
   "min-time-between-jwks-requests" : 10, 
   "public-key-cache-ttl": 86400, 
   "redirect-rewrite-rules" : { 
   "^/wsmaster/api/(.*)$" : "/api/$1" 
   } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.

上面包含的客户端配置属性都可以在Keycloak控制台进行配置,见下图:

配置Keycloak客户端属性

也就是说我们需要的json文件和图中的配置项是对应的。比较人性化的是我们不需要自行编写这个json文件,Keycloak提供了下载客户端配置的方法,这里我只使用了必要的配置项:

你可以下载客户端json配置

引入客户端配置

虽然顺利拿到json文件,但是加载这个json配置却不太顺利,经过我的摸索需要实现一个KeycloakConfigResolver并注入Spring IoC,有下面两种实现方式。

复用Spring Boot Adapter配置

直接复用Spring Boot的配置形式,先声明Spring Boot的KeycloakConfigResolver实现:

/** 
   * 复用spring boot 的方法 
   * 
   * @return the keycloak config resolver 
   */ 
  @Bean 
  public KeycloakConfigResolver keycloakConfigResolver() { 
      return new KeycloakSpringBootConfigResolver(); 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

然后复用Spring Boot的application.yaml的配置项:

复用Spring Boot配置项

原来的角色资源映射约束失效。

自定义实现

你也可以自定义写解析,这个时候json形式已经不重要了,你可以将json文件的内容存储到任何你擅长的地方。

/** 
 * 自己写解析 
 * 
 * @return the keycloak config resolver 
 */ 
@Bean 
public KeycloakConfigResolver fileKeycloakConfigResolver() { 
    return  new KeycloakConfigResolver() { 
        @SneakyThrows 
        @Override 
        public KeycloakDeployment resolve(HttpFacade.Request request) { 
            // json 文件放到resources 文件夹下 
            ClassPathResource classPathResource = new ClassPathResource("./keycloak.json"); 
            AdapterConfig adapterConfig = new ObjectMapper().readValue(classPathResource.getFile(), AdapterConfig.class); 
 
            return KeycloakDeploymentBuilder.build(adapterConfig); 
        } 
    }; 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

角色命名策略

Spring Security会为每个角色添加ROLE_前缀,这需要我们声明GrantedAuthoritiesMapper的实现SimpleAuthorityMapper来完成这一功能。Keycloak在KeycloakAuthenticationProvider中配置该功能:

KeycloakAuthenticationProvider authenticationProvider = keycloakAuthenticationProvider(); 
 authenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); 
  • 1.
  • 2.

完整的配置

applicaiton.yaml:

keycloak: 
# 声明客户端所在的realm 
  realm: felord.cn 
# keycloak授权服务器的地址 
  auth-server-url: http://localhost:8011/auth 
# 客户端名称 
  resource: springboot-client 
# 声明这是一个公开的客户端,否则不能在keycloak外部环境使用,会403 
  public-client: true 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

这里要结合Keycloak导出的json文件配置。

Spring Security配置:

@KeycloakConfiguration 
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter { 
     
    /** 
     * 复用spring boot 的方法 
     * 
     * @return the keycloak config resolver 
     */ 
    @Bean 
    public KeycloakConfigResolver keycloakConfigResolver() { 
        return new KeycloakSpringBootConfigResolver(); 
    } 
    /** 
     * 自己写解析 
     * 
     * @return the keycloak config resolver 
     */ 
//    @Bean 
    public KeycloakConfigResolver fileKeycloakConfigResolver() { 
        return request -> { 
            // json 文件放到resources 文件夹下 
            ClassPathResource classPathResource = new ClassPathResource("./keycloak.json"); 
            AdapterConfig adapterConfig = null
            try { 
                adapterConfig = new ObjectMapper().readValue(classPathResource.getFile(),  
                        AdapterConfig.class); 
            } catch (IOException e) { 
                e.printStackTrace(); 
            } 
 
            return KeycloakDeploymentBuilder.build(adapterConfig); 
        }; 
    } 
    /** 
     *  配置{@link AuthenticationManager} 
     *  这里会引入Keycloak的{@link AuthenticationProvider}实现 
     * 
     * @param auth the auth 
     */ 
    @Autowired 
    public void configureGlobal(AuthenticationManagerBuilder auth) { 
        KeycloakAuthenticationProvider authenticationProvider = keycloakAuthenticationProvider(); 
        authenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); 
        auth.authenticationProvider(authenticationProvider); 
    } 
    /** 
     * 会话身份验证策略 
     */ 
    @Bean 
    @Override 
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { 
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); 
    } 
    /** 
     * 配置 session 监听器 保证单点退出生效 
     * 
     * @return the servlet listener registration bean 
     */ 
    @Bean 
    public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() { 
        return new ServletListenerRegistrationBean<>(new HttpSessionEventPublisher()); 
    } 
    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
        super.configure(http); 
        http 
                .authorizeRequests() 
                .antMatchers("/customers*").hasRole("USER"
                .antMatchers("/admin/**").hasRole("base_user"
                .anyRequest().permitAll(); 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.

调用流程

资源客户端springboot-client有一个接口/admin/foo,当未登录调用该接口时会转发到:

http://localhost:8011/auth/realms/felord.cn/protocol/openid-connect/auth?response_type=code&client_id=springboot-client&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fsso%2Flogin&state=ec00d608-5ce7-47a0-acc8-8a20a2bfadfd&login=true&scope=openid 
  • 1.

输入正确的用户密码后才能得到期望的结果。

典型的authorazation code flow。

总结

Keycloak整合Spring Security的要点这里需要再梳理一下。在原生情况下,客户端的配置、用户的信息、角色信息都由Keycloak负责;客户端只负责角色和资源的映射关系。后续会深入并定制Keycloak和Spring Security以满足实际场景需要。

本文转载自微信公众号「码农小胖哥」,可以通过以下二维码关注。转载本文请联系码农小胖哥公众号。

责任编辑:武晓燕 来源: 码农小胖哥
相关推荐

2022-02-15 07:35:12

服务器KeycloakOAuth2

2021-10-19 14:02:12

服务器SpringSecurity

2021-07-07 07:33:49

开源Keycloak平台

2022-01-12 08:49:33

CaddyWeb服务器Go语言

2021-11-10 05:00:30

服务器Spring授权

2021-08-06 06:51:16

适配器配置Spring

2009-09-17 18:05:51

Nis服务器

2021-11-15 13:58:00

服务器配置授权

2021-08-29 23:33:44

OAuth2服务器Keycloak

2019-07-09 14:57:09

代理服务器NginxHTTPS

2010-08-25 21:25:41

DHCP服务器

2009-09-17 18:06:44

Nis服务器

2022-06-27 14:21:09

Spring语言

2021-11-11 07:38:15

服务器过滤器框架

2010-05-20 18:19:45

2017-09-06 09:21:04

服务器C&CDropBox

2009-07-06 10:26:32

2009-06-30 09:39:20

Win Server 打印服务器

2022-05-12 07:37:51

单点登录微服务开源

2021-12-28 11:13:05

安全认证 Spring Boot
点赞
收藏

51CTO技术栈公众号