一 、認證服務器上發Token的改造:uuid字符串改造為JWT
之前生成的token都是一個無意義的字符串uuid,也存在着上一篇 https://www.cnblogs.com/lihaoyang/p/12203586.html 所說的幾個問題。本篇就要把token改造成JWT。
在認證服務器配置類AuthorizationServerConfigurerAdapter 的實現類 OAuth2AuthServerConfig 里,做相應的改造:
1,在TokenStore里,換掉之前的JdbcTokenStore,換成JwtTokenStore
需要new一個JwtAccessTokenConverter 轉換器,傳給 JwtTokenStore,這個Converter作用是設置 對 jwt 進行簽名的key。
2,在配置方法 configure(AuthorizationServerEndpointsConfigurer endpoints) 里,設置 endpoints.tokenEnhancer(jwtTokenEnhancer())
3,在配置方法 configure(AuthorizationServerSecurityConfigurer security) 里,設置 security.tokenKeyAccess("isAuthenticated()") ,會自動對外暴漏一個獲取JWT簽名的key的服務。而且是需要經過身份 認證的才能請求該服務。
OAuth2AuthServerConfig 源碼:
package com.nb.security.server.auth; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; import javax.sql.DataSource; /** * Created by: 李浩洋 on 2019-10-29 * * 認證服務器 **/ @EnableRedisHttpSession //啟用spring session redis @Configuration //這是一個配置類 @EnableAuthorizationServer //當前應用是一個認證服務器 public class OAuth2AuthServerConfig extends AuthorizationServerConfigurerAdapter {//AuthorizationServerConfigurerAdapter:認證服務器適配器 //Spring 對密碼加密的封裝,自己配置下 @Autowired private PasswordEncoder passwordEncoder; @Autowired private AuthenticationManager authenticationManager; @Qualifier("dataSource") @Autowired private DataSource dataSource; @Autowired private UserDetailsService userDetailsService; //tokenStore是進行存取token的接口,默認內存的實現還有redis,jdbc,jwt的實現(idea ctrl+H可看類關系) @Bean public TokenStore tokenStore(){ //return new JdbcTokenStore(dataSource);//這里配置用jdbc進行存取token return new JwtTokenStore(jwtTokenEnhancer());//jwt存取token } private JwtAccessTokenConverter jwtTokenEnhancer() { /** * 對jwt進行簽名的key,jwt是明文,簽名防篡改。 * 接收token的人需要用同樣的key驗簽名,需要把這個key通過服務暴漏出去,使用服務的人才能拿到key */ JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey("lihaoyang"); return converter; } /** * 1,配置客戶端應用的信息,讓認證服務器知道有哪些客戶端應用來申請令牌。 * * ClientDetailsServiceConfigurer:客戶端的詳情服務的配置 * @param clients * @throws Exception */ @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { /////////2--從數據庫里讀取客戶端應用配置信息,需要一個數據源, // spring會自動去 oauth_client_details 表里讀取客戶端信息 clients.jdbc(dataSource); } /* @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { ///////////////1----配置在了內存里 ////////////////////// clients.inMemory()//配置在內存里,后面修改為數據庫里 //~============== 注冊【客戶端應用】,使客戶端應用能夠訪問認證服務器 =========== .withClient("orderApp") .secret(passwordEncoder.encode("123456")) //123456 加密后 $2a$10$smHIzJWYZGDUR7zvtDWDZuCw7awklq23MBll/vETtEHHd37gdl.9K .scopes("read","write") //orderApp有哪些權限 .accessTokenValiditySeconds(3600) //token的有效期 .resourceIds("order-server") //資源服務器的id。發給orderApp的token,能訪問哪些資源服務器,可以多個 .authorizedGrantTypes("password")//授權方式,再給orderApp做授權的時候可以用哪種授權方式授權 //~=============客戶端應用配置結束 ===================== .and() //~============== 注冊【資源服務器-訂單服務】(因為訂單服務需要來認證服務器驗令牌),使訂單服務也能夠訪問認證服務器 =========== .withClient("orderService") .secret(passwordEncoder.encode("123456")) //123456 加密后 $2a$10$IW8NdL0L.0MPech5NmhYLen3vLj7tVeGrXi6sIV.u.WlBp1VKBcCm .scopes("read") //orderServer有哪些權限 .accessTokenValiditySeconds(3600) //token的有效期 .resourceIds("order-server") //資源服務器的id。 .authorizedGrantTypes("password");//授權方式, }*/ public static void main(String[] args) { //手動加密123456 System.err.println(new BCryptPasswordEncoder().encode("123456")); } /** *,2,配置用戶信息 * @param endpoints * @throws Exception */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { //傳給他一個authenticationManager用來校驗傳過來的用戶信息是不是合法的,注進來一個,自己實現 endpoints //這里指定userDetailsService是專門給refresh_token用的,其他的四種授權模式不需要這個 .userDetailsService(userDetailsService) .tokenStore(tokenStore()) //告訴服務器要用自定義的tokenStore里去存取token .tokenEnhancer(jwtTokenEnhancer()) //加入jwt需要用到 .authenticationManager(authenticationManager); } /** * 3,配置資源服務器過來驗token 的規則 * @param security * @throws Exception */ @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security /** * 暴漏驗token的服務,過來驗令牌有效性的請求,不是誰都能驗的,只有經過身份認證的請求才能調。 * 所謂身份認證就是,必須攜帶clientId,clientSecret,否則隨便一請求過來驗token是不驗的 */ .checkTokenAccess("isAuthenticated()") /** * 暴漏獲取jwt簽名key的服務,只有經過身份認證的請求才能調(同上) * 上邊 tokenStore里的signKey */ .tokenKeyAccess("isAuthenticated()"); } }
啟動認證服務器
clinet_details表數據
用PostMan做實驗,直接請求認證服務器,申請令牌接口 localhost:9090/oauth/token
返回數據
{ "access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsib3JkZXItc2VydmVyIl0sInVzZXJfbmFtZSI6ImxoeSIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJleHAiOjE1ODQwNzY1MzMsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwianRpIjoiZjI3MmNjYTktNzc3OS00MGU5LWEzZWYtNGZlMTViNjllNWJlIiwiY2xpZW50X2lkIjoiYWRtaW4ifQ.ANQ1BtRnQPJOMvnN5roYd8i1nmGNY1pnMjUVFIr2KPc", "token_type":"bearer", "refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsib3JkZXItc2VydmVyIl0sInVzZXJfbmFtZSI6ImxoeSIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJhdGkiOiJmMjcyY2NhOS03Nzc5LTQwZTktYTNlZi00ZmUxNWI2OWU1YmUiLCJleHAiOjE1ODQwNzY1NDMsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwianRpIjoiZDJlYWU1MzgtYjMxYy00NWUyLWJiNTktYzhlNWIxODgwYTAxIiwiY2xpZW50X2lkIjoiYWRtaW4ifQ.n-ySt7qqhCvRLLgGqw1GqJe6n10-4GB5J3tCa38x1bQ", "expires_in":19, "scope":"read write", "jti":"f272cca9-7779-40e9-a3ef-4fe15b69e5be" }
在https://jwt.io/ 網址 解析一下返回得access_token 信息:
至此,已經把token由無意義的uuid字符串,改造成了自包含用戶信息JWT。
代碼 :https://github.com/lhy1234/springcloud-security/tree/chapt-6-1-jwt01
歡迎關注個人公眾號一起交流學習: