news 2026/6/12 10:14:19

Spring Security 鉴权流程与过滤器链深度剖析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Security 鉴权流程与过滤器链深度剖析

一、login接口鉴权流程

1.1 流程概述

login接口是用户认证入口,核心是验证用户名密码并生成JWT Token。流程涉及控制器、认证管理器、用户服务、密码编码器、JWT工具和过滤器协同工作。

1.2 详细步骤与代码示例

1.2.1 请求接收(Controller层接口)

组件标注:表现层接口(AuthController.login())

@RestController

@RequestMapping("/api/auth")

@RequiredArgsConstructor

public class AuthController {

private final AuthenticationManager authenticationManager;

private final JwtUtils jwtUtils;

@PostMapping("/login")

public Result<JwtResponse> login(@RequestBody LoginRequest request) {

Authentication authentication = authenticationManager.authenticate(

new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())

);

UserDetails userDetails = (UserDetails) authentication.getPrincipal();

String token = jwtUtils.generateToken(userDetails);

return Result.success(new JwtResponse(token, userDetails.getUsername()));

}

}

@Data class LoginRequest { private String username; private String password; }

@Data class JwtResponse { private String token; private String username; public JwtResponse(String t, String u) { token=t; username=u; } }

1.2.2 触发认证与加载用户信息(Service层)

自定义用户服务实现:

@Service

@RequiredArgsConstructor

public class UserDetailsServiceImpl implements UserDetailsService {

private final UserMapper userMapper;

private final RoleMapper roleMapper;

@Override

public UserDetails loadUserByUsername(String username) {

UserPo user = userMapper.selectOne(new QueryWrapper<UserPo>().eq("username", username));

if (user == null) throw new UsernameNotFoundException("用户不存在");

Set<RolePo> roles = roleMapper.findRolesByUserId(user.getId());

user.setRoles(roles);

return user;

}

}

Spring Security认证管理器源码核心逻辑(ProviderManager):

public class ProviderManager implements AuthenticationManager {

private List<AuthenticationProvider> providers;

public Authentication authenticate(Authentication auth) {

for (AuthenticationProvider p : providers) {

if (p.supports(auth.getClass())) {

Authentication result = p.authenticate(auth);

if (result != null) return result;

}

}

throw new AuthenticationException("认证失败") {};

}

}

1.2.3 密码校验(Util层)

配置类代码:

@Configuration

public class SecurityConfig {

@Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }

}

密码对比源码核心逻辑(DaoAuthenticationProvider):

public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {

protected void additionalAuthenticationChecks(UserDetails ud, UsernamePasswordAuthenticationToken auth) {

String presented = auth.getCredentials().toString();

String encoded = ud.getPassword();

if (!passwordEncoder.matches(presented, encoded)) throw new BadCredentialsException("密码错误");

}

}

BCryptPasswordEncoder源码核心逻辑:

public class BCryptPasswordEncoder implements PasswordEncoder {

public boolean matches(CharSequence raw, String encoded) {

BCrypt.HashData hashData = decode(encoded);

byte[] hashed = BCrypt.hashpw(raw.toString(), hashData);

return constantTimeEquals(hashed, hashData.password);

}

}

1.2.4 生成JWT Token(Util层)

JWT工具类代码:

@Component

public class JwtUtils {

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

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

public String generateToken(UserDetails ud) {

return Jwts.builder().setSubject(ud.getUsername()).setIssuedAt(new Date())

.setExpiration(new Date(System.currentTimeMillis() + expiration))

.signWith(SignatureAlgorithm.HS256, secret).compact();

}

}

1.2.5 后续请求认证(插件层:Filter)

自定义过滤器代码:

@Component

@RequiredArgsConstructor

public class JwtAuthFilter extends OncePerRequestFilter {

private final JwtUtils jwtUtils;

private final UserDetailsServiceImpl userDetailsService;

@Override protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) {

String token = parseJwt(req);

if (token != null && jwtUtils.validateToken(token)) {

String username = jwtUtils.extractUsername(token);

UserDetails ud = userDetailsService.loadUserByUsername(username);

UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(ud, null, ud.getAuthorities());

SecurityContextHolder.getContext().setAuthentication(auth);

}

chain.doFilter(req, res);

}

