最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

springboot集成JWT之雙重token的實(shí)現(xiàn)

 更新時(shí)間:2025年03月12日 10:34:57   作者:編夢(mèng)小匠  
本文主要介紹了springboot集成JWT之雙重token的實(shí)現(xiàn),前端使用accessToken進(jìn)行登錄和驗(yàn)證,后端使用refreshToken定期更新accessToken,具有一定的參考價(jià)值,感興趣的可以了解一下

一,單個(gè)token缺點(diǎn)

token一般存儲(chǔ)在瀏覽器中,容易被盜取,而為了防止被盜取長(zhǎng)期使用,token的有效時(shí)間必然無(wú)法不能設(shè)置太長(zhǎng),此舉也必然引起用戶的頻繁登錄,給用戶帶來(lái)不好的體驗(yàn)

二,雙重token(accessToken,refreshToken)

(一)設(shè)計(jì)思路

1,將accessToken作為是否登錄的標(biāo)識(shí),存儲(chǔ)在瀏覽器當(dāng)中。由于該token可瀏覽器看到,容易被盜取,可將有效時(shí)間設(shè)置的盡可能短一些,解決盜取長(zhǎng)期使用問(wèn)題
2,將refreshToken作為是否更新accessToken的標(biāo)識(shí),只要refreshToken不過(guò)期,則自動(dòng)更新accessToken,可將有效時(shí)間設(shè)置的長(zhǎng)些,解決頻繁登錄問(wèn)題
3,不直接將refreshToken存儲(chǔ)到瀏覽器上,而是將其存儲(chǔ)到accessToken的載荷里,后端通過(guò)獲取accessToken的載荷內(nèi)容獲取到refreshToken,防止refreshToken被盜取
4,accessToken構(gòu)成:
         載荷:refreshToken
         有效時(shí)間:盡可能短(這里我設(shè)置為30分鐘)
         密鑰:用戶的密碼
     refreshToken構(gòu)成:
         載荷:用戶的id
         有效時(shí)間:長(zhǎng)(這里我設(shè)置為一天)
         密鑰:用戶的密碼

(二)后端代碼

1,TokenUtil(生成token和獲取用戶信息)

具體步驟看注釋

@Component
@Slf4j
public class TokenUtils {
    private static IUserService staticAdminService;

    @Resource
    private IUserService adminService;

    @PostConstruct
    public void setUserService() {
        staticAdminService = adminService;
    }
    
	//	生成token
    public static String genToken(String adminId, String sign, Integer time) {
    	//生成過(guò)期時(shí)間
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND, time*60);

        return JWT.create().withAudience(adminId) //載荷
                .withExpiresAt(instance.getTime()) //time分鐘后過(guò)期
                .sign(Algorithm.HMAC256(sign)); // 密鑰
    }
    
    //根據(jù)accessToken獲取用戶信息
    //  1.通過(guò)accessToken的載荷拿到refreshToken
    //  2.通過(guò)refreshToken的載荷拿到userId
    //  3.調(diào)用根據(jù)用戶id獲取用戶信息的方法拿到用戶信息
    public static User getCurrentAdmin() {
        String accessToken = null;
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            accessToken = request.getHeader("token");
            System.out.println("access"+accessToken);
            if (StrUtil.isBlank(accessToken)) {
                log.error("獲取當(dāng)前登錄的accessToken失敗, token: {}", accessToken);
                return null;
            }
            String refreshToken = JWT.decode(accessToken).getAudience().get(0);
            if (StrUtil.isBlank(refreshToken)) {
                log.error("獲取當(dāng)前登錄的refreshToken失敗, token: {}", refreshToken);
                return null;
            }
            String userId = JWT.decode(refreshToken).getAudience().get(0);
            return staticAdminService.getById(Integer.valueOf(userId));
        } catch (Exception e) {
            log.error("獲取當(dāng)前登錄的管理員信息失敗, token={}", accessToken,  e);
            return null;
        }
    }
}

2,JwtInterceptor(檢驗(yàn)accessToken是否合法)

實(shí)現(xiàn)思路:
(1)檢驗(yàn)accessToken是否為空
(2)通過(guò)accessToken的載荷內(nèi)容拿到refreshToken
(3)驗(yàn)證獲取到的refreshToken是否合法,若不合法,則accessToken為無(wú)效token,合法則返回密鑰
(4)通過(guò)該密鑰去判斷accessToken是否過(guò)期

