paulwong

          使用Spring Cloud Security OAuth2搭建授權(quán)服務(wù)

          Spring Cloud Security OAuth2 是 Spring 對(duì) OAuth2 的開源實(shí)現(xiàn),優(yōu)點(diǎn)是能與Spring Cloud技術(shù)線無(wú)縫集成,如果全部使用默認(rèn)配置,開發(fā)者只需要添加注解就能完成 OAuth2 授權(quán)服務(wù)的搭建。

          1. 添加依賴

          授權(quán)服務(wù)是基于Spring Security的,因此需要在項(xiàng)目中引入兩個(gè)依賴:

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

          <dependency>
                   <groupId>org.springframework.cloud</groupId>
                   <artifactId>spring-cloud-starter-oauth2</artifactId>
           </dependency>


          前者為 Security,后者為Security的OAuth2擴(kuò)展。

          2. 添加注解和配置

          在啟動(dòng)類中添加@EnableAuthorizationServer注解:

          @SpringBootApplication
          @EnableAuthorizationServer
          public class AlanOAuthApplication {
              public static void main(String[] args) {
                  SpringApplication.run(AlanOAuthApplication.class, args);
              }
          }


          完成這些我們的授權(quán)服務(wù)最基本的骨架就已經(jīng)搭建完成了。但是要想跑通整個(gè)流程,我們必須分配 client_idclient_secret才行。Spring Security OAuth2的配置方法是編寫@Configuration類繼承AuthorizationServerConfigurerAdapter,然后重寫void configure(ClientDetailsServiceConfigurer clients)方法,如:

          @Override
              public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
                  clients.inMemory() // 使用in-memory存儲(chǔ)
                          .withClient("client") // client_id
                          .secret("secret") // client_secret
                          .authorizedGrantTypes("authorization_code") // 該client允許的授權(quán)類型
                          .scopes("app"); // 允許的授權(quán)范圍
              }


          3. 授權(quán)流程

          訪問(wèn)授權(quán)頁(yè)面:

          localhost:8080/oauth/authorize?client_id=client&response_type=code&redirect_uri=http://www.baidu.com


          此時(shí)瀏覽器會(huì)讓你輸入用戶名密碼,這是因?yàn)?Spring Security 在默認(rèn)情況下會(huì)對(duì)所有URL添加Basic Auth認(rèn)證。默認(rèn)的用戶名為user, 密碼是隨機(jī)生成的,在控制臺(tái)日志中可以看到。

          oauth2

          畫風(fēng)雖然很簡(jiǎn)陋,但是基本功能都具備了。點(diǎn)擊Authorize后,瀏覽器就會(huì)重定向到百度,并帶上code參數(shù):

          這里寫圖片描述

          拿到code以后,就可以調(diào)用

          POST/GET http://client:secret@localhost:8080/oauth/token
          • 1

          來(lái)?yè)Q取access_token了:

          curl -X POST -H "Cache-Control: no-cache" -H "Content-Type: application/x-www-form-urlencoded" -d 'grant_type=authorization_code&code=Li4NZo&redirect_uri=http://www.baidu.com' "http://client:secret@localhost:8080/oauth/token"

          返回如下:

          {
            "access_token": "32a1ca28-bc7a-4147-88a1-c95abcc30556",
            "token_type": "bearer",
            "expires_in": 2591999,
            "scope": "app"
          }

          到此我們最最基本的授權(quán)服務(wù)就搭建完成了。然而,這僅僅是個(gè)demo,如果要在生產(chǎn)環(huán)境中使用,還需要做更多的工作。

          4. 使用MySQL存儲(chǔ)access_token和client信息

          把授權(quán)服務(wù)器中的數(shù)據(jù)存儲(chǔ)到數(shù)據(jù)庫(kù)中并不難,因?yàn)?Spring Cloud Security OAuth 已經(jīng)為我們?cè)O(shè)計(jì)好了一套Schema和對(duì)應(yīng)的DAO對(duì)象。但在使用之前,我們需要先對(duì)相關(guān)的類有一定的了解。

          4.1 相關(guān)接口

          Spring Cloud Security OAuth2通過(guò)DefaultTokenServices類來(lái)完成token生成、過(guò)期等 OAuth2 標(biāo)準(zhǔn)規(guī)定的業(yè)務(wù)邏輯,而DefaultTokenServices又是通過(guò)TokenStore接口完成對(duì)生成數(shù)據(jù)的持久化。在上面的demo中,TokenStore的默認(rèn)實(shí)現(xiàn)為InMemoryTokenStore,即內(nèi)存存儲(chǔ)。 對(duì)于Client信息,ClientDetailsService接口負(fù)責(zé)從存儲(chǔ)倉(cāng)庫(kù)中讀取數(shù)據(jù),在上面的demo中默認(rèn)使用的也是InMemoryClientDetialsService實(shí)現(xiàn)類。說(shuō)到這里就能看出,要想使用數(shù)據(jù)庫(kù)存儲(chǔ),只需要提供這些接口的實(shí)現(xiàn)類即可。慶幸的是,框架已經(jīng)為我們寫好JDBC實(shí)現(xiàn)了,即JdbcTokenStoreJdbcClientDetailsService

          4.2 建表

          要想使用這些JDBC實(shí)現(xiàn),首先要建表。框架為我們提前設(shè)計(jì)好了schema, 在github上:https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql

          在使用這套表結(jié)構(gòu)之前要注意的是,對(duì)于MySQL來(lái)說(shuō),默認(rèn)建表語(yǔ)句中主鍵是varchar(255)類型,在mysql中執(zhí)行會(huì)報(bào)錯(cuò),原因是mysql對(duì)varchar主鍵長(zhǎng)度有限制。所以這里改成128即可。其次,語(yǔ)句中會(huì)有某些字段為LONGVARBINARY類型,它對(duì)應(yīng)mysql的blob類型,也需要修改一下。

          4.3 配置

          數(shù)據(jù)庫(kù)建好后,下一步就是配置框架使用JDBC實(shí)現(xiàn)。方法還是編寫@Configuration類繼承AuthorizationServerConfigurerAdapter

          @Autowired
              private AuthenticationManager authenticationManager;

              @Autowired
              private DataSource dataSource;
              @Bean // 聲明TokenStore實(shí)現(xiàn)
              public TokenStore tokenStore() {
                  return new JdbcTokenStore(dataSource);
              }
              @Bean // 聲明 ClientDetails實(shí)現(xiàn)
              public ClientDetailsService clientDetails() {
                  return new JdbcClientDetailsService(dataSource);
              }
              @Override // 配置框架應(yīng)用上述實(shí)現(xiàn)
              public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
                  endpoints.authenticationManager(authenticationManager);
                  endpoints.tokenStore(tokenStore());

                  // 配置TokenServices參數(shù)
                  DefaultTokenServices tokenServices = new DefaultTokenServices();
                  tokenServices.setTokenStore(endpoints.getTokenStore());
                  tokenServices.setSupportRefreshToken(false);
                  tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
                  tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
                  tokenServices.setAccessTokenValiditySeconds( (int) TimeUnit.DAYS.toSeconds(30)); // 30天
                  endpoints.tokenServices(tokenServices);
              }

          完成這些后,框架就會(huì)將中間產(chǎn)生的數(shù)據(jù)寫到mysql中了。oauth_client_details是client表,可以直接在該表中添加記錄來(lái)添加client: 
          這里寫圖片描述

          4.4 需要注意的地方

          這里不得不說(shuō) Spring 設(shè)計(jì)有一個(gè)奇葩地的方。注意看oauth_access_token表是存放訪問(wèn)令牌的,但是并沒有直接在字段中存放token。Spring 使用OAuth2AccessToken來(lái)抽象與令牌有關(guān)的所有屬性,在寫入到數(shù)據(jù)庫(kù)時(shí),Spring將該對(duì)象通過(guò)JDK自帶的序列化機(jī)制序列成字節(jié)直接保存到了該表的token字段中。也就是說(shuō),如果只看數(shù)據(jù)表你是看不出access_token的值是多少,過(guò)期時(shí)間等信息的。這就給資源服務(wù)器的實(shí)現(xiàn)帶來(lái)了麻煩。我們的資源提供方并沒有使用Spring Security,也不想引入 Spring Security 的任何依賴,這時(shí)候就只能將 DefaultOAuth2AccessToken的源碼copy到資源提供方的項(xiàng)目中,然后讀取token字段并反序列化還原對(duì)象來(lái)獲取token信息。但是如果這樣做還會(huì)遇到反序列化兼容性的問(wèn)題,具體解決方法參考我另一篇博文:http://blog.csdn.net/neosmith/article/details/52539614

          5. 總結(jié)

          至此一個(gè)能在生產(chǎn)環(huán)境下使用的授權(quán)服務(wù)就搭建好了。其實(shí)我們?cè)趯?shí)際使用時(shí)應(yīng)該適當(dāng)定制JdbcTokenStoreClientDetailsService來(lái)實(shí)適應(yīng)業(yè)務(wù)需要,甚至可以直接從0開始實(shí)現(xiàn)接口,完全不用框架提供的實(shí)現(xiàn)。另外,Spring 直接將DefaultOAuth2AccessToken序列化成字節(jié)保存到數(shù)據(jù)庫(kù)中的設(shè)計(jì),我認(rèn)為是非常不合理的。或許設(shè)計(jì)者的初衷是保密access_token,但是通過(guò)加密的方法也可以實(shí)現(xiàn),完全不應(yīng)該直接扔字節(jié)。不過(guò)通過(guò)定制TokenStore接口,我們可以使用自己的表結(jié)構(gòu)而不拘泥于默認(rèn)實(shí)現(xiàn)。

          http://blog.csdn.net/tracker_w/article/category/6360121

          http://blog.csdn.net/neosmith/article/details/52539927

          posted on 2016-09-16 18:22 paulwong 閱讀(8752) 評(píng)論(0)  編輯  收藏 所屬分類: MICROSERVICESPRING CLOUD

          主站蜘蛛池模板: 灯塔市| 岳普湖县| 宜春市| 汕头市| 鄂托克旗| 郧西县| 云浮市| 永宁县| 德兴市| 苍南县| 白银市| 新干县| 乐亭县| 石泉县| 漯河市| 深圳市| 班戈县| 通化市| 米脂县| 乌拉特前旗| 大关县| 尼木县| 兰坪| 三明市| 安乡县| 西宁市| 汶川县| 江油市| 靖安县| 苗栗县| 东辽县| 景宁| 宜兰县| 祁门县| 土默特左旗| 临沭县| 聂拉木县| 兴业县| 驻马店市| 铜陵市| 自治县|