SpringBoot4.0.6 + Security7.x + JWT 最新完整实战|无状态权限认证、统一异常处理、可直接落地

一、项目概述

本项目基于当前主流稳定版本栈 Spring Boot4.0.6 + Spring Security7.x + JWT 开发,实现前后端分离无状态权限认证体系,适配现代企业项目架构。

核心实现功能:

  • URL级别权限控制:白名单放行公开接口,拦截所有需要登录的接口

  • 方法级别细粒度权限:基于 @EnableMethodSecurity 实现角色权限校验

  • 优雅用户体系:内存模拟用户,满足学习测试,无缝切换数据库生产模式

  • 精准异常处理:区分用户名密码错误、Token过期、权限不足等场景

项目核心优势:解耦性强、扩展性高、完全贴合行业主流规范,学习、落地两用。

二、技术环境

本次实战采用主流稳定技术栈,所有依赖版本统一适配,避免版本冲突:

  • JDK 21

  • Spring Boot 4.0.6

  • Spring Security 7.x(Spring Boot4.0.6 内置自带)

  • SpringDoc OpenAPI(接口文档支持,适配SpringBoot4.x)

  • Jwt 工具依赖

  • Maven 3.9+(项目构建管理)

  • Lombok(简化代码开发)

三、项目搭建与核心功能实现

(一)项目初始化

通过IDEA或Spring官网初始化项目,可直接勾选官方自带依赖:Spring Web、Spring Security、Lombok。

重点说明:

  1. Lombok 属于 Spring Boot 官方初始化依赖,可直接勾选使用;

  2. JWT 无官方启动器,初始化页面无法勾选,需要手动在 pom.xml 引入依赖。

(二)手动引入JWT依赖

Spring Boot 官方无 JWT 启动依赖,项目创建后手动在 pom.xml 引入0.12.6 为适配 JDK17+、SpringBoot4.x 生态的稳定版本,可完美兼容本次 JDK21、SpringBoot4.0.6 开发环境,采用 Maven 版本统一管理方式,方便后续升级维护:

XML 复制代码
<!-- 统一版本管理 -->

<properties>

    <jjwt.version>0.12.6</jjwt.version>

</properties>


<!-- JWT 令牌生成与解析工具 -->

<dependency>

    <groupId>io.jsonwebtoken</groupId>

    <artifactId>jjwt-api</artifactId>

    <version>${jjwt.version}</version>

</dependency>

<dependency>

    <groupId>io.jsonwebtoken</groupId>

    <artifactId>jjwt-impl</artifactId>

    <version>${jjwt.version}</version>

    <scope>runtime</scope>

</dependency>

<dependency>

    <groupId>io.jsonwebtoken</groupId>

    <artifactId>jjwt-jackson</artifactId>

    <version>${jjwt.version}</version>

    <scope>runtime</scope>

</dependency>

依赖引入完成后,再编写全局安全配置,开启Web安全、方法级权限校验,配置接口白名单,实现基础认证拦截规则:

java 复制代码
/**
 * Spring Security 7.x 企业级标准配置(JWT 无状态)
 */
@Configuration
@EnableWebSecurity //开启 URL 级安全
@EnableMethodSecurity //开启 方法级细粒度权限
@RequiredArgsConstructor
public class SecurityConfig {

    private final JwtAuthFilter jwtAuthFilter;
    private final JwtAuthenticationEntryPoint entryPoint;
    private final JwtAccessDeniedHandler accessDeniedHandler;

    private static final String[] WHITE_LIST = {"/auth/**", "/test/hello", "/favicon.ico", "/swagger-ui/**", "/v3/api-docs/**", "/swagger-ui.html"};

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        // 1. 跨域

        // 2. 关闭不需要的功能
        http.csrf(AbstractHttpConfigurer::disable);
        http.formLogin(AbstractHttpConfigurer::disable);
        http.httpBasic(AbstractHttpConfigurer::disable);

        // 3. 无状态Session
        http.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

        // 4. 权限配置
        http.authorizeHttpRequests(auth -> auth.requestMatchers(WHITE_LIST).permitAll().anyRequest().authenticated());