@Slf4j
public class JwtInterceptor implements HandlerInterceptor {

    @Autowired
    private IUserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String accessToken = request.getHeader("token");
        if (StrUtil.isBlank(accessToken)) {
            accessToken = request.getParameter("token");
        }
        //執(zhí)行認(rèn)證
        if (StrUtil.isBlank(accessToken)) {
            throw new ServiceException(ErrorCode.TOKEN_NO_EXIST.getCode(), ErrorCode.TOKEN_NO_EXIST.getMsg());
        }
        String refreshToken;
        try{
            refreshToken = JWT.decode(accessToken).getAudience().get(0);
        }catch (Exception e){
            throw new ServiceException(ErrorCode.TOKEN_ERROR.getCode(), ErrorCode.TOKEN_ERROR.getMsg());
        }
        String password = verifyRefreshToken(refreshToken);
        try {
            // 用戶密碼加簽驗(yàn)證 token
            JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(password)).build();
            jwtVerifier.verify(accessToken); // 驗(yàn)證token
        } catch (JWTVerificationException e) {
            throw new ServiceException(ErrorCode.TOKEN_ERROR.getCode(), ErrorCode.TOKEN_ERROR.getMsg());
        }
        return true;
    }
	
	//驗(yàn)證refreshToken是否合法
    public String verifyRefreshToken(String token){
        // 獲取 token 中的adminId
        String adminId;
        User user;
        try {
            adminId = JWT.decode(token).getAudience().get(0);
            // 根據(jù)token中的userid查詢數(shù)據(jù)庫(kù)
            user = userService.getById(Integer.parseInt(adminId));
            System.out.println(user);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(ErrorCode.REFRESE_TOKEN_ERROR.getCode(), ErrorCode.REFRESE_TOKEN_ERROR.getMsg());
        }
        if (user == null) {
            throw new ServiceException(ErrorCode.USER_NO_EXIST.getCode(), ErrorCode.USER_NO_EXIST.getMsg());
        }

        try {
            // 用戶密碼加簽驗(yàn)證 token
            JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
            jwtVerifier.verify(token); // 驗(yàn)證token
        } catch (JWTVerificationException e) {
            throw new ServiceException(ErrorCode.REFRESE_TOKEN_ERROR.getCode(), ErrorCode.REFRESE_TOKEN_ERROR.getMsg());
        }
        return user.getPassword();
    }
}

3, WebConfig(設(shè)置攔截規(guī)則)

@Configuration
public class WebConfig implements  WebMvcConfigurer {
    // 加自定義攔截器JwtInterceptor,設(shè)置攔截規(guī)則
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor()).addPathPatterns("/**").excludePathPatterns("/user/login","/user/register","/refreshToken/refresh");
    }
    @Bean
    public JwtInterceptor jwtInterceptor(){
        return new JwtInterceptor();
    }
}

4,登陸接口處生成accessToken和refreshToken

String refreshToken= TokenUtils.genToken(user.getId().toString(),user.getPassword(),24*60);
String accessToken=TokenUtils.genToken(refreshToken,user.getPassword(),30);

5,編寫(xiě)更新refreshToken的接口

實(shí)現(xiàn)思路:
(1)檢驗(yàn)accessToken是否為空
(2)通過(guò)accessToken的載荷內(nèi)容拿到refreshToken
(3)驗(yàn)證獲取到的refreshToken是否合法,不合法,則accessToken為無(wú)效token(此處無(wú)效token單指自己編寫(xiě),不是后端生成的token),不對(duì)accessToken進(jìn)行更新操作,合法則重新生成新的accessToken

@RestController
@RequestMapping("/refreshToken")
public class refreshTokenController {
    @Autowired
    private IUserService userService;

