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

2025-12-04 0 422

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

在SpringBoot后端开发中,有一些常用的基础工具类可以大大提升开发效率和代码质量。下面为你介绍核心的基础工具类实现,包括纯JWT认证方案(不依赖SpringSecurity)。

1. 编码过滤器(字符编码设置)

@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)

@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. 统一响应格式

public class Result {
    // 响应状态码
    private int code;
    // 响应消息
    private String message;
    // 响应数据
    private T data;
    // 时间戳
    private long timestamp;
    
    public Result() {
        this.timestamp = System.currentTimeMillis();
    }
    
    // 成功响应静态方法
    public static  Result success(T data) {
        Result result = new Result();
        result.setCode(200);
        result.setMessage(\"success\");
        result.setData(data);
        return result;
    }
    
    // 失败响应静态方法
    public static  Result fail(int code, String message) {
        Result 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. 全局异常处理器

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    
    // 处理自定义业务异常
    @ExceptionHandler(BusinessException.class)
    public Result handleBusinessException(BusinessException ex) {
        logger.error(\"业务异常: {}\", ex.getMessage(), ex);
        return Result.fail(ex.getCode(), ex.getMessage());
    }
    
    // 处理JWT相关异常
    @ExceptionHandler(JwtException.class)
    public Result handleJwtException(JwtException ex) {
        logger.error(\"JWT认证异常: {}\", ex.getMessage(), ex);
        return Result.fail(401, \"无效的令牌,请重新登录\");
    }
    
    // 处理参数验证异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result 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 handleException(Exception ex) {
        logger.error(\"系统异常: {}\", ex.getMessage(), ex);
        return Result.fail(500, \"系统内部错误,请稍后重试\");
    }
}

5. 自定义业务异常类

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配置属性类

@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)

@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 roles) {
        Map 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 getRolesFromToken(String token) {
        List roles;
        try {
            Claims claims = getClaimsFromToken(token);
            roles = (List) 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 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)

@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 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 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 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. 过滤器配置类

@Configuration
public class FilterConfig {
    
    @Bean
    public FilterRegistrationBean jwtAuthenticationFilterRegistration(JwtAuthenticationFilter jwtAuthenticationFilter) {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(jwtAuthenticationFilter);
        // 拦截所有请求
        registration.addUrlPatterns(\"/*\");
        // 设置过滤器名称
        registration.setName(\"jwtAuthenticationFilter\");
        // 设置过滤器顺序(越小越先执行)
        registration.setOrder(1);
        return registration;
    }
    
    @Bean
    public FilterRegistrationBean charsetFilterRegistration(CharsetFilter charsetFilter) {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(charsetFilter);
        registration.addUrlPatterns(\"/*\");
        registration.setName(\"charsetFilter\");
        // 字符编码过滤器应该最先执行
        registration.setOrder(0);
        return registration;
    }
}

10. Redis实现Token黑名单

@Component
public class TokenBlacklistService {
    
    @Autowired
    private RedisTemplate 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. 用户实体类

@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 roles;
}

12. 认证控制器

@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> 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 roles = userService.getUserRoles(user.getId());
        
        // 生成JWT令牌
        String token = jwtTokenUtil.generateToken(user.getUsername(), user.getId(), roles);
        
        // 构建响应
        Map 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> 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 result = new HashMap();
        result.put(\"token\", refreshedToken);
        
        return Result.success(result);
    }
    
    /**
     * 用户注销
     */
    @PostMapping(\"/logout\")
    public Result 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> getCurrentUser(HttpServletRequest request) {
        // 从请求属性中获取用户信息(由JwtAuthenticationFilter设置)
        String username = (String) request.getAttribute(\"username\");
        Long userId = (Long) request.getAttribute(\"userId\");
        List roles = (List) request.getAttribute(\"roles\");
        
        Map 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 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. 用户服务接口

public interface UserService {
    
    /**
     * 根据用户名查找用户
     */
    User findByUsername(String username);
    
    /**
     * 保存用户
     */
    void save(User user);
    
    /**
     * 获取用户角色
     */
    List getUserRoles(Long userId);
    
    /**
     * 分配默认角色给用户
     */
    void assignDefaultRole(Long userId);
    
    /**
     * 根据用户ID查找用户
     */
    User findById(Long userId);
}

14. 用户服务实现类

@Service
public class UserServiceImpl implements UserService {
    
    // 这里可以注入数据访问层
    // @Autowired
    // private UserRepository userRepository;
    
    // 模拟用户数据存储
    private static final Map USER_MAP = new ConcurrentHashMap();
    private static final Map<Long, List> 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 getUserRoles(Long userId) {
        // 实际项目中应该从数据库查询用户角色关系
        return USER_ROLES_MAP.getOrDefault(userId, Collections.emptyList());
    }
    
    @Override
    public void assignDefaultRole(Long userId) {
        // 分配默认角色为普通用户
        List 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. 登录和注册请求类

@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. 密码编码器

@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依赖配置

<dependencies>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    
    <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>
    
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    
    
    <dependency>
        <groupId>org.mindrot</groupId>
        <artifactId>jbcrypt</artifactId>
        <version>0.4</version>
    </dependency>
</dependencies>

18. 配置文件 (application.yml)

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. 使用示例

测试控制器(需要认证)

@RestController
@RequestMapping(\"/api/test\")
public class TestController {
    
    /**
     * 需要认证的测试接口
     */
    @GetMapping(\"/protected\")
    public Result protectedResource(HttpServletRequest request) {
        String username = (String) request.getAttribute(\"username\");
        return Result.success(\"你好,\" + username + \"!这是一个需要认证的接口\");
    }
    
    /**
     * 测试用户角色
     */
    @GetMapping(\"/check-role\")
    public Result<List> checkRole(HttpServletRequest request) {
        List roles = (List) 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认证机制。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 开发教程 SpringBoot 开发必备基础工具类实现(纯JWT认证,无SpringSecurity) https://www.zuozi.net/3326.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务