SpringBoot 整合 oauth2(五)實現(xiàn) jwt 及 擴展

什么是jwt顶伞,即 json web token廉油。JWT是一種用于雙方之間傳遞安全信息的簡潔的蛙婴、URL安全的表述性聲明規(guī)范粗井。也是一種token,但是和token有一些不同街图。

jwt優(yōu)點:

  1. 自包含
  2. 防篡改
  3. 可自定義擴展

JWT的結構

JWT包含了使用.分割的三部分:

  1. Header 頭部
  2. Payload 負載
  3. Signature 簽名

比如:eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjoiQSJ9

三個新東西

  1. 什么是自包含浇衬?
    字符串里包含用戶信息。
  2. 什么是防篡改餐济?
    簽名用于驗證消息的發(fā)送者以及消息是沒有經(jīng)過篡改的耘擂。
  3. 可擴展是什么意思?
    你可以在Payload部分加入自己想加入的json字符串
那我們既然token的實現(xiàn)方式那么優(yōu)秀絮姆,為什么還要有jwt呢醉冤,這就需要了解他們的生成機制秩霍。

token生成的其實就是一個UUID,和業(yè)務沒有絲毫的關系蚁阳,這樣帶來最大的問題铃绒,就是需要人工持久化處理token(像處理分布式下的sessionId一樣)。但是jwt就不需要螺捐,因為自包含颠悬,所以token里有身份驗證信息,不需要做后臺持久化處理定血,前端每次請求被保護的資源時請求頭里帶上該token就可以實現(xiàn)赔癌。

好了開始正文。

本文建立在第三節(jié)的基礎上澜沟。這里是順風車

1. 新增JwtTokenConfig

    @Configuration
    public class JwtTokenConfig{

        @Bean
        public TokenStore jwtTokenStore(){
            return new JwtTokenStore(jwtAccessTokenConverter());
        }

        /**
         * token生成處理:指定簽名
         */
        @Bean
        public JwtAccessTokenConverter jwtAccessTokenConverter(){
            JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
            accessTokenConverter.setSigningKey("internet_plus");
            return accessTokenConverter;
        }
    }

jwtTokenStore方法返回一個TokenStore對象的子對象JwtTokenStore灾票,供給認證服務器取來給授權服務器端點配置器,通俗點就是讓MyAuthorizationServerConfig能注入到值茫虽。

jwtAccessTokenConverter方法是根據(jù)簽名生成JwtToken刊苍,同樣也需要在MyAuthorizationServerConfig類里注入。

修改MyAuthorizationServerConfig 的 configure方法

/**
 * 認證服務器
 * Created by Fant.J.
 */
@Configuration
@EnableAuthorizationServer
public class MyAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {


    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private TokenStore tokenStore;
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;



    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        super.configure(security);
    }

    /**
     * 客戶端配置(給誰發(fā)令牌)
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory().withClient(ConsParams.Auth.GET_CLIENT_ID)
                .secret(ConsParams.Auth.GET_SECRET)
                //有效時間 2小時
                .accessTokenValiditySeconds(ConsParams.Auth.GET_TOKEN_VALIDITY_SECONDS)
                //密碼授權模式和刷新令牌
                .authorizedGrantTypes(ConsParams.Auth.GE_TAUTHORIZED_GRANT_TYPES)
                .scopes(ConsParams.Auth.GE_TSCOPES);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .tokenStore(tokenStore)
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService)
                .accessTokenConverter(jwtAccessTokenConverter);
        }
    }
}

和第三節(jié)不同的是 endpoints添加了accessTokenConverter屬性濒析,它規(guī)定了token生成器是jwtAccessTokenConverter班缰,并按照我們設置的簽名來生成。

啟動項目

給/oauth/token 發(fā)送post請求獲取token

請求頭:Authorization:Basic +clientid:secret 的base64加密字符串 (認證服務器中設置的client信息)
請求參數(shù):username password (用戶登陸賬號密碼)


2. JWT擴展

在前文我們介紹過jwt的可擴展性悼枢,一般情況下實現(xiàn)jwt就可以了,但是有一些特殊需求脾拆,比如你想在返回的token中附帶一些別的信息馒索,這就需要我們對jwt進行擴展。

2.1 修改JwtTokenConfig

新增jwtTokenEnhancer方法

@Configuration
    public class JwtTokenConfig{

        @Bean
        public TokenStore jwtTokenStore(){
            return new JwtTokenStore(jwtAccessTokenConverter());
        }

        /**
         * token生成處理:指定簽名
         */
        @Bean
        public JwtAccessTokenConverter jwtAccessTokenConverter(){
            JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
            accessTokenConverter.setSigningKey("internet_plus");
            return accessTokenConverter;
        }

        @Bean
        public TokenEnhancer jwtTokenEnhancer(){
                return new JwtTokenEnhancer();
        }
    }

jwtTokenEnhancer方法 返回一個JwtTokenEnhancer并交給bean工廠名船。

2.2 增加JwtTokenEnhancer類

/**
 * Jwt token 擴展
 * Created by Fant.J.
 */
public class JwtTokenEnhancer implements TokenEnhancer {
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {

        Map<String,Object> info = new HashMap<>();
        info.put("provider","Fant.J");
        //設置附加信息
        ((DefaultOAuth2AccessToken)oAuth2AccessToken).setAdditionalInformation(info);
        return oAuth2AccessToken;
    }
}