        // 5. 异常处理
        http.exceptionHandling(ex -> ex.authenticationEntryPoint(entryPoint).accessDeniedHandler(accessDeniedHandler));

        // 6. JWT过滤器
        http.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
        return authConfig.getAuthenticationManager();
    }
}

核心规则:白名单接口全员可访问,其余所有接口必须登录认证。

(三)自定义用户认证逻辑

本文通过实现 UserDetailsService 接口自定义登录认证逻辑,采用内存用户的方式方便本地测试学习,后续对接数据库,只需修改查询逻辑即可,无需改动全局配置。

java 复制代码
/**
 * 统一用户查询服务
 * 学习阶段:内存用户
 * 生产阶段:只改此类查数据库,其他代码完全不动
 */
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {

    private final PasswordEncoder passwordEncoder;

    /**
     * 统一入口:Spring Security 自动调用这里查询用户
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        return switch (username) {
            case "user" -> User.withUsername("user")
                    .password(passwordEncoder.encode("123456"))
                    .roles("USER")
                    .build();

            case "admin" -> User.withUsername("admin")
                    .password(passwordEncoder.encode("admin123"))
                    .roles("ADMIN")
                    .build();

            default -> throw new UsernameNotFoundException("用户名或密码错误");
        };
    }
}

(四)JWT工具类实现

本次使用 JJWT 0.12.6 版本,统一采用最新解析方式,工具类负责 Token 生成、解析、过期判断,适配 JDK21 高版本环境:

java 复制代码
@Component
public class JwtUtil {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration}")
    private Long expiration;

    private SecretKey getKey() {
        return Keys.hmacShaKeyFor(secret.getBytes());
    }

    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    private Claims extractAllClaims(String token) {
        return Jwts.parser()
                .verifyWith(getKey())
                .build()
                .parseSignedClaims(token)
                .getPayload();
    }

    public String generateToken(UserDetails userDetails) {
        return Jwts.builder()
                .subject(userDetails.getUsername())
                .issuedAt(new Date())
                .expiration(new Date(System.currentTimeMillis() + expiration))
                .signWith(getKey())
                .compact();
    }

    public Boolean validateToken(String token, UserDetails userDetails) {
        final String username = extractUsername(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }

    private Boolean isTokenExpired(String token) {
        return extractClaim(token, Claims::getExpiration).before(new Date());
    }
}

(五)JWT认证拦截过滤器

JWT 过滤器是前后端分离认证的核心组件,用于拦截所有请求,自动解析请求头中的 Token,完成用户身份认证,无需手动登录,实现无状态认证。

执行流程:

  1. 拦截所有HTTP请求

  2. 判断请求头是否携带 Token

  3. 校验 Token 合法性、是否过期

  4. 解析用户名,查询用户信息存入上下文

  5. 放行请求,后续权限拦截器可直接获取登录用户

java 复制代码
@Component
public class JwtAuthFilter extends OncePerRequestFilter {

    private final JwtUtil jwtUtil;
    private final UserDetailsService userDetailsService;

    public JwtAuthFilter(JwtUtil jwtUtil, UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
        this.jwtUtil = jwtUtil;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws ServletException, IOException {
        String header = request.getHeader(JwtConstants.AUTH_HEADER);
        if (header == null || !header.startsWith(JwtConstants.BEARER_PREFIX)) {
            chain.doFilter(request, response);
            return;
        }

        String token = header.substring(7);
        String username = jwtUtil.extractUsername(token);

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails user = userDetailsService.loadUserByUsername(username);

            if (jwtUtil.validateToken(token, user)) {
                UsernamePasswordAuthenticationToken auth =
                        new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(auth);
            }
        }
        chain.doFilter(request, response);
    }
}

​​​​​​​(六)自定义统一返回结果工具

为适配前后端分离开发规范,统一所有接口、异常场景的返回数据格式,自定义通用 Result 响应工具类,封装成功、失败、401未登录、403权限不足等常用返回方法,保证项目响应格式统一、前端对接更便捷:

java 复制代码
@Data
public class Result<T> {

    private int code;
    private String msg;
    private T data;

    public static <T> Result<T> success() {
        return success(null);
    }

    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.setCode(200);
        result.setMsg("成功");
        result.setData(data);
        return result;
    }

    public static <T> Result<T> fail(int code, String msg) {
        Result<T> result = new Result<>();
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }

    public static <T> Result<T> unauthorized(String msg) {
        return fail(401, msg);
    }

    public static <T> Result<T> unauthorized() {
        return fail(401, "未登录或Token已过期");
    }

    public static <T> Result<T> forbidden(String msg) {
        return fail(403, msg);
    }

    public static <T> Result<T> forbidden() {
        return fail(403, "权限不足");
    }
}

该工具类统一项目所有接口、异常场景的返回格式,后续所有自定义异常处理器、业务接口均可复用,实现全站标准化JSON响应,极大降低前端对接成本。

(七)自定义权限不足异常处理器

自定义授权失败处理器,专门处理403 权限不足场景,统一返回标准化JSON结果,替换框架默认异常页面,和登录异常处理器形成完整异常闭环:

java 复制代码
/**
 * 权限不足
 */
@Component
@RequiredArgsConstructor
public class JwtAccessDeniedHandler implements AccessDeniedHandler {

    private final JsonMapper jsonMapper;

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(jsonMapper.writeValueAsString(Result.forbidden(accessDeniedException.getMessage())));
    }
}

