SpringBoot 开发必备基础工具类实现(纯JWT认证,无SpringSecurity)
在SpringBoot后端开发中,有一些常用的基础工具类可以大大提升开发效率和代码质量。下面为你介绍核心的基础工具类实现,包括纯JWT认证方案(不依赖SpringSecurity)。
1. 编码过滤器(字符编码设置)
java
@Component
public class CharsetFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 设置请求和响应的字符编码为UTF-8
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=UTF-8");
// 继续执行过滤链
chain.doFilter(request, response);
}
// 其他生命周期方法可以使用默认实现
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
2. 跨域配置(CORS)
java
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
// 创建CORS配置对象
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 允许所有来源访问(生产环境应该配置具体域名)
corsConfiguration.addAllowedOrigin("*");
// 允许所有请求头
corsConfiguration.addAllowedHeader("*");
// 允许所有HTTP方法
corsConfiguration.addAllowedMethod("*");
// 允许携带凭证(如cookies)
corsConfiguration.setAllowCredentials(true);
// 设置预检请求的缓存时间(秒)
corsConfiguration.setMaxAge(3600L);
// 创建基于URL的CORS配置源
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 注册全局CORS配置,对所有路径生效
source.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(source);
}
}
3. 统一响应格式
java
public class Result<T> {
// 响应状态码
private int code;
// 响应消息
private String message;
// 响应数据
private T data;
// 时间戳
private long timestamp;
public Result() {
this.timestamp = System.currentTimeMillis();
}
// 成功响应静态方法
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMessage("success");
result.setData(data);
return result;
}
// 失败响应静态方法
public static <T> Result<T> fail(int code, String message) {
Result<T> result = new Result<>();
result.setCode(code);
result.setMessage(message);
return result;
}
// getter和setter方法
public int getCode() { return code; }
public void setCode(int code) { this.code = code; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public T getData() { return data; }
public void setData(T data) { this.data = data; }
public long getTimestamp() { return timestamp; }
}
4. 全局异常处理器
java
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
// 处理自定义业务异常
@ExceptionHandler(BusinessException.class)
public Result<String> handleBusinessException(BusinessException ex) {
logger.error("业务异常: {}", ex.getMessage(), ex);
return Result.fail(ex.getCode(), ex.getMessage());
}
// 处理JWT相关异常
@ExceptionHandler(JwtException.class)
public Result<String> handleJwtException(JwtException ex) {
logger.error("JWT认证异常: {}", ex.getMessage(), ex);
return Result.fail(401, "无效的令牌,请重新登录");
}
// 处理参数验证异常
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
// 获取第一个错误信息
String errorMsg = ex.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.findFirst()
.orElse("参数验证失败");
logger.error("参数验证异常: {}", errorMsg, ex);
return Result.fail(400, errorMsg);
}
// 处理其他所有异常
@ExceptionHandler(Exception.class)
public Result<String> handleException(Exception ex) {
logger.error("系统异常: {}", ex.getMessage(), ex);
return Result.fail(500, "系统内部错误,请稍后重试");
}
}
5. 自定义业务异常类
java
public class BusinessException extends RuntimeException {
private int code;
public BusinessException(String message) {
super(message);
this.code = 400;
}
public BusinessException(int code, String message) {
super(message);
this.code = code;
}
public int getCode() {
return code;
}
}
// 自定义JWT异常类
public class JwtException extends RuntimeException {
private int code;
public JwtException(String message) {
super(message);
this.code = 401;
}
public JwtException(int code, String message) {
super(message);
this.code = code;
}
public int getCode() {
return code;
}
}
6. JWT配置属性类
java
@Data
@Configuration
@ConfigurationProperties(prefix = "jwt")
public class JwtProperties {
/** Request Headers :Authorization */
private String header = "Authorization";
/** 令牌前缀,最后留个空格 Bearer */
private String tokenStartWith = "Bearer ";
/** 使用Base64对该令牌进行编码的密钥 */
private String secret = "your-secret-key-change-in-production";
/** 令牌过期时间(毫秒) */
private long expiration = 14400000;
/** 记住我令牌过期时间(毫秒) */
private long rememberExpiration = 604800000;
}
7. 纯JWT工具类(不依赖SpringSecurity)
java
@Component
public class JwtTokenUtil {
private static final String CLAIM_KEY_USERNAME = "username";
private static final String CLAIM_KEY_CREATED = "created";
private static final String CLAIM_KEY_USER_ID = "userId";
private static final String CLAIM_KEY_ROLES = "roles";
@Autowired
private JwtProperties jwtProperties;
/**
* 生成JWT令牌
*/
public String generateToken(String username, Long userId, List<String> roles) {
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, username);
claims.put(CLAIM_KEY_USER_ID, userId);
claims.put(CLAIM_KEY_ROLES, roles);
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
/**
* 从令牌中获取用户名
*/
public String getUsernameFromToken(String token) {
String username;
try {
Claims claims = getClaimsFromToken(token);
username = claims.get(CLAIM_KEY_USERNAME, String.class);
} catch (Exception e) {
username = null;
}
return username;
}
/**
* 从令牌中获取用户ID
*/
public Long getUserIdFromToken(String token) {
Long userId;
try {
Claims claims = getClaimsFromToken(token);
userId = claims.get(CLAIM_KEY_USER_ID, Long.class);
} catch (Exception e) {
userId = null;
}
return userId;
}
/**
* 从令牌中获取用户角色
*/
@SuppressWarnings("unchecked")
public List<String> getRolesFromToken(String token) {
List<String> roles;
try {
Claims claims = getClaimsFromToken(token);
roles = (List<String>) claims.get(CLAIM_KEY_ROLES);
} catch (Exception e) {
roles = Collections.emptyList();
}
return roles;
}
/**
* 验证令牌是否有效
*/
public boolean validateToken(String token) {
try {
Claims claims = getClaimsFromToken(token);
return claims != null && !isTokenExpired(token);
} catch (Exception e) {
return false;
}
}
/**
* 刷新令牌
*/
public String refreshToken(String token) {
String refreshedToken;
try {
Claims claims = getClaimsFromToken(token);
claims.put(CLAIM_KEY_CREATED, new Date());
refreshedToken = generateToken(claims);
} catch (Exception e) {
refreshedToken = null;
}
return refreshedToken;
}
/**
* 从令牌中获取数据声明
*/
private Claims getClaimsFromToken(String token) {
Claims claims;
try {
claims = Jwts.parser()
.setSigningKey(jwtProperties.getSecret())
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
claims = null;
}
return claims;
}
/**
* 生成令牌
*/
private String generateToken(Map<String, Object> claims) {
Date expirationDate = new Date(System.currentTimeMillis() + jwtProperties.getExpiration());
return Jwts.builder()
.setClaims(claims)
.setExpiration(expirationDate)
.signWith(SignatureAlgorithm.HS512, jwtProperties.getSecret())
.compact();
}
/**
* 判断令牌是否过期
*/
private boolean isTokenExpired(String token) {
Date expiredDate = getExpiredDateFromToken(token);
return expiredDate.before(new Date());
}
/**
* 获取令牌过期时间
*/
private Date getExpiredDateFromToken(String token) {
Claims claims = getClaimsFromToken(token);
return claims.getExpiration();
}
}
8. 纯JWT认证过滤器(不依赖SpringSecurity)
java
@Component
public class JwtAuthenticationFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private JwtProperties jwtProperties;
@Autowired
private TokenBlacklistService tokenBlacklistService;
// 不需要认证的路径
private static final List<String> EXCLUDED_PATHS = Arrays.asList(
"/api/auth/login",
"/api/auth/register",
"/swagger-ui/**",
"/v3/api-docs/**"
);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 获取请求URI
String uri = httpRequest.getRequestURI();
// 检查是否为不需要认证的路径
if (isExcludedPath(uri)) {
chain.doFilter(request, response);
return;
}
// 获取请求头中的Authorization
String authHeader = httpRequest.getHeader(jwtProperties.getHeader());
// 如果请求头中没有Bearer令牌,返回401错误
if (authHeader == null || !authHeader.startsWith(jwtProperties.getTokenStartWith())) {
sendErrorResponse(httpResponse, 401, "未提供有效的认证令牌");
return;
}
try {
// 提取令牌
String authToken = authHeader.substring(jwtProperties.getTokenStartWith().length());
// 检查Token是否在黑名单中
if (tokenBlacklistService.isInBlacklist(authToken)) {
sendErrorResponse(httpResponse, 401, "令牌已被注销");
return;
}
// 验证令牌是否有效
if (!jwtTokenUtil.validateToken(authToken)) {
sendErrorResponse(httpResponse, 401, "无效的令牌,请重新登录");
return;
}
// 从令牌中获取用户信息并设置到请求属性中
String username = jwtTokenUtil.getUsernameFromToken(authToken);
Long userId = jwtTokenUtil.getUserIdFromToken(authToken);
List<String> roles = jwtTokenUtil.getRolesFromToken(authToken);
// 将用户信息存储到请求属性中,供后续处理使用
httpRequest.setAttribute("username", username);
httpRequest.setAttribute("userId", userId);
httpRequest.setAttribute("roles", roles);
// 继续执行过滤链
chain.doFilter(request, response);
} catch (Exception e) {
logger.error("JWT认证失败: {}", e.getMessage());
sendErrorResponse(httpResponse, 401, "认证失败,请重新登录");
}
}
/**
* 发送错误响应
*/
private void sendErrorResponse(HttpServletResponse response, int code, String message) throws IOException {
response.setStatus(code);
response.setContentType("application/json;charset=UTF-8");
Result<String> result = Result.fail(code, message);
ObjectMapper mapper = new ObjectMapper();
response.getWriter().write(mapper.writeValueAsString(result));
}
/**
* 检查是否为排除路径
*/
private boolean isExcludedPath(String uri) {
for (String excludedPath : EXCLUDED_PATHS) {
if (excludedPath.contains("**")) {
// 处理通配符路径
String prefix = excludedPath.substring(0, excludedPath.indexOf("**"));
if (uri.startsWith(prefix)) {
return true;
}
} else if (uri.equals(excludedPath)) {
return true;
}
}
return false;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化方法
}
@Override
public void destroy() {
// 销毁方法
}
}
9. 过滤器配置类
java
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<JwtAuthenticationFilter> jwtAuthenticationFilterRegistration(JwtAuthenticationFilter jwtAuthenticationFilter) {
FilterRegistrationBean<JwtAuthenticationFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(jwtAuthenticationFilter);
// 拦截所有请求
registration.addUrlPatterns("/*");
// 设置过滤器名称
registration.setName("jwtAuthenticationFilter");
// 设置过滤器顺序(越小越先执行)
registration.setOrder(1);
return registration;
}
@Bean
public FilterRegistrationBean<CharsetFilter> charsetFilterRegistration(CharsetFilter charsetFilter) {
FilterRegistrationBean<CharsetFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(charsetFilter);
registration.addUrlPatterns("/*");
registration.setName("charsetFilter");
// 字符编码过滤器应该最先执行
registration.setOrder(0);
return registration;
}
}
10. Redis实现Token黑名单
java
@Component
public class TokenBlacklistService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String BLACKLIST_PREFIX = "token:blacklist:";
/**
* 将Token加入黑名单
*/
public void addToBlacklist(String token, long expirationMillis) {
String key = BLACKLIST_PREFIX + token;
redisTemplate.opsForValue().set(key, "1", expirationMillis, TimeUnit.MILLISECONDS);
}
/**
* 检查Token是否在黑名单中
*/
public boolean isInBlacklist(String token) {
String key = BLACKLIST_PREFIX + token;
return redisTemplate.hasKey(key);
}
/**
* 从黑名单中移除Token
*/
public void removeFromBlacklist(String token) {
String key = BLACKLIST_PREFIX + token;
redisTemplate.delete(key);
}
}
11. 用户实体类
java
@Data
public class User {
private Long id;
private String username;
private String password;
private String email;
private String phone;
private Integer status; // 0:禁用 1:启用
private Date createTime;
private Date updateTime;
// 非数据库字段
private List<String> roles;
}
12. 认证控制器
java
@RestController
@RequestMapping("/api/auth")
public class AuthController {
private static final Logger logger = LoggerFactory.getLogger(AuthController.class);
@Autowired
private UserService userService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private JwtProperties jwtProperties;
@Autowired
private TokenBlacklistService tokenBlacklistService;
@Autowired
private PasswordEncoder passwordEncoder;
/**
* 用户登录
*/
@PostMapping("/login")
public Result<Map<String, String>> login(@RequestBody LoginRequest loginRequest) {
// 验证用户名和密码
User user = userService.findByUsername(loginRequest.getUsername());
if (user == null) {
throw new BusinessException(401, "用户名或密码错误");
}
// 验证密码
if (!passwordEncoder.matches(loginRequest.getPassword(), user.getPassword())) {
throw new BusinessException(401, "用户名或密码错误");
}
// 检查用户状态
if (user.getStatus() != 1) {
throw new BusinessException(403, "用户账户已被禁用");
}
// 获取用户角色
List<String> roles = userService.getUserRoles(user.getId());
// 生成JWT令牌
String token = jwtTokenUtil.generateToken(user.getUsername(), user.getId(), roles);
// 构建响应
Map<String, String> result = new HashMap<>();
result.put("token", token);
result.put("username", user.getUsername());
result.put("userId", String.valueOf(user.getId()));
logger.info("用户登录成功: {}", user.getUsername());
return Result.success(result);
}
/**
* 刷新令牌
*/
@PostMapping("/refresh")
public Result<Map<String, String>> refresh(@RequestHeader("Authorization") String authHeader) {
// 提取令牌
String token = authHeader.substring(jwtProperties.getTokenStartWith().length());
// 验证令牌是否有效
if (!jwtTokenUtil.validateToken(token)) {
throw new JwtException("无效的令牌,无法刷新");
}
// 刷新令牌
String refreshedToken = jwtTokenUtil.refreshToken(token);
if (refreshedToken == null) {
throw new JwtException("无法刷新令牌,请重新登录");
}
// 构建响应
Map<String, String> result = new HashMap<>();
result.put("token", refreshedToken);
return Result.success(result);
}
/**
* 用户注销
*/
@PostMapping("/logout")
public Result<String> logout(@RequestHeader("Authorization") String authHeader) {
// 提取令牌
String token = authHeader.substring(jwtProperties.getTokenStartWith().length());
try {
// 计算令牌剩余有效期
Claims claims = Jwts.parser()
.setSigningKey(jwtProperties.getSecret())
.parseClaimsJws(token)
.getBody();
long expirationTime = claims.getExpiration().getTime();
long now = System.currentTimeMillis();
long remainingTime = expirationTime - now;
// 如果Token尚未过期,加入黑名单
if (remainingTime > 0) {
tokenBlacklistService.addToBlacklist(token, remainingTime);
logger.info("用户注销成功,Token已加入黑名单");
}
} catch (Exception e) {
logger.error("处理Token黑名单时出错: {}", e.getMessage());
}
return Result.success("注销成功");
}
/**
* 获取当前用户信息
*/
@GetMapping("/me")
public Result<Map<String, Object>> getCurrentUser(HttpServletRequest request) {
// 从请求属性中获取用户信息(由JwtAuthenticationFilter设置)
String username = (String) request.getAttribute("username");
Long userId = (Long) request.getAttribute("userId");
List<String> roles = (List<String>) request.getAttribute("roles");
Map<String, Object> result = new HashMap<>();
result.put("username", username);
result.put("userId", userId);
result.put("roles", roles);
// 可以获取更多用户信息
User user = userService.findByUsername(username);
if (user != null) {
result.put("email", user.getEmail());
result.put("phone", user.getPhone());
}
return Result.success(result);
}
/**
* 注册新用户
*/
@PostMapping("/register")
public Result<String> register(@RequestBody RegisterRequest registerRequest) {
// 验证两次密码是否一致
if (!registerRequest.getPassword().equals(registerRequest.getConfirmPassword())) {
throw new BusinessException(400, "两次输入的密码不一致");
}
// 检查用户名是否已存在
if (userService.findByUsername(registerRequest.getUsername()) != null) {
throw new BusinessException(400, "用户名已存在");
}
// 创建新用户
User user = new User();
user.setUsername(registerRequest.getUsername());
user.setPassword(passwordEncoder.encode(registerRequest.getPassword()));
user.setStatus(1); // 默认为启用状态
user.setCreateTime(new Date());
user.setUpdateTime(new Date());
// 保存用户
userService.save(user);
// 分配默认角色(例如:普通用户)
userService.assignDefaultRole(user.getId());
logger.info("新用户注册成功: {}", user.getUsername());
return Result.success("注册成功");
}
}
13. 用户服务接口
java
public interface UserService {
/**
* 根据用户名查找用户
*/
User findByUsername(String username);
/**
* 保存用户
*/
void save(User user);
/**
* 获取用户角色
*/
List<String> getUserRoles(Long userId);
/**
* 分配默认角色给用户
*/
void assignDefaultRole(Long userId);
/**
* 根据用户ID查找用户
*/
User findById(Long userId);
}
14. 用户服务实现类
java
@Service
public class UserServiceImpl implements UserService {
// 这里可以注入数据访问层
// @Autowired
// private UserRepository userRepository;
// 模拟用户数据存储
private static final Map<String, User> USER_MAP = new ConcurrentHashMap<>();
private static final Map<Long, List<String>> USER_ROLES_MAP = new ConcurrentHashMap<>();
private static long USER_ID_SEQ = 1;
@Override
public User findByUsername(String username) {
// 实际项目中应该从数据库查询
return USER_MAP.get(username);
}
@Override
public void save(User user) {
// 设置用户ID
if (user.getId() == null) {
user.setId(USER_ID_SEQ++);
}
// 保存用户
USER_MAP.put(user.getUsername(), user);
// 实际项目中应该保存到数据库
// userRepository.save(user);
}
@Override
public List<String> getUserRoles(Long userId) {
// 实际项目中应该从数据库查询用户角色关系
return USER_ROLES_MAP.getOrDefault(userId, Collections.emptyList());
}
@Override
public void assignDefaultRole(Long userId) {
// 分配默认角色为普通用户
List<String> roles = new ArrayList<>();
roles.add("ROLE_USER");
USER_ROLES_MAP.put(userId, roles);
// 实际项目中应该保存到数据库
}
@Override
public User findById(Long userId) {
// 实际项目中应该从数据库查询
return USER_MAP.values().stream()
.filter(user -> user.getId().equals(userId))
.findFirst()
.orElse(null);
}
}
15. 登录和注册请求类
java
@Data
public class LoginRequest {
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
private String password;
private boolean rememberMe = false;
}
@Data
public class RegisterRequest {
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 6, message = "密码长度不能少于6位")
private String password;
@NotBlank(message = "确认密码不能为空")
private String confirmPassword;
private String email;
private String phone;
}
16. 密码编码器
java
@Component
public class PasswordEncoder {
/**
* 对密码进行加密
*/
public String encode(String rawPassword) {
// 使用BCrypt进行密码加密
return BCrypt.hashpw(rawPassword, BCrypt.gensalt(12));
}
/**
* 验证密码
*/
public boolean matches(String rawPassword, String encodedPassword) {
// 验证密码是否匹配
return BCrypt.checkpw(rawPassword, encodedPassword);
}
}
17. Maven依赖配置
xml
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<!-- Redis (用于Token黑名单) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- BCrypt (用于密码加密) -->
<dependency>
<groupId>org.mindrot</groupId>
<artifactId>jbcrypt</artifactId>
<version>0.4</version>
</dependency>
</dependencies>
18. 配置文件 (application.yml)
yaml
server:
port: 8080
spring:
# Redis配置(用于Token黑名单)
redis:
host: localhost
port: 6379
database: 0
# JWT配置
jwt:
# 请求头名称
header: Authorization
# 令牌前缀
token-start-with: Bearer
# 密钥(生产环境应使用强密钥并妥善保管)
secret: your-secret-key-change-in-production
# 令牌过期时间(毫秒)
expiration: 14400000
# 记住我令牌过期时间(毫秒)
remember-expiration: 604800000
19. 使用示例
测试控制器(需要认证)
java
@RestController
@RequestMapping("/api/test")
public class TestController {
/**
* 需要认证的测试接口
*/
@GetMapping("/protected")
public Result<String> protectedResource(HttpServletRequest request) {
String username = (String) request.getAttribute("username");
return Result.success("你好," + username + "!这是一个需要认证的接口");
}
/**
* 测试用户角色
*/
@GetMapping("/check-role")
public Result<List<String>> checkRole(HttpServletRequest request) {
List<String> roles = (List<String>) request.getAttribute("roles");
return Result.success(roles);
}
}
安全性建议
- 密钥安全:生产环境中,JWT密钥应使用强随机字符串,并通过环境变量或配置中心管理
- Token过期时间:根据业务需求设置合理的过期时间,避免过长
- HTTPS传输:确保所有API通信使用HTTPS加密传输
- 密码加密:使用BCrypt等安全的密码哈希算法存储用户密码
- 限流措施:对登录、注册等敏感接口实施限流,防止暴力破解
- 黑名单机制:确保用户主动注销后,其Token立即失效
- 参数验证:对所有用户输入进行严格的参数验证,防止注入攻击
- 错误处理:不要在生产环境中暴露详细的错误信息给客户端
实现特点
- 无SpringSecurity依赖:整个JWT认证体系不依赖SpringSecurity框架,更加轻量级
- 纯过滤器实现:使用Servlet Filter实现JWT验证,简单直接
- 黑名单机制:支持Token主动失效
- 完整的认证流程:包含登录、注册、注销、刷新Token等功能
- 统一响应格式:所有接口返回统一的响应格式,便于前端处理
- 完善的异常处理:包含全局异常处理,统一处理各类异常情况
这个实现方案适合中小型项目使用,不需要SpringSecurity的复杂功能,但又需要基础的JWT认证机制。