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ù)需求獲?。?/p>
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-01
Java+ElasticSearch+Pytorch實(shí)現(xiàn)以圖搜圖功能
這篇文章主要為大家詳細(xì)介紹了Java如何利用ElasticSearch和Pytorch實(shí)現(xiàn)以圖搜圖功能,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解一下2023-06-06
SpringBoot單點(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-12
Java中線程狀態(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-07
SpringBoot集成elasticsearch使用圖文詳解
Spring Boot集成Elasticsearch其實(shí)非常簡(jiǎn)單,這篇文章主要給大家介紹了關(guān)于SpringBoot集成elasticsearch使用的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04
springboot+mybatis攔截器方法實(shí)現(xiàn)水平分表操作
這篇文章主要介紹了springboot+mybatis攔截器方法實(shí)現(xiàn)水平分表操作,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-08-08