​​​​​​​(八)自定义登录认证异常处理器

捕获登录认证异常,返回标准化JSON结果,替换原生异常页面:

java 复制代码
/**
 * 未登录
 */
@Component
@RequiredArgsConstructor
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {

    private final JsonMapper jsonMapper;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(jsonMapper.writeValueAsString(Result.unauthorized(authException.getMessage())));
    }
}

​​​​​​​(九)登录接口控制器(获取Token)

提供账号密码登录接口,校验用户信息成功后,通过JWT工具类生成并返回Token,是整套认证体系的入口:

java 复制代码
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthController {

    private final JwtUtil jwtUtil;
    private final AuthenticationManager authenticationManager;

    @PostMapping("/login")
    public Result<Map<String, String>> login(@RequestBody LoginDto vo) {
        // 1. 手动认证
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(vo.getUsername(), vo.getPassword())
        );

        // 2. 获取用户信息
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();

        // 3. 生成 token
        String token = jwtUtil.generateToken(userDetails);

        Map<String, String> map = new HashMap<>();
        map.put("token", token);
        return Result.success(map);
    }
}

​​​​​​​(十)多场景权限测试控制器

包含公开接口、全员登录接口、管理员专属接口,完整测试权限体系:

java 复制代码
@RestController
@RequestMapping("/test")
public class TestController {
    /**
     * 放行接口(白名单)
     *
     * @return 结果
     */
    @GetMapping("/hello")
    public Result<String> hello() {
        return Result.success("你无需登录!");
    }

    /**
     * 需要登录,但不需要角色(所有登录用户都能访问)
     *
     * @return 结果
     */
    @GetMapping("/auth")
    public Result<String> auth() {
        return Result.success("你已登录!");
    }

    @PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/admin")
    public Result<String> admin() {
        return Result.success("管理员页面!");
    }
}

​​​​​​​四、项目启动与功能测试

启动项目后,可直接通过SpringDoc 在线接口文档完成全部接口自测,无需借助Postman、ApiFox等第三方工具,操作简单直观,适配本项目所有测试场景,

具体测试效果如下:

(一)公开接口测试

访问 /test/hello,无需Token,直接返回成功结果。

(二)登录认证测试

通过 /auth/login 接口登录:

  • 普通用户:user / 123456

  • 管理员用户:admin / admin123

输入错误用户名/密码,返回:用户名或密码错误;无Token访问受限接口,返回:未登录或Token已过期。

(三)权限细分测试

  • user用户登录:可访问 /test/auth,访问 /test/admin 提示403权限不足

  • admin用户登录:可正常访问所有登录后接口

