一、项目概述
本项目基于当前主流稳定版本栈 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。
重点说明:
-
Lombok 属于 Spring Boot 官方初始化依赖,可直接勾选使用;
-
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,完成用户身份认证,无需手动登录,实现无状态认证。
执行流程:
-
拦截所有HTTP请求
-
判断请求头是否携带 Token
-
校验 Token 合法性、是否过期
-
解析用户名,查询用户信息存入上下文
-
放行请求,后续权限拦截器可直接获取登录用户
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用户登录:可正常访问所有登录后接口
(四)核心注意事项
- 用户认证写法说明
本文采用UserDetailsService 接口实现用户认证,相比配置类硬编码用户的方式,结构更清晰,方便后续迭代优化,适配后续数据库用户开发场景。
- 跨域配置规范
前后端分离/微服务项目,后端业务服务不配置CORS,跨域统一由网关或Nginx处理,避免配置冗余、跨域冲突。
- 权限层级规范
采用「URL全局拦截 + 方法细粒度校验」双层权限体系:全局白名单拦截基础登录权限,@PreAuthorize 实现角色、权限细分,贴合企业级权限设计思想。
- 全局异常处理规范
区分两大安全异常场景,配置双异常处理器,实现精准统一响应:
-
AuthenticationEntryPoint:处理未登录、Token错误、Token过期等401认证异常;
-
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 实现零第三方工具自测。整体代码兼顾学习实用性与项目扩展性,仅需改造用户查询逻辑,即可快速迭代为数据库持久化的生产级权限项目。