重寫TokenEnhancer的enhance方法绰上,根據(jù)需求擴展jwt。

2.3 修改MyAuthorizationServerConfig類

/**
 * 認證服務器
 * Created by Fant.J.

 */
@Configuration
@EnableAuthorizationServer
public class MyAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {


    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    @Autowired
    private TokenEnhancer jwtTokenEnhancer;


    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        super.configure(security);
    }

    /**
     * 客戶端配置(給誰發(fā)令牌)
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory().withClient(ConsParams.Auth.GET_CLIENT_ID)
                .secret(ConsParams.Auth.GET_SECRET)
                //有效時間 2小時
                .accessTokenValiditySeconds(ConsParams.Auth.GET_TOKEN_VALIDITY_SECONDS)
                //密碼授權模式和刷新令牌
                .authorizedGrantTypes(ConsParams.Auth.GE_TAUTHORIZED_GRANT_TYPES)
                .scopes(ConsParams.Auth.GE_TSCOPES);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .tokenStore(tokenStore)
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService);
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
            List<TokenEnhancer> enhancerList = new ArrayList<>();
            enhancerList.add(jwtTokenEnhancer);
            enhancerList.add(jwtAccessTokenConverter);
            enhancerChain.setTokenEnhancers(enhancerList);


            endpoints
                    .tokenEnhancer(enhancerChain)
                    .accessTokenConverter(jwtAccessTokenConverter);
    }
}

endpoints的tokenEnhancer方法需要我們提供一個token增強器鏈對象TokenEnhancerChain渠驼,所以我們需要在鏈中加入我們重寫的TokenEnhancer和jwtAccessTokenConverter蜈块,然后放入endpoints。

啟動項目

和上文獲取token方式相同迷扇。

下圖紅框便是我添加的擴展百揭。

解析jwt,獲取擴展信息

如果我們需要獲取jwt本來就有的信息蜓席,我們直接請求方法中吧Authentication當做參數(shù)器一,就可以獲取到jwt原始信息。

如果我們需要獲取jwt擴展的信息厨内,即我們自定義添加的信息祈秕,我們需要做這幾個操作:

  1. pom導入依賴
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>

  1. UserController.java
    @GetMapping("/me")
    public ServerResponse getCurrentUser(Authentication user, HttpServletRequest request) throws UnsupportedEncodingException {

        String s = user.getPrincipal().toString();
        String name = user.getName();
        String header = request.getHeader("Authorization");
        String token = StringUtils.substringAfter(header,"bearer ");
        Claims body = Jwts.parser().setSigningKey(ConsParams.Auth.GET_SIGNING_KEY.getBytes("UTF-8"))
                .parseClaimsJws(token).getBody();

        String username = (String) body.get("username");
        log.info("解析token獲取到的username為{}",username);
        log.info("從Authentication里獲取到的username為{}",s);
        log.info("從Authentication里獲取到的username為{}",name);
        return ServerResponse.createBySuccess(user);
    }

介紹下我的所有文集:

流行框架

SpringCloud
springboot
nginx
redis

底層實現(xiàn)原理:

Java NIO教程
Java reflection 反射詳解
Java并發(fā)學習筆錄
Java Servlet教程
jdbc組件詳解
Java NIO教程
Java語言/版本 研究

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末渺贤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子请毛,更是在濱河造成了極大的恐慌志鞍,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件方仿,死亡現(xiàn)場離奇詭異固棚,居然都是意外死亡,警方通過查閱死者的電腦和手機兼丰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門玻孟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鳍征,你說我怎么就攤上這事黍翎。” “怎么了艳丛?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵匣掸,是天一觀的道長。 經(jīng)常有香客問我氮双,道長碰酝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任戴差,我火速辦了婚禮送爸,結果婚禮上,老公的妹妹穿的比我還像新娘暖释。我一直安慰自己袭厂,他們只是感情好,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布球匕。 她就那樣靜靜地躺著纹磺,像睡著了一般。 火紅的嫁衣襯著肌膚如雪亮曹。 梳的紋絲不亂的頭發(fā)上橄杨,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機與錄音照卦,去河邊找鬼式矫。 笑死,一個胖子當著我的面吹牛役耕,可吹牛的內容都是我干的衷佃。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蹄葱,長吁一口氣:“原來是場噩夢啊……” “哼氏义!你這毒婦竟也來了锄列?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤惯悠,失蹤者是張志新(化名)和其女友劉穎邻邮,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體克婶,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡筒严,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了情萤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸭蛙。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖筋岛,靈堂內的尸體忽然破棺而出娶视,到底是詐尸還是另有隱情,我是刑警寧澤睁宰,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布肪获,位于F島的核電站,受9級特大地震影響柒傻,放射性物質發(fā)生泄漏孝赫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一红符、第九天 我趴在偏房一處隱蔽的房頂上張望青柄。 院中可真熱鬧,春花似錦预侯、人聲如沸刹前。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至祖今,卻和暖如春校坑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背千诬。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工耍目, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人徐绑。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓邪驮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親傲茄。 傳聞我的和親對象是個殘疾皇子毅访,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內容