JWT JSON Web Token是目前主流的登录身份认证方案用来替代传统的 Session。核心作用用户登录后服务端生成一段加密字符串JWT 令牌发给客户端 客户端后续每次请求接口带上这个令牌服务端就能识别「你是谁、是否登录、权限」拦截器充当门卫角色请求进入Controller之前先验token没有登录直接挡回去。首先再创建一个sys_user表CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL COMMENT 登录账号, password VARCHAR(100) NOT NULL COMMENT 加密后的密码, role VARCHAR(20) DEFAULT USER COMMENT 角色ADMIN/USER, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;插入一个测试账号密码明文123456后面代码里会加密VALUES(admin, $2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5EO, ADMIN);在前一个项目的基础上新建文件pom.xml → 加了JWT依赖entity/SysUser.java → 用户实体mapper/SysUserMapper.java → 查用户/插用户utils/JwtUtil.java → 生成token/验tokenservice/AuthService.java → 注册登录业务controller/AuthController.java → 对外暴露接口interceptor/JwtInterceptor.java → 验token拦截器config/WebConfig.java → 注册拦截器修改pom.xml新增!-- JWT依赖 -- dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-api/artifactId version0.12.3/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-impl/artifactId version0.12.3/version scoperuntime/scope /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-jackson/artifactId version0.12.3/version scoperuntime/scope /dependency !-- 密码加密 -- dependency groupIdorg.springframework.security/groupId artifactIdspring-security-crypto/artifactId /dependency在entity包中新建用户实体类SysUser.javapackage com.example.excel_import.entity; import lombok.Data; import java.time.LocalDateTime; Data//自动生成getter和setter方法 public class SysUser { private Integer id; //用户ID private String username; //用户名 private String password; //密码 private String role; //角色 private LocalDateTime createTime;//创建时间 }在mapper包中创建SysUserMapper接口package com.example.excel_import.mapper; import com.example.excel_import.entity.SysUser; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; Mapper public interface SysUserMapper { // 根据用户名查询用户登录用 Select(SELECT * FROM sys_user WHERE username #{username}) SysUser findByUsername(String username); // 注册用户插入新用户 Insert(INSERT INTO sys_user(username, password, role) VALUES(#{username}, #{password}, #{role})) void insert(SysUser user); }新建utils包创建JwtUtil类 生成token/验tokenpackage com.example.excel_import.utils; import io.jsonwebtoken.*; import io.jsonwebtoken.security.Keys; import javax.crypto.SecretKey; import java.nio.charset.StandardCharsets; import java.util.Date; public class JwtUtil { // 密钥实际项目要放到配置文件里别写死在代码里 private static final String SECRET your-secret-key-your-secret-key-your-secret-key; // 过期时间24小时毫秒 private static final long EXPIRATION 86400000; // 生成密钥对象 private static final SecretKey KEY Keys.hmacShaKeyFor(SECRET.getBytes(StandardCharsets.UTF_8)); /** * 生成JWT令牌 * param userId 用户ID * param username 用户名 * param role 角色 * return JWT字符串 */ public static String generateToken(Integer userId, String username, String role) { Date now new Date(); Date expiration new Date(now.getTime() EXPIRATION); return Jwts.builder() .subject(String.valueOf(userId)) // 主题存用户ID .claim(username, username) // 自定义字段用户名 .claim(role, role) // 自定义字段角色 .issuedAt(now) // 签发时间 .expiration(expiration) // 过期时间 .signWith(KEY) // 签名 .compact(); // 生成字符串 } /** * 解析JWT令牌 * param token 令牌字符串 * return Claims对象包含用户信息 */ public static Claims parseToken(String token) { return Jwts.parser() .verifyWith(KEY) .build() .parseSignedClaims(token) .getPayload(); } /** * 验证令牌是否有效 * param token 令牌字符串 * return true有效false无效或过期 */ public static boolean validateToken(String token) { try { parseToken(token); return true; } catch (ExpiredJwtException e) { System.out.println(令牌已过期); return false; } catch (Exception e) { System.out.println(令牌无效 e.getMessage()); return false; } } }在service包中创建AuthService类 注册登录业务package com.example.excel_import.service; import com.example.excel_import.entity.SysUser; import com.example.excel_import.mapper.SysUserMapper; import com.example.excel_import.utils.JwtUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; Service public class AuthService { Autowired private SysUserMapper sysUserMapper; // 密码加密器 private BCryptPasswordEncoder encoder new BCryptPasswordEncoder(); /** * 用户注册 */ public MapString, Object register(String username, String password) { MapString, Object result new HashMap(); // 1. 检查账号是否已存在 SysUser existUser sysUserMapper.findByUsername(username); if (existUser ! null) { result.put(success, false); result.put(message, 账号已存在); return result; } // 2. 加密密码 String encodedPassword encoder.encode(password); // 3. 创建用户 SysUser user new SysUser(); user.setUsername(username); user.setPassword(encodedPassword); user.setRole(USER); // 默认普通用户 sysUserMapper.insert(user); result.put(success, true); result.put(message, 注册成功); return result; } /** * 用户登录 */ public MapString, Object login(String username, String password) { MapString, Object result new HashMap(); // 1. 查询用户 SysUser user sysUserMapper.findByUsername(username); if (user null) { result.put(success, false); result.put(message, 账号不存在); return result; } // 2. 校验密码明文 vs 密文 if (!encoder.matches(password, user.getPassword())) { result.put(success, false); result.put(message, 密码错误); return result; } // 3. 生成JWT令牌 String token JwtUtil.generateToken(user.getId(), user.getUsername(), user.getRole()); result.put(success, true); result.put(message, 登录成功); result.put(token, token); // 前端要存这个token result.put(role, user.getRole()); return result; } }在controller包中创建AuthController类package com.example.excel_import.controller; import com.example.excel_import.service.AuthService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.Map; RestController RequestMapping(/auth) public class AuthController { Autowired private AuthService authService; // 注册接口 PostMapping(/register) public MapString, Object register( RequestParam String username, RequestParam String password) { return authService.register(username, password); } // 登录接口 PostMapping(/login) public MapString, Object login( RequestParam String username, RequestParam String password) { return authService.login(username, password); } }新建interceptor包在包中创建JwtInterceptor类 检验token拦截器package com.example.excel_import.interceptor; import com.example.excel_import.utils.JwtUtil; import io.jsonwebtoken.Claims; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; Component//标识这是一个Spring组件可以被Spring容器管理自动装配 public class JwtInterceptor implements HandlerInterceptor { Override//标识这是一个重写方法 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 1. 从请求头取token String token request.getHeader(Authorization); // 2. 没有token直接拒绝 if (token null || !token.startsWith(Bearer )) { response.setStatus(401); response.getWriter().write({\success\:false,\message\:\请先登录\}); return false; } // 3. 去掉Bearer 前缀 token token.substring(7); // 4. 验证token if (!JwtUtil.validateToken(token)) { response.setStatus(401); response.getWriter().write({\success\:false,\message\:\登录已过期请重新登录\}); return false; } // 5. 解析用户信息存到request里供后面用 Claims claims JwtUtil.parseToken(token); request.setAttribute(userId, claims.getSubject()); request.setAttribute(username, claims.get(username)); request.setAttribute(role, claims.get(role)); return true; // 放行 } }在config包中创建WebConfig类 注册拦截器package com.example.excel_import.config; import com.example.excel_import.interceptor.JwtInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; Configuration public class WebConfig implements WebMvcConfigurer { Autowired private JwtInterceptor jwtInterceptor; Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtInterceptor) .addPathPatterns(/**) // 拦截所有接口 .excludePathPatterns( // 排除这些接口公开访问 /auth/register, /auth/login ); } }数据流向注册账号密码 → BCrypt加密 → 存数据库登录账号密码 → 查数据库 → BCrypt比对 → 生成JWT → 返回前端后续请求带token → 拦截器验签 → 放行到Controller运行此项目测试验证清单POST/auth/register能注册新用户POST/auth/login能登录并返回tokenGET/api/users不带token返回401GET/api/users带正确token返回数据打开PostmanPOST url:http://localhost:8080/auth/registerBody中form-data中key:username value:testkey:password value:123456点击SENDPOST url:http://localhost:8080/auth/loginBody中form-data中key:username value:testkey:password value:123456点击SEND获取tokenGET url:http://localhost:8080/api/usersHeaders中新增key:AuthorizationValue:Bearer 生成的token例如Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI0IiwidXNlcm5hbWUiOiJ0ZXN0Iiwicm9sZSI6IlVTRVIiLCJpYXQiOjE3ODA4ODE4ODIsImV4cCI6MTc4MDk2ODI4Mn0.db13bq5lDlnD3dsZQumx5QK7_nsyGvfeHYVCRu0pypE
JWT登录认证系统 —— 用户注册/登录 + 接口保护
发布时间:2026/6/9 2:46:56
JWT JSON Web Token是目前主流的登录身份认证方案用来替代传统的 Session。核心作用用户登录后服务端生成一段加密字符串JWT 令牌发给客户端 客户端后续每次请求接口带上这个令牌服务端就能识别「你是谁、是否登录、权限」拦截器充当门卫角色请求进入Controller之前先验token没有登录直接挡回去。首先再创建一个sys_user表CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL COMMENT 登录账号, password VARCHAR(100) NOT NULL COMMENT 加密后的密码, role VARCHAR(20) DEFAULT USER COMMENT 角色ADMIN/USER, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;插入一个测试账号密码明文123456后面代码里会加密VALUES(admin, $2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5EO, ADMIN);在前一个项目的基础上新建文件pom.xml → 加了JWT依赖entity/SysUser.java → 用户实体mapper/SysUserMapper.java → 查用户/插用户utils/JwtUtil.java → 生成token/验tokenservice/AuthService.java → 注册登录业务controller/AuthController.java → 对外暴露接口interceptor/JwtInterceptor.java → 验token拦截器config/WebConfig.java → 注册拦截器修改pom.xml新增!-- JWT依赖 -- dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-api/artifactId version0.12.3/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-impl/artifactId version0.12.3/version scoperuntime/scope /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-jackson/artifactId version0.12.3/version scoperuntime/scope /dependency !-- 密码加密 -- dependency groupIdorg.springframework.security/groupId artifactIdspring-security-crypto/artifactId /dependency在entity包中新建用户实体类SysUser.javapackage com.example.excel_import.entity; import lombok.Data; import java.time.LocalDateTime; Data//自动生成getter和setter方法 public class SysUser { private Integer id; //用户ID private String username; //用户名 private String password; //密码 private String role; //角色 private LocalDateTime createTime;//创建时间 }在mapper包中创建SysUserMapper接口package com.example.excel_import.mapper; import com.example.excel_import.entity.SysUser; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; Mapper public interface SysUserMapper { // 根据用户名查询用户登录用 Select(SELECT * FROM sys_user WHERE username #{username}) SysUser findByUsername(String username); // 注册用户插入新用户 Insert(INSERT INTO sys_user(username, password, role) VALUES(#{username}, #{password}, #{role})) void insert(SysUser user); }新建utils包创建JwtUtil类 生成token/验tokenpackage com.example.excel_import.utils; import io.jsonwebtoken.*; import io.jsonwebtoken.security.Keys; import javax.crypto.SecretKey; import java.nio.charset.StandardCharsets; import java.util.Date; public class JwtUtil { // 密钥实际项目要放到配置文件里别写死在代码里 private static final String SECRET your-secret-key-your-secret-key-your-secret-key; // 过期时间24小时毫秒 private static final long EXPIRATION 86400000; // 生成密钥对象 private static final SecretKey KEY Keys.hmacShaKeyFor(SECRET.getBytes(StandardCharsets.UTF_8)); /** * 生成JWT令牌 * param userId 用户ID * param username 用户名 * param role 角色 * return JWT字符串 */ public static String generateToken(Integer userId, String username, String role) { Date now new Date(); Date expiration new Date(now.getTime() EXPIRATION); return Jwts.builder() .subject(String.valueOf(userId)) // 主题存用户ID .claim(username, username) // 自定义字段用户名 .claim(role, role) // 自定义字段角色 .issuedAt(now) // 签发时间 .expiration(expiration) // 过期时间 .signWith(KEY) // 签名 .compact(); // 生成字符串 } /** * 解析JWT令牌 * param token 令牌字符串 * return Claims对象包含用户信息 */ public static Claims parseToken(String token) { return Jwts.parser() .verifyWith(KEY) .build() .parseSignedClaims(token) .getPayload(); } /** * 验证令牌是否有效 * param token 令牌字符串 * return true有效false无效或过期 */ public static boolean validateToken(String token) { try { parseToken(token); return true; } catch (ExpiredJwtException e) { System.out.println(令牌已过期); return false; } catch (Exception e) { System.out.println(令牌无效 e.getMessage()); return false; } } }在service包中创建AuthService类 注册登录业务package com.example.excel_import.service; import com.example.excel_import.entity.SysUser; import com.example.excel_import.mapper.SysUserMapper; import com.example.excel_import.utils.JwtUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; Service public class AuthService { Autowired private SysUserMapper sysUserMapper; // 密码加密器 private BCryptPasswordEncoder encoder new BCryptPasswordEncoder(); /** * 用户注册 */ public MapString, Object register(String username, String password) { MapString, Object result new HashMap(); // 1. 检查账号是否已存在 SysUser existUser sysUserMapper.findByUsername(username); if (existUser ! null) { result.put(success, false); result.put(message, 账号已存在); return result; } // 2. 加密密码 String encodedPassword encoder.encode(password); // 3. 创建用户 SysUser user new SysUser(); user.setUsername(username); user.setPassword(encodedPassword); user.setRole(USER); // 默认普通用户 sysUserMapper.insert(user); result.put(success, true); result.put(message, 注册成功); return result; } /** * 用户登录 */ public MapString, Object login(String username, String password) { MapString, Object result new HashMap(); // 1. 查询用户 SysUser user sysUserMapper.findByUsername(username); if (user null) { result.put(success, false); result.put(message, 账号不存在); return result; } // 2. 校验密码明文 vs 密文 if (!encoder.matches(password, user.getPassword())) { result.put(success, false); result.put(message, 密码错误); return result; } // 3. 生成JWT令牌 String token JwtUtil.generateToken(user.getId(), user.getUsername(), user.getRole()); result.put(success, true); result.put(message, 登录成功); result.put(token, token); // 前端要存这个token result.put(role, user.getRole()); return result; } }在controller包中创建AuthController类package com.example.excel_import.controller; import com.example.excel_import.service.AuthService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.Map; RestController RequestMapping(/auth) public class AuthController { Autowired private AuthService authService; // 注册接口 PostMapping(/register) public MapString, Object register( RequestParam String username, RequestParam String password) { return authService.register(username, password); } // 登录接口 PostMapping(/login) public MapString, Object login( RequestParam String username, RequestParam String password) { return authService.login(username, password); } }新建interceptor包在包中创建JwtInterceptor类 检验token拦截器package com.example.excel_import.interceptor; import com.example.excel_import.utils.JwtUtil; import io.jsonwebtoken.Claims; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; Component//标识这是一个Spring组件可以被Spring容器管理自动装配 public class JwtInterceptor implements HandlerInterceptor { Override//标识这是一个重写方法 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 1. 从请求头取token String token request.getHeader(Authorization); // 2. 没有token直接拒绝 if (token null || !token.startsWith(Bearer )) { response.setStatus(401); response.getWriter().write({\success\:false,\message\:\请先登录\}); return false; } // 3. 去掉Bearer 前缀 token token.substring(7); // 4. 验证token if (!JwtUtil.validateToken(token)) { response.setStatus(401); response.getWriter().write({\success\:false,\message\:\登录已过期请重新登录\}); return false; } // 5. 解析用户信息存到request里供后面用 Claims claims JwtUtil.parseToken(token); request.setAttribute(userId, claims.getSubject()); request.setAttribute(username, claims.get(username)); request.setAttribute(role, claims.get(role)); return true; // 放行 } }在config包中创建WebConfig类 注册拦截器package com.example.excel_import.config; import com.example.excel_import.interceptor.JwtInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; Configuration public class WebConfig implements WebMvcConfigurer { Autowired private JwtInterceptor jwtInterceptor; Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtInterceptor) .addPathPatterns(/**) // 拦截所有接口 .excludePathPatterns( // 排除这些接口公开访问 /auth/register, /auth/login ); } }数据流向注册账号密码 → BCrypt加密 → 存数据库登录账号密码 → 查数据库 → BCrypt比对 → 生成JWT → 返回前端后续请求带token → 拦截器验签 → 放行到Controller运行此项目测试验证清单POST/auth/register能注册新用户POST/auth/login能登录并返回tokenGET/api/users不带token返回401GET/api/users带正确token返回数据打开PostmanPOST url:http://localhost:8080/auth/registerBody中form-data中key:username value:testkey:password value:123456点击SENDPOST url:http://localhost:8080/auth/loginBody中form-data中key:username value:testkey:password value:123456点击SEND获取tokenGET url:http://localhost:8080/api/usersHeaders中新增key:AuthorizationValue:Bearer 生成的token例如Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI0IiwidXNlcm5hbWUiOiJ0ZXN0Iiwicm9sZSI6IlVTRVIiLCJpYXQiOjE3ODA4ODE4ODIsImV4cCI6MTc4MDk2ODI4Mn0.db13bq5lDlnD3dsZQumx5QK7_nsyGvfeHYVCRu0pypE