(四)核心注意事项

  1. 用户认证写法说明

本文采用UserDetailsService 接口实现用户认证,相比配置类硬编码用户的方式,结构更清晰,方便后续迭代优化,适配后续数据库用户开发场景。

  1. 跨域配置规范

前后端分离/微服务项目,后端业务服务不配置CORS,跨域统一由网关或Nginx处理,避免配置冗余、跨域冲突。

  1. 权限层级规范

采用「URL全局拦截 + 方法细粒度校验」双层权限体系:全局白名单拦截基础登录权限,@PreAuthorize 实现角色、权限细分,贴合企业级权限设计思想。

  1. 全局异常处理规范

区分两大安全异常场景,配置双异常处理器,实现精准统一响应:

  1. AuthenticationEntryPoint:处理未登录、Token错误、Token过期等401认证异常;

  2. AccessDeniedHandler:处理已登录但权限不足的403授权异常;

彻底摒弃框架默认异常页面,前后端分离项目统一返回JSON格式,适配前端状态码处理逻辑。

禁止所有认证异常统一返回"未登录",必须区分密码错误、Token过期、权限不足等场景,提升前端交互体验,符合项目开发规范。

五、配套源码

本项目为纯净实战版源码,所有配置贴合 Spring Boot4.0.6 + Security7.x 官方规范。

源码包含完整模块:

  • 安全配置模块(权限拦截、密码加密、方法权限开启)

  • 用户认证模块(自定义用户体系、适配生产迭代)

  • JWT登录模块(Token生成、过滤器拦截、认证校验)

  • 统一响应模块(标准化返回结果)

  • 全局异常处理模块(登录异常+权限异常双处理器)

  • 全覆盖测试接口模块

源码可直接运行测试,后期仅需修改用户查询逻辑,即可快速迭代为数据库持久化的企业正式项目,无需重构全局代码。

本文配套全套可运行源码已上传CSDN,包含文中所有配置类、工具类、控制器、异常处理器,下载即可直接启动测试。

源码链接:[SpringBoot4.0.6+Security7.x+JWT 完整实战源码(可直接运行)](https://download.csdn.net/download/m0_67605733/92916509 "SpringBoot4.0.6+Security7.x+JWT 完整实战源码(可直接运行)")

适合日常学习、二次开发、项目基础模板搭建、毕设后端架构复用。

六、项目总结

本文基于 Spring Boot4.0.6、Spring Security7.x、JJWT0.12.6 稳定技术栈,完成了一套规范、可落地的前后端分离无状态权限认证方案。项目完整实现了 JWT 令牌生成、请求拦截认证、URL 权限管控、方法级细粒度授权、401/403 精准异常处理等核心功能,同时依托 SpringDoc 实现零第三方工具自测。整体代码兼顾学习实用性与项目扩展性,仅需改造用户查询逻辑,即可快速迭代为数据库持久化的生产级权限项目。

相关推荐
DIY源码阁1 小时前
JavaSwing宿舍管理系统 - MySQL版
java·数据库·mysql·eclipse
Han_han9191 小时前
递归相关题目:
java
kTR2hD1qb2 小时前
Claude Code Skill的介绍与使用
java·前端·数据库·人工智能
汤米粥2 小时前
python学习——核心语法三
java·python·学习
basketball6162 小时前
Kadane算法 C++实现
java·c++·算法
一 乐2 小时前
汽车租赁|基于SprinBoot+vue的汽车租赁管理系统(源码+数据库+文档)
数据库·vue.js·spring boot·汽车·论文·毕设·汽车租赁管理系统
better_liang2 小时前
每日Java面试场景题知识点之-如何设计分布式锁
java·redis·zookeeper·面试·分布式锁
战族狼魂2 小时前
集 “自动飞行、智能识别、实时预警、勤务联动” 于一体的高速公路应急车道无人机检测系统方案
java·人工智能·大模型·无人机
一只鹿鹿鹿2 小时前
信息化项目管理规范(参考Word文件)
java·大数据·运维·开发语言·数据库