1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Spring Security OAuth2.0前后端分離下的登錄授權(quán)

        共 727字,需瀏覽 2分鐘

         ·

        2021-02-02 09:24


        點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

        優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

        ? 作者?|? 狂盜一枝梅

        來(lái)源 |? urlify.cn/67zUBr

        76套java從入門到精通實(shí)戰(zhàn)課程分享

        本篇文章將會(huì)解決上一篇文章《Spring Security OAuth2.0認(rèn)證授權(quán)五:用戶信息擴(kuò)展到j(luò)wt 》中遺留的問(wèn)題,并在原有的項(xiàng)目中新增模塊business-server用來(lái)充當(dāng)前端頁(yè)面的web容器并轉(zhuǎn)發(fā)登錄請(qǐng)求和更換token的請(qǐng)求等,以模擬前后端分離下的登錄以及更換token操作。


        一、jwt令牌在網(wǎng)關(guān)處的過(guò)期時(shí)間校驗(yàn)

        上一篇文章中講了在網(wǎng)關(guān)處解析token并轉(zhuǎn)發(fā)到目標(biāo)服務(wù)的操作,因?yàn)槭褂昧薺wt令牌的原因,所以省了一步到認(rèn)證服務(wù)器認(rèn)證的操作,只要驗(yàn)簽成功,就認(rèn)為令牌有效。這實(shí)際上留下了一個(gè)bug:服務(wù)端無(wú)法主動(dòng)取消jwt令牌,所以這個(gè)令牌只要客戶端保存下來(lái),如果不調(diào)用認(rèn)證服務(wù)器的令牌驗(yàn)證接口,這個(gè)jwt令牌將永遠(yuǎn)有效。因此需要在網(wǎng)關(guān)處加上對(duì)過(guò)期時(shí)間的校驗(yàn)。

        在TokenFilter中添加以下代碼邏輯

        //取出exp字段,判斷token是否已經(jīng)過(guò)期
        try?{
        ????Map?map?=?objectMapper.readValue(payLoad,?new?TypeReference>()?{
        ????});
        ????long?expiration?=?((Integer)?map.get("exp"))?*?1000L;
        ????if?(expiration?????????return?unAuthorized(exchange,?"未認(rèn)證的請(qǐng)求:token存在,但是已經(jīng)失效",WrapperResult.TOKEN_EXPIRE);
        ????}
        }?catch?(IOException?e)?{
        ????log.error("",?e);
        ????return?unAuthorized(exchange,?"未認(rèn)證的請(qǐng)求:錯(cuò)誤的token",null);
        }


        二、refresh-token接口缺少用戶信息

        refresh-token在access_token過(guò)期,但是refresh-token未過(guò)期的時(shí)候使用,目的是使用refresh_token更新已經(jīng)過(guò)期的access_token,這樣理論上來(lái)說(shuō),客戶端只要能在refresh_token過(guò)期之前進(jìn)行任意操作,就可以避免重新登錄了。

        上一篇文章中將用戶信息放到了jwt token中并返回給客戶端,但是如果使用refresh_token更新token,后端會(huì)報(bào)錯(cuò),前端取到的token中則缺少了用戶信息。究其原因,和JwtAccessTokenConverter有關(guān)系,關(guān)于這個(gè)類的實(shí)例,當(dāng)初創(chuàng)建的方法如下

        @Bean
        public?JwtAccessTokenConverter?accessTokenConverter(){
        ????JwtAccessTokenConverter?jwtAccessTokenConverter?=?new?JwtAccessTokenConverter();
        ????jwtAccessTokenConverter.setSigningKey(SIGNING_KEY);//對(duì)稱秘鑰,資源服務(wù)器使用該秘鑰來(lái)驗(yàn)證
        ????return?jwtAccessTokenConverter;
        }

        這里的new操作省了很多默認(rèn)參數(shù)的指定,且先看下為啥會(huì)缺少用戶信息,擴(kuò)展用戶信息的關(guān)鍵在于方法com.kdyzm.spring.security.auth.center.service.MyUserDetailsServiceImpl#loadUserByUsername,這里擴(kuò)展了用戶信息,使其從單純的username字符串變成了UserDetailsExpand對(duì)象,然后在增強(qiáng)方法com.kdyzm.spring.security.auth.center.enhancer.CustomTokenEnhancer#enhance中將擴(kuò)展信息取出來(lái)放到Token中。

        經(jīng)過(guò)debug,發(fā)現(xiàn)

        最終發(fā)現(xiàn)是如下代碼的問(wèn)題org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter#extractAuthentication

        public?Authentication?extractAuthentication(Map?map)?{
        ????if?(map.containsKey(USERNAME))?{
        ????????Object?principal?=?map.get(USERNAME);
        ????????Collection?authorities?=?getAuthorities(map);
        ????????//運(yùn)行到這里的時(shí)候userDetailsService為空,所以并沒(méi)有執(zhí)行自定義的loadUserByUsername方法
        ????????if?(userDetailsService?!=?null)?{
        ????????????UserDetails?user?=?userDetailsService.loadUserByUsername((String)?map.get(USERNAME));
        ????????????authorities?=?user.getAuthorities();
        ????????????principal?=?user;
        ????????}
        ????????return?new?UsernamePasswordAuthenticationToken(principal,?"N/A",?authorities);
        ????}
        ????return?null;
        }

        層層網(wǎng)上追尋調(diào)用鏈,竟然是JwtAccessTokenConverter創(chuàng)建的時(shí)候省略參數(shù)導(dǎo)致的,只需要如此做就可以解決問(wèn)題了

        @Bean
        public?JwtAccessTokenConverter?accessTokenConverter(){
        ????JwtAccessTokenConverter?jwtAccessTokenConverter?=?new?JwtAccessTokenConverter();
        ????DefaultAccessTokenConverter?tokenConverter?=?new?DefaultAccessTokenConverter();
        ????DefaultUserAuthenticationConverter?userTokenConverter?=?new?DefaultUserAuthenticationConverter();
        ????userTokenConverter.setUserDetailsService(userDetailsService);
        ????tokenConverter.setUserTokenConverter(userTokenConverter);
        ????jwtAccessTokenConverter.setAccessTokenConverter(tokenConverter);
        ????jwtAccessTokenConverter.setSigningKey(SIGNING_KEY);//對(duì)稱秘鑰,資源服務(wù)器使用該秘鑰來(lái)驗(yàn)證
        ????return?jwtAccessTokenConverter;
        }

        JwtAccessTokenConverter對(duì)象創(chuàng)建的時(shí)候指定DefaultUserAuthenticationConverter使用的userDetailsService即可。


        三、新建business-server模塊作為web容器

        這里新建的business-server模塊有兩個(gè)功能

        1. 充當(dāng)web容器,該服務(wù)并沒(méi)有使用模板化技術(shù),使用的是純html、css實(shí)現(xiàn)前端

        2. 轉(zhuǎn)發(fā)前端登錄、更換token請(qǐng)求

        可能會(huì)有人對(duì)第二條有疑問(wèn),為什么要這么做?之前測(cè)試的時(shí)候基本上都是使用postman發(fā)起的請(qǐng)求,請(qǐng)求的方式是這樣的http://127.0.0.1:30000/oauth/token?client_id=c1&client_secret=secret&grant_type=password&username=zhangsan&password=123可以看到這里傳遞了很重要的參數(shù)client_idclient_secret,這兩個(gè)參數(shù)無(wú)論如何也不應(yīng)當(dāng)泄露給前端,通常都是中間的真正的客戶端服務(wù)拼接這兩個(gè)參數(shù)再將請(qǐng)求轉(zhuǎn)發(fā)給認(rèn)證服務(wù)


        四、前后端分離

        設(shè)計(jì)上想要實(shí)現(xiàn)以下功能

        1. 首頁(yè)未登錄則提示用戶登錄,已經(jīng)登錄則展示用戶個(gè)人信息

        2. 用戶登錄之后將令牌保存到localStorage

        3. token過(guò)期之后用戶可以選擇使用refresh_token更換已經(jīng)過(guò)期的令牌(access_token)

        4. 已經(jīng)過(guò)期的refresh_token不能用于更換新的令牌


        1、關(guān)閉認(rèn)證服務(wù)表單登錄

        以前請(qǐng)求認(rèn)證服務(wù)的任意接口,如果沒(méi)有認(rèn)證,則都會(huì)跳轉(zhuǎn)到系統(tǒng)自帶的登錄頁(yè)面,現(xiàn)在我們想要實(shí)現(xiàn)前后端分離了,原來(lái)系統(tǒng)自帶的登錄頁(yè)面就有些礙眼了,直接關(guān)閉就好。關(guān)閉方法如下,spring security的配置更改為如下:

        ????????????????.formLogin()
        ????????????????.disable();

        2、前后端代碼

        前端代碼在business-server/src/main/resources/static目錄下,只有兩個(gè)頁(yè)面,一個(gè)首頁(yè),一個(gè)登陸頁(yè)面

        后端只有兩個(gè)接口

        • 登錄接口:com.kdyzm.spring.security.oauth.study.business.server.controller.LoginController#login

        • 更新token接口:com.kdyzm.spring.security.oauth.study.business.server.controller.TokenController#refreshToken

        其它不做贅述,不過(guò)前端頁(yè)面寫起來(lái)挺麻煩的。。難是不難的


        五、測(cè)試

        源代碼:

        測(cè)試前首先需要重新執(zhí)行初始化sql(auth-server/docs/sql/init.sql),然后依次啟動(dòng)register-server、gateway-server、auth-server、resource-server、business-server?五個(gè)服務(wù)

        啟動(dòng)成功后打開(kāi)瀏覽器,輸入http://127.0.0.1:30002/地址,就會(huì)看到以下頁(yè)面


        點(diǎn)擊登錄之后,出現(xiàn)登錄框


        輸入賬號(hào)密碼之后,登錄成功之后會(huì)跳轉(zhuǎn)首頁(yè),就會(huì)看到個(gè)人信息


        這里設(shè)置的token有效期為10秒,所以很快token就會(huì)失效,十秒鐘之后刷新頁(yè)面就會(huì)有新的提示



        接下來(lái)可以有兩種選擇,一種是使用refresh-token更新失效的令牌,另外一種是重新登錄,這里refresh_token的有效期也很短,只有30秒,如果超出30秒,則會(huì)更新失敗,提示如下

        而如果在30秒內(nèi)刷新令牌,則會(huì)重新獲取到令牌并刷新當(dāng)前頁(yè)


        六、源代碼地址

        源代碼地址:https://gitee.com/kdyzm/spring-security-oauth-study/tree/v7.0.0





        粉絲福利:Java從入門到入土學(xué)習(xí)路線圖

        ??????

        ??長(zhǎng)按上方微信二維碼?2 秒


        感謝點(diǎn)贊支持下哈?

        瀏覽 111
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            亚洲性爱大片 | 成人做爰A片AAA毛真人 | 伊人影视大香蕉 | 成人免费无码婬片在线 | 小早川怜子家庭医生手机观看 | 国模yumi私拍大尺度写真 | 护士丝袜脚交footjob 女人被狂躁到高潮嗷嗷叫 噜噜噜av | 99久久婷婷国产综合精品青牛牛 | 伊人性爱 | 女上男下动态视频 |