Spring Security JWT认证完整实现

一、自定义匿名用户访问返回处理:AnonymousAuthenticationEntryPoint

核心作用

未登录的匿名用户访问需要认证的接口时,替代 Security 默认的「401 页面响应」,返回自定义的 JSON 格式提示(明确告知 “未登录”),适配前后端分离场景(前端需解析 JSON 做跳转或提示)。

java
/**
 * 匿名用户访问处理
 * 核心:拦截匿名用户的未认证请求,返回统一JSON格式的“未登录”提示
 *
 * @author luoyuanxiang
 */
@Slf4j
@Component // 注入Spring容器,供Security配置使用
public class AnonymousAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        // 1. 日志记录:打印访问失败的接口路径和异常信息,方便排查问题
        log.info("用户需要登录,访问[{}]失败,AuthenticationException={}", request.getRequestURI(), authException.getMessage(), authException);
        
        // 2. 跨域配置:允许前端跨域请求(前后端分离必配,否则前端无法解析响应)
        response.setHeader("Access-Control-Allow-Origin", "*");
        // 3. 响应格式:指定返回JSON,避免前端解析乱码
        response.setHeader("Content-type", "application/json;charset=UTF-8");
        
        // 4. 自定义响应体:使用项目统一的Result工具类,状态码424(自定义,区分“未登录”和其他401场景)
        Result<String> object = Result.error(424, "未登录,请登录访问");
        // 5. 写入响应:将Result转为JSON字符串返回
        response.getWriter().print(JSONUtil.toJsonStr(object));
    }
}

二、自定义权限访问异常处理:CustomAccessDeniedHandler

核心作用

已登录但权限不足的用户访问接口时(比如普通用户访问管理员接口),替代 Security 默认的「403 页面响应」,返回自定义 JSON 格式的 “无权限” 提示,适配前后端分离。

java
/**
 * 授权异常处理
 * 核心:拦截已登录用户的“权限不足”请求,返回统一JSON格式的“无权限”提示
 *
 * @author luoyuanxiang
 */
@Component // 注入Spring容器,供Security配置使用
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        // 1. 自定义响应体:状态码403(HTTP标准“禁止访问”码),提示“无权限”
        Result<Object> error = Result.error(403, "无权限访问");
        
        // 2. 跨域与响应格式配置:同匿名用户处理,确保前端能解析
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Content-type", "application/json;charset=UTF-8");
        
        // 3. 写入响应:返回JSON
        response.getWriter().print(JSONUtil.toJsonStr(error));
    }
}

三、自定义跳过认证注解类和 AOP 实现

3.1 免认证注解:NoAuth

核心作用

定义一个「标记注解」,用于标注不需要登录就能访问的接口 / 控制器(比如登录接口、注册接口、验证码接口),替代传统的 “在 Security 配置中硬编码白名单路径”,更灵活易维护。

java
/**
 * 无需认证token注解
 * 核心:标记接口/控制器,告知Security“该路径免登录访问”
 *
 * @author luoyuanxiang
 */
@Documented // 生成API文档时,显示该注解
@Retention(RetentionPolicy.RUNTIME) // 注解保留到运行时(必须,因为需要在运行时扫描)
@Target({ElementType.METHOD, ElementType.TYPE}) // 注解可用于:方法(单个接口)、类(整个控制器)
public @interface NoAuth {
}

3.2 免认证路径收集:RequestMappingCollector

核心作用

通过实现 Spring 的 BeanPostProcessor 接口,在项目启动时自动扫描所有加了 @NoAuth 注解的接口路径,收集成 “免认证白名单”,供 Security 配置使用(避免手动写死白名单)。

四、自定义实现登录用户信息:SecurityUser

核心作用

继承 Security 提供的 User 类(实现 UserDetails 接口),扩展存储项目自定义的用户实体(UserEntity) —— 因为 Security 默认的 User 只包含用户名、密码、权限,无法满足业务需求(比如需要用户 ID、昵称等)。

java
/**
 * 登录用户详情
 * 核心:扩展Security的User类,关联项目自定义的UserEntity,存储完整用户信息
 *
 * @author luoyuanxiang
 */
@Getter
@Setter
public class SecurityUser extends User { // 继承Security的User,自动实现UserDetails接口

    // 扩展字段:关联项目自定义的用户实体(存储用户ID、昵称、角色等业务字段)
    private UserEntity userEntity;

    // 构造方法:调用父类构造(传递Security必需的用户名、密码、权限)
    public SecurityUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
        super(username, password, authorities);
    }
}

五、自定义实现 UserDetailsService 接口:UserDetailsServiceImpl

核心作用

实现 Security 的 UserDetailsService 接口,是用户认证的核心数据源—— 当用户登录时,Security 会调用该类的 loadUserByUsername 方法,从数据库查询用户信息,供后续密码校验和权限赋值。

六、JWT 工具类和 JWT 过滤器实现

6.1 JWT 工具类:JwtUtils

核心作用

封装 JWT 的核心操作:生成 Token(登录成功后返回给前端)、解析 Token(从请求头提取用户信息)、验证 Token(是否过期、签名是否合法),是前后端分离认证的 “核心工具”。

6.2 JWT 过滤器:JwtAuthenticationFilter

核心作用

继承 Security 的 OncePerRequestFilter(确保每次请求只执行一次),拦截所有请求,从请求头提取 JWT Token,验证合法性后将用户信息存入 SecurityContext(让 Security 后续能识别 “当前用户已登录”)。

七、Spring Security 配置:SecurityConfig

核心作用

Security 的核心配置类:整合上述所有自定义组件(过滤器、异常处理器、白名单),配置认证授权规则(哪些路径免认证、哪些需权限)、跨域、会话管理等,是整个 Security 流程的 “总指挥”。

八、登录实现:AuthController

核心作用

提供自定义登录接口/login)和 Token 校验接口(/check),是用户触发认证流程的入口:接收前端传入的用户名密码,调用 Security 的 AuthenticationManager 校验,成功后生成 JWT 返回给前端。

整体流程总结

  1. 未登录访问需认证接口:JWT 过滤器未提取到有效 Token → Security 触发 AnonymousAuthenticationEntryPoint → 返回 “未登录” JSON。
  2. 已登录但权限不足:JWT 验证通过,但用户权限不匹配 → Security 触发 CustomAccessDeniedHandler → 返回 “无权限” JSON。
  3. 登录流程:前端调用 /login(@NoAuth 免认证)→ 传入用户名密码 → AuthenticationManager 校验 → 成功生成 JWT → 返回给前端。
  4. 已登录访问接口:前端在 Authorization 头携带 JWT → JWT 过滤器解析验证 Token → 合法则设置认证信息 → Security 允许访问接口。
线程池参数设置最佳实践
GitHub Actions自动化部署Spring Boot至阿里云镜像仓库

评论区

评论加载中...