Java實(shí)現(xiàn)JWT登錄認(rèn)證的示例代碼
什么是JWT?
JWT(Json Web Token),簡(jiǎn)單來說就是:web領(lǐng)域中基于json格式的令牌。是最常用的令牌規(guī)范。
- 第一部分:Header(頭),指定了令牌的簽名算法、令牌類型。
- 第二部分:Payload(有效載荷),使用Base64來編碼的,不是加密算法,能夠解碼。因此,該部分不適合存放用戶的私密信息(如:密碼)。
- 第三部分:Signature(簽名),將第一部分和第二部分通過密鑰加密得到。
解析Token可以根據(jù)第三部分解密得到前兩部分的信息,再比對(duì)前端傳來的用戶信息,完成校驗(yàn)。
Token驗(yàn)證失敗的情況?
token過期
密鑰不正確
篡改了頭部、載荷等
為什么需要令牌?
- 承載了一定的數(shù)據(jù)信息,減少數(shù)據(jù)庫訪問次數(shù)
- 具有一定的防偽功能
如何實(shí)現(xiàn)?
該部分代碼不需要硬記,理解后直接使用即可。
添加依賴:
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
JwtUtils.java(生成、解析Token的工具類)
public class JwtUtil { /** * 生成Token * @param secretKey * @param ttlMillis * @param claims * @return */ public static String createToken(String secretKey, long ttlMillis, Map<String, Object> claims) { // 指定簽名的時(shí)候使用的簽名算法,也就是header那部分 SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; // 生成token的過期時(shí)間 long expMillis = System.currentTimeMillis() + ttlMillis; Date exp = new Date(expMillis); // 設(shè)置jwt的body JwtBuilder builder = Jwts.builder() // 如果有私有聲明,一定要先設(shè)置這個(gè)自己創(chuàng)建的私有的聲明,這個(gè)是給builder的claim賦值,一旦寫在標(biāo)準(zhǔn)的聲明賦值之后,就是覆蓋了那些標(biāo)準(zhǔn)的聲明的 .setClaims(claims) // 設(shè)置簽名使用的簽名算法和簽名使用的秘鑰 .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8)) // 設(shè)置過期時(shí)間 .setExpiration(exp); return builder.compact(); } /** * 解析token * @param secretKey * @param token * @return */ public static Claims parseToken(String secretKey, String token) { // 得到DefaultJwtParser Claims claims = Jwts.parser() // 設(shè)置簽名的秘鑰 .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8)) // 設(shè)置需要解析的jwt .parseClaimsJws(token).getBody(); return claims; } }
jwt配置:
application.yaml:
user: jwt: # 設(shè)置jwt簽名加密時(shí)使用的秘鑰 user-secret-key: xxx # 設(shè)置jwt過期時(shí)間 user-ttl: 7200000 # 設(shè)置前端傳遞過來的令牌名稱 user-token-name: token
JwtProperties:
@Component @ConfigurationProperties(prefix = "user.jwt") @Data public class JwtProperties { private String userSecretKey; private long userTtl; private String userTokenName; }
JwtClaimsConstant(管理常量):
public class JwtClaimsConstant { public static final String USER_ID = "userId"; }
jwt攔截器:
@Component @Slf4j public class JwtInterceptor implements HandlerInterceptor { @Resource private JwtProperties jwtProperties; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //1. 獲取用戶信息 String token = request.getHeader(jwtProperties.getUserTokenName()); //2. 判斷用戶信息是否有效,存入ThreadLocal try { Claims claims = JwtUtil.parseToken(jwtProperties.getUserSecretKey(), token); String userInfo = claims.get(JwtClaimsConstant.USER_ID).toString(); if (StrUtil.isNotBlank(userInfo)){ UserContext.setUser(Long.valueOf(userInfo)); } //3. 放行 return true; }catch (Exception e){ //4. 不通過 response.setStatus(401); return false; } } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { //清除用戶信息 UserContext.removeUser(); } }
WebMvc配置(添加jwt攔截器)
@Configuration public class WebConfig implements WebMvcConfigurer { @Resource private JwtInterceptor jwtInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtInterceptor) .addPathPatterns("/user/**") .excludePathPatterns("/user/user/login"); } }
UserContext(存儲(chǔ)用戶登錄信息,方便其他業(yè)務(wù)需求獲取)
public class UserContext { private static final ThreadLocal<Long> threadLocal = new ThreadLocal<>(); /** * 獲取用戶信息 * @return */ public static Long getUser() { return threadLocal.get(); } /** * 設(shè)置用戶信息 * @param userId */ public static void setUser(Long userId){ threadLocal.set(userId); } /** * 移除用戶信息 */ public static void removeUser(){ threadLocal.remove(); } }
登錄業(yè)務(wù)邏輯:
controller層:
@RestController @RequestMapping("/user/user") @Slf4j public class UserController { @Resource private UserService userService; @PostMapping("/login") public UserLoginVO login(@RequestBody UserLoginDTO userLoginDTO){ log.info("用戶登錄:{}",userLoginDTO); return userService.login(userLoginDTO); } }
service層:
public interface UserService { UserLoginVO login(UserLoginDTO userLoginDTO); }
@Service @Slf4j public class UserServiceImpl implements UserService { @Resource private UserMapper userMapper; @Resource private JwtProperties jwtProperties; @Override public UserLoginVO login(UserLoginDTO userLoginDTO) { String username = userLoginDTO.getUsername(); String password = userLoginDTO.getPassword(); //1. 校驗(yàn)用戶名和密碼 UserLogin userLogin = userMapper.getUserByName(username); if (userLogin == null){ throw new BaseException("用戶名或密碼錯(cuò)誤"); } password = DigestUtils.md5DigestAsHex(password.getBytes()); if (!password.equals(userLogin.getPassword())){ throw new BaseException("用戶名或密碼錯(cuò)誤"); } //2. 生成token HashMap<String, Object> claims = new HashMap<>(); claims.put(JwtClaimsConstant.USER_ID, userLogin.getId()); String token = JwtUtil.createToken( jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(), claims); //3. 封裝vo UserLoginVO loginVO = UserLoginVO.builder() .token(token) .userId(userLogin.getId()) .username(userLogin.getUsername()) .build(); return loginVO; } }
mapper層:
@Mapper public interface UserMapper { UserLogin getUserByName(String username); }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.sky.user.mapper.UserMapper"> <select id="getUserByName" resultType="com.sky.user.pojo.entity.UserLogin"> select * from user_login where username = #{username} and is_delete = 0; </select> </mapper>
其他關(guān)聯(lián)代碼:
@Data @AllArgsConstructor @NoArgsConstructor public class UserLoginDTO { /** * 用戶名 */ private String username; /** * 密碼 */ private String password; }
@Data @Builder @NoArgsConstructor @AllArgsConstructor public class UserLoginVO { private String token; private Long userId; private String username; }
測(cè)試:
登錄接口:
其他接口:
到此這篇關(guān)于Java實(shí)現(xiàn)JWT登錄認(rèn)證的示例代碼的文章就介紹到這了,更多相關(guān)Java JWT登錄認(rèn)證內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring 定時(shí)任務(wù)@Scheduled 注解四大參數(shù)用法解析
本文詳細(xì)介紹了Spring框架中使用@Scheduled注解實(shí)現(xiàn)定時(shí)任務(wù)的方法,重點(diǎn)講解了fixedRate、fixedDelay、cron和initialDelay這四個(gè)參數(shù)的用法,并通過實(shí)例代碼進(jìn)行了詳細(xì)說明,感興趣的朋友一起看看吧2025-01-01Java+ElasticSearch+Pytorch實(shí)現(xiàn)以圖搜圖功能
這篇文章主要為大家詳細(xì)介紹了Java如何利用ElasticSearch和Pytorch實(shí)現(xiàn)以圖搜圖功能,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解一下2023-06-06SpringBoot單點(diǎn)登錄實(shí)現(xiàn)過程詳細(xì)分析
這篇文章主要介紹了SpringBoot單點(diǎn)登錄實(shí)現(xiàn)過程,單點(diǎn)登錄英文全稱Single?Sign?On,簡(jiǎn)稱就是SSO。它的解釋是:在多個(gè)應(yīng)用系統(tǒng)中,只需要登錄一次,就可以訪問其他相互信任的應(yīng)用系統(tǒng)2022-12-12Java中線程狀態(tài)+線程安全問題+synchronized的用法詳解
這篇文章主要介紹了Java中線程狀態(tài)+線程安全問題+synchronized的用法詳解,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-04-04詳解Spring中@Valid和@Validated注解用法
本文將以新增一個(gè)員工為功能切入點(diǎn),以常規(guī)寫法為背景,慢慢烘托出?@Valid?和?@Validated?注解用法詳解,文中的示例代碼講解詳細(xì),感興趣的可以了解一下2022-07-07SpringBoot集成elasticsearch使用圖文詳解
Spring Boot集成Elasticsearch其實(shí)非常簡(jiǎn)單,這篇文章主要給大家介紹了關(guān)于SpringBoot集成elasticsearch使用的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04springboot+mybatis攔截器方法實(shí)現(xiàn)水平分表操作
這篇文章主要介紹了springboot+mybatis攔截器方法實(shí)現(xiàn)水平分表操作,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-08-08