背景
简单梳理一下项目中的JWT登录认证功能
一、简单理解
JWT(JSON Web Token)是json格式的网络令牌,分为三个主要部分:Header(头部)、Payload(载荷)和Signature(签名)。其对比cookie和session的优点,常用于登录认证场景中。
二、实现步骤
数据库设计
MySQL
-- auto-generated definition
create table user
(
id int auto_increment comment ' 主键'
primary key,
username varchar(20) not null comment '用户名',
password varchar(100) not null comment '密码',
create_time datetime not null comment '创建时间',
update_time datetime not null comment '修改时间',
constraint user_uk
unique (username)
)
comment '用户表';
项目基础配置
1、application.yml 配置
yml
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/my_blog_demo?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
configuration:
map-underscore-to-camel-case: true
2、pom.xml依赖文件配置
xml
<!--添加以下JWT所需的依赖-->
<!-- JWT API -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<!-- JWT 实现 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<!-- JSON 解析 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
基础组件封装
1、实体类
java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
//用户id
private Integer id;
//用户名
private String username;
//密码
private String password;
//创建时间
private LocalDateTime createTime;
//修改时间
private LocalDateTime updateTIme;
}
2、Result类
java
@Data
public class Result<T> implements Serializable {
private Integer code; //业务状态码:1成功,0失败
private String msg; //提示信息
private T data; //数据
private Result(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
//响应成功,无数据返回
public static <T> Result<T> success() {
return new Result<>(1, "success", null);
}
//响应成功,有数据返回
public static <T> Result<T> success(T data) {
return new Result<>(1, "success", data);
}
//响应失败,提示错误信息
public static <T> Result<T> error(String msg) {
return new Result<>(0, msg, null);
}
}
3、DTO
java
@Data
public class UserLoginDTO {
//用户名
private String username;
//密码
private String password;
}
4、全局异常处理器
java
public class BaseException extends RuntimeException {
public BaseException(String message) {
super(message);
}
}
java
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler
public Result exceptionHandler(BaseException ex){
log.error("异常信息:{}", ex.getMessage());
return Result.error(ex.getMessage());
}
}
JWT认证实现
1、封装JWT工具类
java
public class JwtUtil {
/**
* 生成JWT
*/
public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
Key key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));
long expMillis = System.currentTimeMillis() + ttlMillis;
Date exp = new Date(expMillis);
JwtBuilder builder = Jwts.builder()
.setClaims(claims)
.setExpiration(exp)
.signWith(key, SignatureAlgorithm.HS256);
return builder.compact();
}
/**
* 解析JWT
*/
public static Claims parseJWT(String secretKey, String token) {
Key key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));
Claims claims = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody();
return claims;
}
}
2、定义拦截器并注册
java
@Slf4j
@Component
public class JwtInterceptor implements HandlerInterceptor {
private static final String SECRET_KEY = "hooknum-login-demo-jwt-secret-key-2026";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 从请求头获取token
String token = request.getHeader("token");
if (token == null || token.isEmpty()) {
log.info("认证不通过");
response.setStatus(401);
return false;
}
try {
// 解析token
Claims claims = JwtUtil.parseJWT(SECRET_KEY, token);
// 可以把用户信息放入request
request.setAttribute("userId", claims.get("userId"));
} catch (Exception e) {
log.info("认证不通过");
response.setStatus(401);
return false;
}
return true;
}
}
java
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private JwtInterceptor jwtInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/**") // 拦截所有请求
.excludePathPatterns("/user/auth/login"); // 登录接口放行
}
}
三层架构实现
Controller层
java
@RestController
@RequestMapping("/user/auth")
@Slf4j
public class UserAuthController {
@Autowired
private UserAuthService userAuthService;
@PostMapping("/login")
public Result login(@RequestBody UserLoginDTO userLoginDTO) {
log.info("用户登录:");
String token = userAuthService.login(userLoginDTO);
return Result.success(token);
}
}
Service层
java
public interface UserAuthService {
String login(UserLoginDTO userLoginDTO);
}
java
@Service
public class UserAuthServiceImpl implements UserAuthService {
@Autowired
private UserMapper userMapper;
@Override
public String login(UserLoginDTO userLoginDTO) {
String username = userLoginDTO.getUsername();
String password = userLoginDTO.getPassword();
User user = userMapper.getByUseranme(username);
//用户身份检验
if (user == null){
throw new BaseException("用户不存在");
}
if (!user.getPassword().equals(password)){
throw new BaseException("密码错误");
}
//检验通过、生成JWT令牌
Map<String, Object> claims = new HashMap<>();
claims.put("userId",user.getId());
String token = JwtUtil.createJWT("hooknum-login-demo-jwt-secret-key-2026", 3600000, claims);
return token;
}
}
Mapper层
java
@Mapper
public interface UserMapper {
@Select("select * from my_blog_demo.user where username = #{username}")
User getByUseranme(String username);
}
三、测试
先向表中插入一条用户数据,hooknum,123456


四、总结
本篇功能简单,设计所学知识有JWT、全局异常处理器、拦截器。一般JWT会配合ThreadLocal保存当前用户。通篇下来让我进一步复习了登录认证的流程和设计,不过实战登录认证功能有更加完善的规范设计和安全机制,这值得我进一步学习和实践。
最后
声明:本文仅作为学习记录分享使用,内容基于作者当前技术认知整理,如需要参考请自行决定价值度,若有不准确或描述不清之处,欢迎评论区指正,我会在能力范围内修正完善,谢谢阅读!🙏