private String parseJwt(HttpServletRequest req) {

String h = req.getHeader("Authorization");

return (h != null && h.startsWith("Bearer ")) ? h.substring(7) : null;

}

}

1.3 login接口执行流程图

前端发起登录请求\nPOST /api/auth/login

AuthController.login

AuthenticationManager.authenticate

DaoAuthenticationProvider.authenticate

UserDetailsServiceImpl.loadUserByUsername

UserMapper.selectOne\n查询用户基础信息

RoleMapper.findRolesByUserId\n加载角色权限

additionalAuthenticationChecks\n密码校验

BCryptPasswordEncoder.matches\n比对密码

生成已认证凭证\nUsernamePasswordAuthenticationToken

JwtUtils.generateToken\n生成JWT Token

返回Token给前端

二、@PreAuthorize接口鉴权流程

2.1 流程概述

@PreAuthorize是方法级权限控制注解,核心是在方法执行前校验用户权限。流程涉及AOP拦截、权限解析、授权决策三个阶段。

2.2 详细步骤与代码示例

2.2.1 控制器接口标注@PreAuthorize(表现层)

@RestController

@RequestMapping("/api/order")

@RequiredArgsConstructor

public class OrderController {

private final OrderService orderService;

@GetMapping

@PreAuthorize("hasAuthority('order:view')")

public PageResult<OrderVo> listOrders(OrderQuery query) {

return orderService.queryOrders(query);

}

}

2.2.2 AOP拦截与权限表达式解析(插件层)

配置类代码:

@Configuration

@EnableGlobalMethodSecurity(prePostEnabled = true)

public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {

@Override protected MethodSecurityExpressionHandler createExpressionHandler() {

DefaultMethodSecurityExpressionHandler h = new DefaultMethodSecurityExpressionHandler();

h.setPermissionEvaluator(new CustomPermissionEvaluator());

return h;

}

}

MethodSecurityInterceptor源码核心逻辑:

public class MethodSecurityInterceptor implements MethodInterceptor {

public Object invoke(MethodInvocation mi) {

Collection<ConfigAttribute> attrs = attributeSource.getAttributes(mi);

if (attrs == null) return mi.proceed();

Authentication auth = SecurityContextHolder.getContext().getAuthentication();

accessDecisionManager.decide(auth, mi, attrs);

return mi.proceed();

}

}

2.2.3 权限校验逻辑(Service层)

自定义权限检查器:

@Component

public class PermissionChecker {

public boolean hasPermission(String code) {

Authentication auth = SecurityContextHolder.getContext().getAuthentication();

return auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals(code));

}

}

表达式解析源码核心逻辑(SecurityExpressionRoot):

public class SecurityExpressionRoot {

public boolean hasAuthority(String auth) {

return authentication.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals(auth));

}

}

授权决策管理器源码核心逻辑(AffirmativeBased):

public class AffirmativeBased implements AccessDecisionManager {

public void decide(Authentication auth, Object obj, Collection<ConfigAttribute> attrs) {

for (AccessDecisionVoter v : decisionVoters) {

int r = v.vote(auth, obj, attrs);

if (r == ACCESS_GRANTED) return;

}

throw new AccessDeniedException("权限不足");

}

}

2.2.4 业务逻辑执行(Service层)

@Service

@RequiredArgsConstructor

public class OrderServiceImpl implements OrderService {

private final OrderMapper orderMapper;

private final DataScopeService dataScopeService;

public PageResult<OrderVo> queryOrders(OrderQuery q) {

DataScopeService.DataScope scope = dataScopeService.getCurUserDataScope();

LambdaQueryWrapper<OrderPo> w = new LambdaQueryWrapper<>();

if (scope.getScopeType() == 1) w.eq(OrderPo::getCreatorId, scope.getUserId());

else if (scope.getScopeType() == 2) w.eq(OrderPo::getDeptId, scope.getDeptIds().get(0));

Page<OrderPo> p = orderMapper.selectPage(new Page<>(q.getPageNum(), q.getPageSize()), w);

return convertToPageResult(p);

}

}

2.3 @PreAuthorize接口执行流程图

前端携带Token请求GET /api/order

JwtAuthFilter.doFilterInternal

提取Token并验证

设置SecurityContextUsernamePasswordAuthenticationToken

DispatcherServlet分发请求

OrderController.listOrders@PreAuthorize标注方法

MethodSecurityInterceptor.invokeAOP拦截

