Spring Cloud Security 為構建安全的SpringBoot應用提供了一系列解決方案鹅髓,結合Oauth2還可以實現(xiàn)更多功能瑟押,比如使用JWT令牌存儲信息,刷新令牌功能锅很,本文將對其結合JWT使用進行詳細介紹其馏。
JWT是JSON WEB TOKEN的縮寫,它是基于 RFC 7519 標準定義的一種可以安全傳輸?shù)牡腏SON對象爆安,由于使用了數(shù)字簽名叛复,所以是可信任和安全的。
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的字符串:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzI2ODI4MzEsInVzZXJfbmFtZSI6Im1hY3JvIiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iXSwianRpIjoiYzFhMDY0NWEtMjhiNS00NDY4LWI0YzctOTYyMzEzMTg1M2FmIiwiY2xpZW50X2lkIjoiYWRtaW4iLCJzY29wZSI6WyJhbGwiXX0.x4i6sRN49R6JSjd5hd1Fr2DdEMBsYdC4KB6Uw1huXPgCopy to clipboardErrorCopied
可以在該網(wǎng)站上獲得解析結果:https://jwt.io/
該模塊只是對oauth2-server模塊的擴展,直接復制過來擴展下下即可蹈垢。
在上一節(jié)中我們都是把令牌存儲在內存中的慷吊,這樣如果部署多個服務,就會導致無法使用令牌的問題曹抬。 Spring Cloud Security中有兩種存儲令牌的方式可用于解決該問題溉瓶,一種是使用Redis來存儲,另一種是使用JWT來存儲谤民。
在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存儲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中擴展一個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
如果我們需要獲取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認證測試服務