    @GetMapping("/refresh")
    public Result refresh(@RequestParam String accessToken) {
        if (StrUtil.isBlank(accessToken)) {
            throw new ServiceException(ErrorCode.TOKEN_NO_EXIST.getCode(), ErrorCode.TOKEN_NO_EXIST.getMsg());
        }
        String refreshToken;
        try{
            refreshToken = JWT.decode(accessToken).getAudience().get(0);
        }catch (Exception e){
            throw new ServiceException(ErrorCode.TOKEN_ERROR.getCode(), ErrorCode.TOKEN_ERROR.getMsg());
        }
        if(StrUtil.isBlank(refreshToken)){
            return Result.error(ErrorCode.REFRESH_TOKEN_NULL.getCode(), ErrorCode.REFRESH_TOKEN_NULL.getMsg());
        }
        // 獲取 token 中的adminId
        String adminId;
        User user;
        try {
            adminId = JWT.decode(refreshToken).getAudience().get(0);
            // 根據(jù)token中的userid查詢數(shù)據(jù)庫(kù)
            user = userService.getById(Integer.parseInt(adminId));
            System.out.println(user);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(ErrorCode.REFRESE_TOKEN_ERROR.getCode(), ErrorCode.REFRESE_TOKEN_ERROR.getMsg());
        }
        if (user == null) {
            throw new ServiceException(ErrorCode.USER_NO_EXIST.getCode(), ErrorCode.USER_NO_EXIST.getMsg());
        }

        try {
            // 用戶密碼加簽驗(yàn)證 token
            JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
            jwtVerifier.verify(refreshToken); // 驗(yàn)證token
        } catch (JWTVerificationException e) {
            throw new ServiceException(ErrorCode.REFRESE_TOKEN_ERROR.getCode(), ErrorCode.REFRESE_TOKEN_ERROR.getMsg());
        }
        String currentAccessToken= TokenUtils.genToken(refreshToken,user.getPassword(),30);
        return Result.success(currentAccessToken);
    }
}

(三)前端代碼

1,登錄成功后存儲(chǔ)accessToken

存儲(chǔ)方式不限,在這我是存儲(chǔ)在localStorage里,并在vuex里共享該數(shù)據(jù)

//storage.js
const TOKEN_KEY = 'tk'
export const getInfo = () => {
  const token = localStorage.getItem(TOKEN_KEY)
  return token || ''
}
export const setInfo = (token) => {
  localStorage.setItem(TOKEN_KEY, token)
}
export const removeInfo = () => {
  localStorage.removeItem(TOKEN_KEY)
}

//token模塊
import { getInfo, setInfo ,removeInfo} from '@/utils/storage'
export default {
  namespaced: true,
  state () {
    return {
      tokenInfo: getInfo()
    }
  },
  mutations: {
    setTokenInfo (state, info) {
      state.tokenInfo = info
      setInfo(state.tokenInfo)
    },
    removeTokenInfo () {
      removeInfo()
    }
  },
  actions: {}
}
store.commit('token/setTokenInfo', data)

2,axios請(qǐng)求攔截器處設(shè)置請(qǐng)求頭

instance.interceptors.request.use(function (config) {
  const accessToken = store.state.token.tokenInfo
  if (accessToken) {
    config.headers.token = accessToken
  }
  return config
}, function (error) {
  // 對(duì)請(qǐng)求錯(cuò)誤做些什么
  return Promise.reject(error)
})

3,axios響應(yīng)攔截器配置

設(shè)置accessToken過(guò)期則判斷refreshToken是否過(guò)期,如果未過(guò)期,重新生成accessToken,并重新發(fā)送請(qǐng)求

instance.interceptors.response.use(async function (response) {
  if (response.data.code === 200) {
    return response.data
  } else if (response.data.code === 1008 || response.data.code === 1009 || response.data.code === 1010) {
  	//accessToken過(guò)期
    const { data } = await refreshToken(store.state.token.tokenInfo)
    store.commit('token/setTokenInfo', data)
    return instance(response.config)
  } else if (response.data.code === 1014) {
  	//refreshToken過(guò)期
    window.location.href = '/login'
  } else {
    Vue.prototype.$message.error(response.data.msg)
    return Promise.reject(response.data.msg)
  }
}, function (error) {
  // 對(duì)響應(yīng)錯(cuò)誤做點(diǎn)什么
  return Promise.reject(error)
})

4,守衛(wèi)路由配置

判斷是否有accessToken,是否在登錄注冊(cè)頁(yè)面,若都無(wú)則跳轉(zhuǎn)到登錄頁(yè),重新登錄

router.beforeEach(async (to, from, next) => {
  const token = store.state.token.tokenInfo
  if (
    // 檢查用戶是否已登錄
    !token &&
    // ?? 避免無(wú)限重定向
    to.path !== '/login' && to.path !== '/register'
  ) {
    // 將用戶重定向到登錄頁(yè)面
    next({ path: '/login' })
  } else {
    next()
  }
})

