<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ù)器

          共 14358字,需瀏覽 29分鐘

           ·

          2021-08-21 22:20


          源 / 碼農(nóng)小胖哥        文/ 

          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
          {
              /**
               *  注冊(cè)了一個(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í)際場景需要。

          好文推薦


          字節(jié)跳動(dòng)小組長無意中得知整個(gè)部門的薪資!自己只有28K!手下人卻拿35K!怎么辦?


          太尷尬!百度某程序員向領(lǐng)導(dǎo)請(qǐng)假去面試,卻在面試一樓大廳和領(lǐng)導(dǎo)相遇,網(wǎng)友:緣分啊!回去一起對(duì)對(duì)面試題!


          某程序員求助:盒馬優(yōu)選面試通過,卻被告知在阿里黑名單里,無法入職!自己只用過花唄借唄,都按時(shí)還款,為什么會(huì)進(jìn)入黑名單?


          END


          頂級(jí)程序員:topcoding

          做最好的程序員社區(qū):Java后端開發(fā)、Python、大數(shù)據(jù)、AI


          一鍵三連「分享」、「點(diǎn)贊」和「在看」


          瀏覽 17
          點(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>
                  五月丁香夫妻自拍偷拍 | 免费电影黄色视频 | 久久涩| 蜜桃视频网站免费观看 | 夜夜爽天天操 |