Spring?Boot?使用?Hutool-jwt?實現(xiàn)?token?驗證功能
更新時間:2023年07月04日 15:42:30 作者:訾博ZiBo
JWT?就是一種網(wǎng)絡(luò)身份認(rèn)證和信息交換格式,這篇文章主要介紹了Spring Boot使用Hutool-jwt實現(xiàn)token驗證,需要的朋友可以參考下
一、JWT 概述
1、簡介
簡單地說,JWT 就是一種網(wǎng)絡(luò)身份認(rèn)證和信息交換格式。
2、結(jié)構(gòu)
- Header 頭部信息,主要聲明了 JWT 的簽名算法等信息;
- Payload 載荷信息,主要承載了各種聲明并傳遞明文數(shù)據(jù);
- Signature 簽名,擁有該部分的 JWT 被稱為 JWS,也就是簽了名的 JWS ,用于校驗數(shù)據(jù)。
# 整體結(jié)構(gòu) header.payload.signature
3、使用
JWT模塊的核心主要是兩個類:
JWT
類用于鏈?zhǔn)缴?、解析或驗證JWT信息。JWTUtil
類主要是JWT的一些工具封裝,提供更加簡潔的JWT生成、解析和驗證工作。
二、基本使用
邏輯較為簡單,下面的代碼作為參考。
0、整體思路
- 寫一個工具類封裝生成、校驗和解析 token 的方法;
- 在注冊和登錄時生成 token ,生成的 token 存入 redis ,下次登錄去 redis 獲取,如果存在則直接返回,反之重新生成,并存入 redis;
- 在攔截器中校驗和解析 token ,拿到 token 中有用的信息存入
private static final ThreadLocal<UserDto> *THREAD_LOCAL* = new ThreadLocal<>();
,以便后續(xù)取用。
1、JWT 工具類
根據(jù)需要調(diào)整 userDto
package com.zibo.common.util; import cn.hutool.jwt.JWT; import com.zibo.modules.user.dto.UserDto; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang3.StringUtils; /** * JWT 工具類 * * @author zibo * @date 2023/7/2 14:36 * @slogan 慢慢學(xué),不要停。 */ public class JWTUtility { /** * 密鑰 */ private static final byte[] KEY = "zibo".getBytes(); /** * 過期時間(秒):7 天 */ public static final long EXPIRE = 7 * 24 * 60 * 60; private JWTUtility() { } /** * 根據(jù) userDto 生成 token * * @param dto 用戶信息 * @return token */ public static String generateTokenForUser(UserDto dto) { Map<String, Object> map = new HashMap<>(); map.put("id", dto.getId()); map.put("nickname", dto.getNickname()); return generateToken(map); } /** * 根據(jù) map 生成 token 默認(rèn):HS265(HmacSHA256)算法 * * @param map 攜帶數(shù)據(jù) * @return token */ public static String generateToken(Map<String, Object> map) { JWT jwt = JWT.create(); // 設(shè)置攜帶數(shù)據(jù) map.forEach(jwt::setPayload); // 設(shè)置密鑰 jwt.setKey(KEY); // 設(shè)置過期時間 jwt.setExpiresAt(new Date(System.currentTimeMillis() + EXPIRE * 1000)); return jwt.sign(); } /** * token 校驗 * @param token token * @return 是否通過校驗 */ public static boolean verify (String token) { if (StringUtils.isBlank(token)) return false; return JWT.of(token).setKey(KEY).verify(); } /** * token 校驗,并獲取 userDto * @param token token * @return userDto */ public static UserDto verifyAndGetUser(String token) { if(!verify(token)) return null; // 解析數(shù)據(jù) JWT jwt = JWT.of(token); Long id = Long.valueOf(jwt.getPayload("id").toString()); String nickname = jwt.getPayload("nickname").toString(); // 返回用戶信息 return new UserDto(id, nickname); } }
2、在攔截器中校驗和解析 token
package com.zibo.common.config; import com.zibo.common.enums.AppCode; import com.zibo.common.pojo.ApiException; import com.zibo.common.pojo.UserHolder; import com.zibo.common.util.JWTUtility; import com.zibo.modules.user.dto.UserDto; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; /** * 自定義攔截器 * @author Administrator */ @Component @Slf4j public class UserInterceptor implements HandlerInterceptor { /** * 進(jìn)入controller方法之前執(zhí)行。如果返回false,則不會執(zhí)行 controller 的方法 * * @param request 請求 * @param response 響應(yīng) * @param handler 處理器 * @return 是否放行 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // 獲取 header 中的 Authorization 信息 String token = request.getHeader("token"); if (StringUtils.isNotBlank(token)) { UserDto dto = JWTUtility.verifyAndGetUser(token); if (dto != null) { UserHolder.setUserInfo(dto); } else { log.error("token 驗證失敗!token is {}, uri is {}", token, request.getRequestURI()); throw new ApiException(AppCode.TOKEN_ERROR, "token 校驗不通過!"); } } else { log.error("token 驗證失??!token is {}, uri is {}", token, request.getRequestURI()); throw new ApiException(AppCode.TOKEN_ERROR, "token 為空!"); } return true; } /** * 響應(yīng)結(jié)束之前 * @param request 請求 * @param response 響應(yīng) * @param handler 處理器 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { // 清理掉當(dāng)前線程中的數(shù)據(jù),防止內(nèi)存泄漏 UserHolder.remove(); } }
3、在注冊和登錄時簽發(fā) token
@PostMapping("/loginOrRegister") public UserDto loginOrRegister(@RequestBody @Validated UserDto dto) { // 通過手機號查詢 UserDto byPhone = service.findByPhone(dto.getPhone()); // 如果操作標(biāo)記為空,則報錯 if (ObjectUtils.isEmpty(dto.getOperation())) { throw new ApiException("操作標(biāo)記不能為空!"); } // 如果是注冊 if (dto.getOperation() == 0) { // 如果用戶已經(jīng)存在,則報錯 if (ObjectUtils.isNotEmpty(byPhone)) { throw new ApiException("注冊失??!賬號已存在!"); } // 創(chuàng)建用戶 Long save = service.save(dto); // 返回用戶信息 UserDto userDto = service.findById(save); // 根據(jù)用戶生成 token String token = JWTUtility.generateTokenForUser(userDto); // 保存到 redis service.saveToken(userDto.getId(), token); // 設(shè)置 token userDto.setToken(token); LogUtility.baseInfoWith("注冊成功!" + userDto); return userDto; } // 登錄 if (ObjectUtils.isEmpty(byPhone)) { log.error("登錄失敗!賬號不存在!" + dto); // 賬號不存在 throw new ApiException("登錄失?。≠~號不存在!com/zibo/controller/user/UserController.java:62"); } else { // 比較密碼是否一致 if (!byPhone.getPassword().equals(dto.getPassword())) { throw new ApiException("登錄失?。≠~號或密碼錯誤!"); } // 更新最后登錄時間 byPhone.setLastLoginTime(LocalDateTime.now()); service.save(byPhone); // 從 redis 獲取 token String token = service.getToken(byPhone.getId()); if (StringUtils.isBlank(token)) { // 根據(jù)用戶生成 token token = JWTUtility.generateTokenForUser(byPhone); log.info("用戶登錄,并生成token,id 為:{}, 昵稱為:{},token 為:{}", byPhone.getId(), byPhone.getNickname(), token); // 保存到 redis service.saveToken(byPhone.getId(), token); } // 設(shè)置 token byPhone.setToken(token); LogUtility.baseInfoWith("登錄成功!" + byPhone); return byPhone; } }
4、用戶信息 UserHolder
package com.zibo.common.pojo; import com.zibo.modules.user.dto.UserDto; /** * 存放用戶信息的容器 * @author Administrator */ public class UserHolder { private static final ThreadLocal<UserDto> THREAD_LOCAL = new ThreadLocal<>(); private UserHolder() { } /** * 獲取線程中的用戶 * @return 用戶信息 */ public static UserDto getUserInfo() { return THREAD_LOCAL.get(); } /** * 設(shè)置當(dāng)前線程中的用戶 * @param info 用戶信息 */ public static void setUserInfo(UserDto info) { THREAD_LOCAL.set(info); } public static Long getUserId() { UserDto dto = THREAD_LOCAL.get(); if (dto != null) { return dto.getId(); } else { // 注冊或登錄時沒有,返回 0 return 0L; } } public static void remove() { THREAD_LOCAL.remove(); } }
到此這篇關(guān)于Spring Boot 使用 Hutool-jwt 實現(xiàn) token 驗證的文章就介紹到這了,更多相關(guān)Spring Boot token 驗證內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot統(tǒng)一返回JSON格式實現(xiàn)方法詳解
這篇文章主要介紹了SpringBoot統(tǒng)一返回JSON格式實現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-02-02Quarkus中實現(xiàn)Resteasy的文件上傳下載操作
這篇文章主要為大家介紹了Quarkus中實現(xiàn)Resteasy的文件上傳下載的操作過程步驟,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02