SpringBoot 开发必备基础工具类实现(纯JWT认证,无SpringSecurity)

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);
    }
}

安全性建议

  1. 密钥安全:生产环境中,JWT密钥应使用强随机字符串,并通过环境变量或配置中心管理
  2. Token过期时间:根据业务需求设置合理的过期时间,避免过长
  3. HTTPS传输:确保所有API通信使用HTTPS加密传输
  4. 密码加密:使用BCrypt等安全的密码哈希算法存储用户密码
  5. 限流措施:对登录、注册等敏感接口实施限流,防止暴力破解
  6. 黑名单机制:确保用户主动注销后,其Token立即失效
  7. 参数验证:对所有用户输入进行严格的参数验证,防止注入攻击
  8. 错误处理:不要在生产环境中暴露详细的错误信息给客户端

实现特点

  1. 无SpringSecurity依赖:整个JWT认证体系不依赖SpringSecurity框架,更加轻量级
  2. 纯过滤器实现:使用Servlet Filter实现JWT验证,简单直接
  3. 黑名单机制:支持Token主动失效
  4. 完整的认证流程:包含登录、注册、注销、刷新Token等功能
  5. 统一响应格式:所有接口返回统一的响应格式,便于前端处理
  6. 完善的异常处理:包含全局异常处理,统一处理各类异常情况

这个实现方案适合中小型项目使用,不需要SpringSecurity的复杂功能,但又需要基础的JWT认证机制。

相关推荐
张较瘦_4 小时前
Springboot | 初识Springboot 从“手动做饭”到“点外卖”的编程革命
java·spring boot·后端
间彧4 小时前
举例说明混合使用CAS和传统锁机制的成功案例
后端
间彧4 小时前
在高并发场景下,如何评估是使用CAS还是传统锁机制更合适?
后端
间彧4 小时前
在高并发场景下,如何量化评估何时应该从CAS切换到传统锁机制?
后端
oak隔壁找我4 小时前
SpringBoot 整合 Minio 和 FastDFS 实现分布式文件存储
java·后端
间彧5 小时前
CAS技术原理与应用详解
后端
华仔啊5 小时前
35岁程序员失业了,除了送外卖,还能做什么?
前端·后端·程序员
big狼王5 小时前
SonarQube本地化搭建及代码检测并导出报告PDF
java·pdf·sonarqube·sonarscanner
杨筱毅5 小时前
【Android】Handler/Looper机制相关的类图和流程图
android·java·流程图