到此這篇關(guān)于springboot集成JWT之雙重token的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)springboot 雙重token內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

  • hadoop是什么語(yǔ)言

    hadoop是什么語(yǔ)言

    Hadoop是一個(gè)由Apache基金會(huì)所開(kāi)發(fā)的分布式系統(tǒng)基礎(chǔ)架構(gòu)。 用戶可以在不了解分布式底層細(xì)節(jié)的情況下,開(kāi)發(fā)分布式程序。充分利用集群的威力進(jìn)行高速運(yùn)算和存儲(chǔ)
    2017-09-09
  • Java中json使用方法_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Java中json使用方法_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    JSON(JavaScript Object Notation) 是一種輕量級(jí)的數(shù)據(jù)交換格式, json是個(gè)非常重要的數(shù)據(jù)結(jié)構(gòu),在web開(kāi)發(fā)中應(yīng)用十分廣泛。下面通過(guò)本文給大家講解Java中json使用方法,感興趣的朋友一起看看吧
    2017-07-07
  • 詳解shrio的認(rèn)證(登錄)過(guò)程

    詳解shrio的認(rèn)證(登錄)過(guò)程

    這篇文章主要介紹了shrio的認(rèn)證(登錄)過(guò)程,幫助大家更好的理解和使用shrio框架,感興趣的朋友可以了解下
    2021-02-02
  • Java中的@PostConstruct注解的使用

    Java中的@PostConstruct注解的使用

    本文主要介紹了Java中的@PostConstruct注解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • Mybatis 如何批量刪除數(shù)據(jù)的實(shí)現(xiàn)示例

    Mybatis 如何批量刪除數(shù)據(jù)的實(shí)現(xiàn)示例

    這篇文章主要介紹了Mybatis 如何批量刪除數(shù)據(jù)的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • mybatis中${}和#{}取值的區(qū)別分析

    mybatis中${}和#{}取值的區(qū)別分析

    mybatis中使用sqlMap進(jìn)行sql查詢時(shí),經(jīng)常需要?jiǎng)討B(tài)傳遞參數(shù),在動(dòng)態(tài)SQL解析階段, #{ } 和 ${ } 會(huì)有不同的表現(xiàn),這篇文章主要給大家介紹了關(guān)于mybatis中${}和#{}取值區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2021-09-09
  • Java對(duì)象和Json文本轉(zhuǎn)換工具類的實(shí)現(xiàn)

    Java對(duì)象和Json文本轉(zhuǎn)換工具類的實(shí)現(xiàn)

    Json?是一個(gè)用于Java對(duì)象和Json文本相互轉(zhuǎn)換的工具類,本文主要介紹了Java對(duì)象和Json文本轉(zhuǎn)換工具類,具有一定的參考價(jià)值,感興趣的可以了解一下
    2022-03-03
  • Spring底層核心源碼原理解析

    Spring底層核心源碼原理解析

    這篇文章主要介紹了Spring底層核心源碼原理解析,當(dāng)在某個(gè)方法上加了@Transactional注解后,就表示該方法在調(diào)用時(shí)會(huì)開(kāi)啟Spring事務(wù),而這個(gè)方法所在的類所對(duì)應(yīng)的Bean對(duì)象會(huì)是該類的代理對(duì)象,需要的朋友可以參考下
    2023-09-09
  • Java中使用instanceof判斷對(duì)象類型的示例

    Java中使用instanceof判斷對(duì)象類型的示例

    在List<Object>中遍歷Object時(shí),先判斷類型,再定向轉(zhuǎn)換,本文給大家介紹Java中使用instanceof判斷對(duì)象類型,感興趣的朋友跟隨小編一起看看吧
    2023-08-08
  • springboot自定義starter方法及注解實(shí)例

    springboot自定義starter方法及注解實(shí)例

    這篇文章主要為大家介紹了springboot自定義starter方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08

最新評(píng)論

共和县| 西畴县| 肥西县| 贺兰县| 吉安市| 共和县| 明星| 柘荣县| 丽江市| 瑞安市| 红河县| 若尔盖县| 崇仁县| 青岛市| 凤山县| 融水| 农安县| 长宁区| 鄂尔多斯市| 读书| 桂阳县| 南阳市| 扬州市| 汤原县| 阜宁县| 普安县| 孝感市| 岱山县| 萨嘎县| 慈利县| 温州市| 贵阳市| 柏乡县| 崇州市| 阳西县| 桐庐县| 若羌县| 安宁市| 泰宁县| 临潭县| 化州市|