Spring Cloud Security:Oauth2結合JWT使用

Spring Cloud Security 為構建安全的SpringBoot應用提供了一系列解決方案鹅髓,結合Oauth2還可以實現(xiàn)更多功能瑟押,比如使用JWT令牌存儲信息,刷新令牌功能锅很,本文將對其結合JWT使用進行詳細介紹其馏。

JWT簡介

JWT是JSON WEB TOKEN的縮寫,它是基于 RFC 7519 標準定義的一種可以安全傳輸?shù)牡腏SON對象爆安,由于使用了數(shù)字簽名叛复,所以是可信任和安全的。

JWT的組成

JWT token的格式:header.payload.signature扔仓;

header中用于存放簽名的生成算法褐奥;

{

"alg": "HS256",

"typ": "JWT"

}Copy to clipboardErrorCopied

payload中用于存放數(shù)據(jù),比如過期時間当辐、用戶名抖僵、用戶所擁有的權限等;

{

"exp": 1572682831,

"user_name": "macro",

"authorities": [

? "admin"

],

"jti": "c1a0645a-28b5-4468-b4c7-9623131853af",

"client_id": "admin",

"scope": [

? "all"

]

}Copy to clipboardErrorCopied

signature為以header和payload生成的簽名缘揪,一旦header和payload被篡改耍群,驗證將失敗义桂。

JWT實例

這是一個JWT的字符串:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzI2ODI4MzEsInVzZXJfbmFtZSI6Im1hY3JvIiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iXSwianRpIjoiYzFhMDY0NWEtMjhiNS00NDY4LWI0YzctOTYyMzEzMTg1M2FmIiwiY2xpZW50X2lkIjoiYWRtaW4iLCJzY29wZSI6WyJhbGwiXX0.x4i6sRN49R6JSjd5hd1Fr2DdEMBsYdC4KB6Uw1huXPgCopy to clipboardErrorCopied

可以在該網(wǎng)站上獲得解析結果:https://jwt.io/

創(chuàng)建oauth2-jwt-server模塊

該模塊只是對oauth2-server模塊的擴展,直接復制過來擴展下下即可蹈垢。

oauth2中存儲令牌的方式

在上一節(jié)中我們都是把令牌存儲在內存中的慷吊,這樣如果部署多個服務,就會導致無法使用令牌的問題曹抬。 Spring Cloud Security中有兩種存儲令牌的方式可用于解決該問題溉瓶,一種是使用Redis來存儲,另一種是使用JWT來存儲谤民。

使用Redis存儲令牌

在pom.xml中添加Redis相關依賴:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>Copy to clipboardErrorCopied

在application.yml中添加redis相關配置:

spring:

? redis: #redis相關配置

? ? password: 123456 #有密碼時設置Copy to clipboardErrorCopied

添加在Redis中存儲令牌的配置:

/**

* 使用redis存儲token的配置

* Created by macro on 2019/10/8.

*/@ConfigurationpublicclassRedisTokenStoreConfig{@AutowiredprivateRedisConnectionFactoryredisConnectionFactory;@BeanpublicTokenStoreredisTokenStore(){returnnewRedisTokenStore(redisConnectionFactory);}}Copy to clipboardErrorCopied

在認證服務器配置中指定令牌的存儲策略為Redis:

/**

* 認證服務器配置

* Created by macro on 2019/9/30.

*/@Configuration@EnableAuthorizationServerpublicclassAuthorizationServerConfigextendsAuthorizationServerConfigurerAdapter{@AutowiredprivatePasswordEncoderpasswordEncoder;@AutowiredprivateAuthenticationManagerauthenticationManager;@AutowiredprivateUserServiceuserService;@Autowired@Qualifier("redisTokenStore")privateTokenStoretokenStore;/**

? ? * 使用密碼模式需要配置

? ? */@Overridepublicvoidconfigure(AuthorizationServerEndpointsConfigurerendpoints){endpoints.authenticationManager(authenticationManager).userDetailsService(userService).tokenStore(tokenStore);//配置令牌存儲策略}//省略代碼...}Copy to clipboardErrorCopied

運行項目后使用密碼模式來獲取令牌堰酿,訪問如下地址:http://localhost:9401/oauth/token

進行獲取令牌操作,可以發(fā)現(xiàn)令牌已經(jīng)被存儲到Redis中张足。

使用JWT存儲令牌

添加使用JWT存儲令牌的配置:

/**

* 使用Jwt存儲token的配置

* Created by macro on 2019/10/8.

*/@ConfigurationpublicclassJwtTokenStoreConfig{@BeanpublicTokenStorejwtTokenStore(){returnnewJwtTokenStore(jwtAccessTokenConverter());}@BeanpublicJwtAccessTokenConverterjwtAccessTokenConverter(){JwtAccessTokenConverteraccessTokenConverter=newJwtAccessTokenConverter();accessTokenConverter.setSigningKey("test_key");//配置JWT使用的秘鑰returnaccessTokenConverter;}}Copy to clipboardErrorCopied

在認證服務器配置中指定令牌的存儲策略為JWT:

/**

* 認證服務器配置

* Created by macro on 2019/9/30.

*/@Configuration@EnableAuthorizationServerpublicclassAuthorizationServerConfigextendsAuthorizationServerConfigurerAdapter{@AutowiredprivatePasswordEncoderpasswordEncoder;@AutowiredprivateAuthenticationManagerauthenticationManager;@AutowiredprivateUserServiceuserService;@Autowired@Qualifier("jwtTokenStore")privateTokenStoretokenStore;@AutowiredprivateJwtAccessTokenConverterjwtAccessTokenConverter;@AutowiredprivateJwtTokenEnhancerjwtTokenEnhancer;/**

? ? * 使用密碼模式需要配置

? ? */@Overridepublicvoidconfigure(AuthorizationServerEndpointsConfigurerendpoints){endpoints.authenticationManager(authenticationManager).userDetailsService(userService).tokenStore(tokenStore)//配置令牌存儲策略.accessTokenConverter(jwtAccessTokenConverter);}//省略代碼...}Copy to clipboardErrorCopied

運行項目后使用密碼模式來獲取令牌触创,訪問如下地址:http://localhost:9401/oauth/token

發(fā)現(xiàn)獲取到的令牌已經(jīng)變成了JWT令牌,將access_token拿到https://jwt.io/?網(wǎng)站上去解析下可以獲得其中內容为牍。

{

? "exp": 1572682831,

? "user_name": "macro",

? "authorities": [

? ? "admin"

? ],

? "jti": "c1a0645a-28b5-4468-b4c7-9623131853af",

? "client_id": "admin",

? "scope": [

? ? "all"

? ]

}Copy to clipboardErrorCopied

擴展JWT中存儲的內容

有時候我們需要擴展JWT中存儲的內容哼绑,這里我們在JWT中擴展一個key為enhance,value為enhance info的數(shù)據(jù)碉咆。

繼承TokenEnhancer實現(xiàn)一個JWT內容增強器:

/**

* Jwt內容增強器

* Created by macro on 2019/10/8.

*/publicclassJwtTokenEnhancerimplementsTokenEnhancer{@OverridepublicOAuth2AccessTokenenhance(OAuth2AccessTokenaccessToken,OAuth2Authenticationauthentication){Map<String,Object>info=newHashMap<>();info.put("enhance","enhance info");((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(info);returnaccessToken;}}Copy to clipboardErrorCopied

創(chuàng)建一個JwtTokenEnhancer實例:

/**

* 使用Jwt存儲token的配置

* Created by macro on 2019/10/8.

*/@ConfigurationpublicclassJwtTokenStoreConfig{//省略代碼...@BeanpublicJwtTokenEnhancerjwtTokenEnhancer(){returnnewJwtTokenEnhancer();}}Copy to clipboardErrorCopied

在認證服務器配置中配置JWT的內容增強器:

/**

* 認證服務器配置

* Created by macro on 2019/9/30.

*/@Configuration@EnableAuthorizationServerpublicclassAuthorizationServerConfigextendsAuthorizationServerConfigurerAdapter{@AutowiredprivatePasswordEncoderpasswordEncoder;@AutowiredprivateAuthenticationManagerauthenticationManager;@AutowiredprivateUserServiceuserService;@Autowired@Qualifier("jwtTokenStore")privateTokenStoretokenStore;@AutowiredprivateJwtAccessTokenConverterjwtAccessTokenConverter;@AutowiredprivateJwtTokenEnhancerjwtTokenEnhancer;/**

? ? * 使用密碼模式需要配置

? ? */@Overridepublicvoidconfigure(AuthorizationServerEndpointsConfigurerendpoints){TokenEnhancerChainenhancerChain=newTokenEnhancerChain();List<TokenEnhancer>delegates=newArrayList<>();delegates.add(jwtTokenEnhancer);//配置JWT的內容增強器delegates.add(jwtAccessTokenConverter);enhancerChain.setTokenEnhancers(delegates);endpoints.authenticationManager(authenticationManager).userDetailsService(userService).tokenStore(tokenStore)//配置令牌存儲策略.accessTokenConverter(jwtAccessTokenConverter).tokenEnhancer(enhancerChain);}//省略代碼...}Copy to clipboardErrorCopied

運行項目后使用密碼模式來獲取令牌抖韩,之后對令牌進行解析,發(fā)現(xiàn)已經(jīng)包含擴展的內容疫铜。

{

? "user_name": "macro",

? "scope": [

? ? "all"

? ],

? "exp": 1572683821,

? "authorities": [

? ? "admin"

? ],

? "jti": "1ed1b0d8-f4ea-45a7-8375-211001a51a9e",

? "client_id": "admin",

? "enhance": "enhance info"

}Copy to clipboardErrorCopied

Java中解析JWT中的內容

如果我們需要獲取JWT中的信息茂浮,可以使用一個叫jjwt的工具包。

在pom.xml中添加相關依賴:

<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.0</version></dependency>Copy to clipboardErrorCopied

修改UserController類块攒,使用jjwt工具類來解析Authorization頭中存儲的JWT內容励稳。

/**

* Created by macro on 2019/9/30.

*/@RestController@RequestMapping("/user")publicclassUserController{@GetMapping("/getCurrentUser")publicObjectgetCurrentUser(Authenticationauthentication,HttpServletRequestrequest){Stringheader=request.getHeader("Authorization");Stringtoken=StrUtil.subAfter(header,"bearer ",false);returnJwts.parser().setSigningKey("test_key".getBytes(StandardCharsets.UTF_8)).parseClaimsJws(token).getBody();}}Copy to clipboardErrorCopied

將令牌放入Authorization頭中,訪問如下地址獲取信息:http://localhost:9401/user/getCurrentUser

刷新令牌

在Spring Cloud Security 中使用oauth2時囱井,如果令牌失效了驹尼,可以使用刷新令牌通過refresh_token的授權模式再次獲取access_token。

只需修改認證服務器的配置庞呕,添加refresh_token的授權模式即可新翎。

/**

* 認證服務器配置

* Created by macro on 2019/9/30.

*/@Configuration@EnableAuthorizationServerpublicclassAuthorizationServerConfigextendsAuthorizationServerConfigurerAdapter{@Overridepublicvoidconfigure(ClientDetailsServiceConfigurerclients)throwsException{clients.inMemory().withClient("admin").secret(passwordEncoder.encode("admin123456")).accessTokenValiditySeconds(3600).refreshTokenValiditySeconds(864000).redirectUris("http://www.baidu.com").autoApprove(true)//自動授權配置.scopes("all").authorizedGrantTypes("authorization_code","password","refresh_token");//添加授權模式}}Copy to clipboardErrorCopied

使用刷新令牌模式來獲取新的令牌,訪問如下地址:http://localhost:9401/oauth/token

使用到的模塊

springcloud-learning

└── oauth2-jwt-server -- 使用jwt的oauth2認證測試服務

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末住练,一起剝皮案震驚了整個濱河市地啰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌讲逛,老刑警劉巖亏吝,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異盏混,居然都是意外死亡蔚鸥,警方通過查閱死者的電腦和手機惜论,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來止喷,“玉大人馆类,你說我怎么就攤上這事〉” “怎么了乾巧?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長预愤。 經(jīng)常有香客問我沟于,道長,這世上最難降的妖魔是什么鳖粟? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任社裆,我火速辦了婚禮,結果婚禮上向图,老公的妹妹穿的比我還像新娘。我一直安慰自己标沪,他們只是感情好榄攀,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著金句,像睡著了一般檩赢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上违寞,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天贞瞒,我揣著相機與錄音,去河邊找鬼趁曼。 笑死军浆,一個胖子當著我的面吹牛,可吹牛的內容都是我干的挡闰。 我是一名探鬼主播乒融,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼摄悯!你這毒婦竟也來了赞季?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤奢驯,失蹤者是張志新(化名)和其女友劉穎申钩,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瘪阁,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡撒遣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年断盛,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片愉舔。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡钢猛,死狀恐怖,靈堂內的尸體忽然破棺而出轩缤,到底是詐尸還是另有隱情命迈,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布火的,位于F島的核電站壶愤,受9級特大地震影響,放射性物質發(fā)生泄漏馏鹤。R本人自食惡果不足惜征椒,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望湃累。 院中可真熱鬧勃救,春花似錦、人聲如沸治力。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宵统。三九已至晕讲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間马澈,已是汗流浹背瓢省。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留痊班,地道東北人勤婚。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像辩块,于是被迫代替她去往敵國和親蛔六。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容