<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring Security 中使用Keycloak作為認(rèn)證授權(quán)服務(wù)器

          共 14201字,需瀏覽 29分鐘

           ·

          2021-08-09 22:36

          Keycloak對(duì)流行的Java應(yīng)用提供了適配器。在系列文章的上一篇我們演示了針對(duì)Spring Boot的安全保護(hù),用的就是適配器的一種。Keycloak同樣提供Spring Security的適配器,后續(xù)的幾篇文章我們就來共同學(xué)習(xí)Spring Security適配器的使用。

          ?

          Keycloak的安裝可參考前面的系列教程。

          適配器集成

          在Spring 應(yīng)用中我們集成keycloak-spring-security-adapter

          <dependency>
              <groupId>org.keycloak</groupId>
              <artifactId>keycloak-spring-security-adapter</artifactId>
              <version>15.0.0</version>
          </dependency>

          在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>       

          然后就能利用Spring Security的特性來集成KeycloakKeycloak 提供了一個(gè) KeycloakWebSecurityConfigurerAdapter 作為創(chuàng)建WebSecurityConfigurer 實(shí)例的方便基類。我們可以編寫了一個(gè)配置類來定制我們的安全策略,就像這樣:

          @KeycloakConfiguration
          public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter
          {
              /**
               *  注冊了一個(gè)Keycloak的AuthenticationProvider
               */

              @Autowired
              public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
                  auth.authenticationProvider(keycloakAuthenticationProvider());
              }

              /**
               * 定義會(huì)話策略
               */

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

          ?

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

          配置完上面的然后我們直接啟動(dòng)應(yīng)用,結(jié)果并不像期望的那樣:

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

          拋出找不到 keycloak.json文件的異常。Keycloak支持的每個(gè)Java適配器都可以通過一個(gè)簡單的JSON文件進(jìn)行配置,我們?nèi)笔У木褪沁@個(gè)文件。

          {
            "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"
             }
          }

          上面包含的客戶端配置屬性都可以在Keycloak控制臺(tái)進(jìn)行配置,見下圖:

          配置Keycloak客戶端屬性

          也就是說我們需要的json文件和圖中的配置項(xiàng)是對(duì)應(yīng)的。比較人性化的是我們不需要自行編寫這個(gè)json文件,Keycloak提供了下載客戶端配置的方法,這里我只使用了必要的配置項(xiàng):

          你可以下載客戶端json配置

          引入客戶端配置

          雖然順利拿到json文件,但是加載這個(gè)json配置卻不太順利,經(jīng)過我的摸索需要實(shí)現(xiàn)一個(gè)KeycloakConfigResolver并注入Spring IoC,有下面兩種實(shí)現(xiàn)方式。

          復(fù)用Spring Boot Adapter配置

          直接復(fù)用Spring Boot的配置形式,先聲明Spring BootKeycloakConfigResolver實(shí)現(xiàn):

            /**
               * 復(fù)用spring boot 的方法
               *
               * @return the keycloak config resolver
               */

              @Bean
              public KeycloakConfigResolver keycloakConfigResolver() {
                  return new KeycloakSpringBootConfigResolver();
              }

          然后復(fù)用Spring Bootapplication.yaml的配置項(xiàng):

          復(fù)用Spring Boot配置項(xiàng)

          ?

          原來的角色資源映射約束失效。

          自定義實(shí)現(xiàn)

          你也可以自定義寫解析,這個(gè)時(shí)候json形式已經(jīng)不重要了,你可以將json文件的內(nèi)容存儲(chǔ)到任何你擅長的地方。

          /**
           * 自己寫解析
           *
           * @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);
                  }
              };
          }

          角色命名策略

          Spring Security會(huì)為每個(gè)角色添加ROLE_前綴,這需要我們聲明GrantedAuthoritiesMapper的實(shí)現(xiàn)SimpleAuthorityMapper來完成這一功能。KeycloakKeycloakAuthenticationProvider中配置該功能:

                  KeycloakAuthenticationProvider authenticationProvider = keycloakAuthenticationProvider();
                  authenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());

          完整的配置

          applicaiton.yaml:

          keycloak:
          # 聲明客戶端所在的realm
            realm: felord.cn
          # keycloak授權(quán)服務(wù)器的地址
            auth-server-url: http://localhost:8011/auth
          # 客戶端名稱
            resource: springboot-client
          # 聲明這是一個(gè)公開的客戶端,否則不能在keycloak外部環(huán)境使用,會(huì)403
            public-client: true

          ?

          這里要結(jié)合Keycloak導(dǎo)出的json文件配置。

          Spring Security配置:

          @KeycloakConfiguration
          public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
              
              /**
               * 復(fù)用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}
               *  這里會(huì)引入Keycloak的{@link AuthenticationProvider}實(shí)現(xiàn)
               *
               * @param auth the auth
               */

              @Autowired
              public void configureGlobal(AuthenticationManagerBuilder auth) {
                  KeycloakAuthenticationProvider authenticationProvider = keycloakAuthenticationProvider();
                  authenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
                  auth.authenticationProvider(authenticationProvider);
              }
              /**
               * 會(huì)話身份驗(yàn)證策略
               */

              @Bean
              @Override
              protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
                  return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
              }
              /**
               * 配置 session 監(jiān)聽器 保證單點(diǎn)退出生效
               *
               * @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();
              }
          }

          調(diào)用流程

          資源客戶端springboot-client有一個(gè)接口/admin/foo,當(dāng)未登錄調(diào)用該接口時(shí)會(huì)轉(zhuǎn)發(fā)到:

          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

          輸入正確的用戶密碼后才能得到期望的結(jié)果。

          ?

          典型的authorazation code flow

          總結(jié)

          Keycloak整合Spring Security的要點(diǎn)這里需要再梳理一下。在原生情況下,客戶端的配置、用戶的信息、角色信息都由Keycloak負(fù)責(zé);客戶端只負(fù)責(zé)角色和資源的映射關(guān)系。后續(xù)會(huì)深入并定制KeycloakSpring Security以滿足實(shí)際場景需要。




          往期推薦

          騰訊員工吐槽:團(tuán)隊(duì)來了個(gè)阿里高p,瞬間會(huì)議變多,群多了

          程序員加入新團(tuán)隊(duì)必問的20道問題

          只是想虐下春麗,一不小心擼了臺(tái)游戲機(jī)...

          Spring Boot中使用時(shí)序數(shù)據(jù)庫InfluxDB

          萬萬沒想到!logger.info() 還能導(dǎo)致線上故障?


          瀏覽 56
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品色色色 | 91绿奴在线播放 | 亚洲精品h | 久操免费视频 | 国产亚洲免费视频在线观看 |