<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 727字,需瀏覽 2分鐘

           ·

          2021-02-02 09:24


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

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

          ? 作者?|? 狂盜一枝梅

          來源 |? urlify.cn/67zUBr

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

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


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

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

          在TokenFilter中添加以下代碼邏輯

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


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

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

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

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

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

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

          最終發(fā)現(xià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);
          ????????//運行到這里的時候userDetailsService為空,所以并沒有執(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ù)導(dǎo)致的,只需要如此做就可以解決問題了

          @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);//對稱秘鑰,資源服務(wù)器使用該秘鑰來驗證
          ????return?jwtAccessTokenConverter;
          }

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


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

          這里新建的business-server模塊有兩個功能

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

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

          可能會有人對第二條有疑問,為什么要這么做?之前測試的時候基本上都是使用postman發(fā)起的請求,請求的方式是這樣的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,這兩個參數(shù)無論如何也不應(yīng)當(dāng)泄露給前端,通常都是中間的真正的客戶端服務(wù)拼接這兩個參數(shù)再將請求轉(zhuǎn)發(fā)給認(rèn)證服務(wù)


          四、前后端分離

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

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

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

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

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


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

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

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

          2、前后端代碼

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

          后端只有兩個接口

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

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

          其它不做贅述,不過前端頁面寫起來挺麻煩的。。難是不難的


          五、測試

          源代碼:

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

          啟動成功后打開瀏覽器,輸入http://127.0.0.1:30002/地址,就會看到以下頁面


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


          輸入賬號密碼之后,登錄成功之后會跳轉(zhuǎn)首頁,就會看到個人信息


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



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

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


          六、源代碼地址

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





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

          ??????

          ??長按上方微信二維碼?2 秒


          感謝點贊支持下哈?

          瀏覽 111
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  肉搏丝袜一区 | 少妇后入在线观看 | 91国产大片 | 日韩熟女av | 国产日韩欧美操 |