attributeSource.getAttributes获取权限表达式

accessDecisionManager.decide授权决策

WebExpressionVoter.vote表达式投票

SecurityExpressionRoot.hasAuthority解析权限逻辑

PermissionChecker.hasPermission校验权限

OrderServiceImpl.queryOrders执行业务逻辑

返回数据给前端

三、Spring Security过滤器链详解

3.1 过滤器执行顺序与功能

顺序 过滤器名称 功能描述 使用场景

1 SecurityContextPersistenceFilter 恢复或清理SecurityContext,隔离请求间状态。 所有请求必经,前后端分离可简化。

2 LogoutFilter 处理退出请求,清理认证信息。 需显式退出功能时启用。

3 UsernamePasswordAuthenticationFilter 处理传统用户名密码登录请求。 前后端分离通常替换为自定义登录接口。

4 JwtAuthFilter 自定义过滤器,提取Bearer Token并设置认证信息。 前后端分离核心过滤器,手动配置。

5 AnonymousAuthenticationFilter 为未认证用户分配匿名身份。 区分未登录与已登录用户。

6 ExceptionTranslationFilter 捕获安全异常并转换为HTTP响应(401/403)。 所有异常处理中枢,必配置。

7 FilterSecurityInterceptor URL级权限校验,根据authorizeRequests配置判断访问权限。 粗粒度权限控制。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/11 21:40:51

跨国企业合规痛点:多地区法规差异下的许可统一管理

跨国企业合规痛点&#xff1a;多地区法规差异下的许可统一管理在全球化加速发展的今天&#xff0c;越来越多的企业选择拓展国际市场&#xff0c;走向海外。跨国经营并非一条坦途&#xff0c;在许可合规管理方面&#xff0c;企业常常面临多地区法规差异带来的复杂问题。一个企业…

作者头像 李华
网站建设 2026/6/9 18:42:05

面向AI系统的数据隐私保护测试框架设计与实践

随着《个人信息保护法》和《数据安全法》的深入实施&#xff0c;AI系统数据隐私保护已成为测试工作的核心议题。2025年&#xff0c;全球数据泄露事件同比激增67%&#xff0c;其中AI模型训练与推理环节占比达41%。本文提出覆盖全生命周期的测试方案&#xff0c;帮助测试团队构建…

作者头像 李华
网站建设 2026/6/12 9:40:31

移动端自动化测试:工具选择与实战技巧深度解析

一、移动端自动化测试现状与挑战随着智能设备渗透率持续攀升&#xff0c;移动应用功能复杂度呈指数级增长。截至2025年&#xff0c;全球移动用户日均使用时长突破4小时&#xff0c;这对测试工作提出更高要求。传统手工测试面临三大瓶颈&#xff1a;设备碎片化&#xff08;需覆盖…

作者头像 李华
网站建设 2026/6/10 19:20:57

升级竞价思维:从被动跟随到主动布局的策略进化

在亚马逊广告投放的竞技场中&#xff0c;“系统建议出价”曾像海妖的歌声一般&#xff0c;诱惑着无数卖家踏入高成本低回报的迷雾&#xff0c;直到越来越多的人发现&#xff0c;这一“友好”的工具背后&#xff0c;潜藏着一套精密的算法逻辑——它并非以卖家的利润最大化为目的…

作者头像 李华
网站建设 2026/6/10 10:45:42

汽车行业最严重漏洞:20家知名车企API暴露车主个人信息

汽车行业最严重漏洞&#xff1a;20家知名车企API暴露车主个人信息 近日安全研究人员Sam Curry披露了近20家知名汽车制造商在线服务中的API安全漏洞&#xff0c;这些漏洞可能允许黑客执行恶意活动&#xff0c;包括从解锁、启动、跟踪汽车到窃取客户个人信息。这可能是汽车行业迄…

作者头像 李华
网站建设 2026/6/8 13:29:44

SVN 某个用户 进项目需要常常输入密码

用户输入密码后 大约10多分钟还需要再输入密码&#xff1a; 1. Linux 首先 我查看了 该用户确实有权限 其次开始操作&#xff0c;让该用户进入/home/xx 下 删除隐藏文件 .subversion/ 再开始 操作 svn co 或 svn ls 触发 输入密码的操作&#xff0c;即可&#xff0c;可…

作者头像 李华