Spring Cloud微服務安全實戰_6-2_jwt認證之認證服務改造


一 、認證服務器上發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

 

 

歡迎關注個人公眾號一起交流學習:

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM