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

news/2025/11/26 16:06:06/文章来源:https://www.cnblogs.com/sun-10387834/p/19267772

一、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;@Overridepublic 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接口执行流程图

graph TDA[前端发起登录请求\nPOST /api/auth/login] --> B[AuthController.login]B --> C[AuthenticationManager.authenticate]C --> D[DaoAuthenticationProvider.authenticate]D --> E[UserDetailsServiceImpl.loadUserByUsername]E --> F[UserMapper.selectOne\n查询用户基础信息]E --> G[RoleMapper.findRolesByUserId\n加载角色权限]D --> H[additionalAuthenticationChecks\n密码校验]H --> I[BCryptPasswordEncoder.matches\n比对密码]D --> J[生成已认证凭证\nUsernamePasswordAuthenticationToken]J --> K[JwtUtils.generateToken\n生成JWT Token]K --> L[返回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接口执行流程图

graph TDA[前端携带Token请求GET /api/order] --> B[JwtAuthFilter.doFilterInternal]B --> C[提取Token并验证]C --> D[设置SecurityContextUsernamePasswordAuthenticationToken]D --> E[DispatcherServlet分发请求]E --> F[OrderController.listOrders@PreAuthorize标注方法]F --> G[MethodSecurityInterceptor.invokeAOP拦截]G --> H[attributeSource.getAttributes获取权限表达式]H --> I[accessDecisionManager.decide授权决策]I --> J[WebExpressionVoter.vote表达式投票]J --> K[SecurityExpressionRoot.hasAuthority解析权限逻辑]K --> L[PermissionChecker.hasPermission校验权限]L --> M[OrderServiceImpl.queryOrders执行业务逻辑]M --> N[返回数据给前端]

三、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配置判断访问权限。 粗粒度权限控制。

3.2 过滤器链配置与自定义

  • 配置位置:SecurityConfig中通过HttpSecurity链式调用配置。
  • 自定义过滤器插入:使用addFilterBefore/After/At方法,如JwtAuthFilter插入到UsernamePasswordAuthenticationFilter之前。

四、核心流程总结

4.1 login接口核心流程

前端请求→AuthController.login()→AuthenticationManager.authenticate()→
DaoAuthenticationProvider.authenticate()→UserDetailsServiceImpl.loadUserByUsername()→
UserMapper.selectOne()→additionalAuthenticationChecks()→BCryptPasswordEncoder.matches()→
生成UsernamePasswordAuthenticationToken→JwtUtils.generateToken()→返回Token

4.2 @PreAuthorize接口核心流程

前端携带Token请求→JwtAuthFilter.doFilterInternal()→设置SecurityContext→
OrderController.listOrders()→MethodSecurityInterceptor.invoke()→
attributeSource.getAttributes()→accessDecisionManager.decide()→
WebExpressionVoter.vote()→SecurityExpressionRoot.hasAuthority()→
PermissionChecker.hasPermission()→OrderServiceImpl.queryOrders()→返回数据

通过上述流程图与源码剖析,可清晰理解Spring Security在认证与授权中的底层逻辑,以及自定义组件与源码组件的协作方式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/977330.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu 系统下,libncurses.so.5 缺失的处理办法

# 在Linux执行命令报错: root@xxx:/home/software/mysql_80# ./mysql -h127.0.0.1 -P3306 -uroot -proot ./mysql: error while loading shared libraries: libncurses.so.5: cannot open shared object file: No suc…

Linux 中判断是否存在连续的空行

001、[root@PC1 test]# ls a.txt b.txt [root@PC1 test]# cat a.txt aaaaaa bbbbbbcccccc dddddd eeeeee ffffff gggggg [root@PC1 test]# awk NF==0{if(prev==1){double=1};prev=1;next}NF>0{prev=0}END{exit !do…

2025 十大手板厂家推荐:技术融合赋能研发新效率

随着制造业数字化转型的深度推进,手板模型行业正迎来技术迭代与需求升级的双重机遇。数据显示,2023 年中国手板模型市场规模已达 186.5 亿元,预计 2025 年将突破 220 亿元,年复合增长率保持 8.7%。医疗设备、消费电…

2025年轻质保温砖实力厂家权威推荐榜单:加气混凝土砖/水泥发泡砖/轻质砖源头厂家精选

在工业窑炉、高温设备与建筑节能领域,轻质保温砖的性能直接影响热能效率与运营成本。据行业数据统计,高性能轻质保温砖可降低设备热损失30%-50%,其导热系数需控制在0.1-0.3W/(mK),同时抗压强度需≥2.5MPa以适应复杂…

CSS命名规范(规则) - 易博奕

CSS命名规范(规则)CSS命名规范(规则) 常用的CSS命名规则 头:header 内容:content/container 尾:footer 导航:nav 侧栏:sidebar 栏目:column 页面外围控制整体佈局宽度:wrapper 左右中:left right center 登…

《通用叙事与共识构建手册》V1.1 精炼版

《通用叙事与共识构建手册》V1.1 精炼版 —— 用于协调人类与人工智能的价值共生协议 序言:导演的诞生 我们正从“信息社会”迈向“叙事社会”。旧时代的工具擅长处理数据,但无力理解意义;善于解答问题,但拙于协调…

基于MATLAB的马尔科夫链蒙特卡洛(MCMC)模拟实现方法

一、MCMC核心算法实现 1. Metropolis-Hastings算法 MATLAB代码示例(目标分布为双峰混合高斯分布): % 参数设置 N = 1e5; % 迭代次数 x = zeros(N,1); % 初始化链 x(1) = 0; % 初始值 mu1 = 1; s…

23、print 和 printf 格式化输出

1、printf 命令 用于格式化并输出数据,它比更基础的 echo 命令更强大和稳定。printf "格式字符串" [参数1] [参数2] ...printf 不会自动添加换行符,除非你在格式字符串中明确使用 \n。特性说明无自动换行 …

Java 虚拟机内存区域划分 - Higurashi

概要 在 Java 8 中,虚拟机内存主要由以下几个部分组成: 程序计数器(Program Counter Register):用于保存当前线程执行的位置,可以看作是当前线程所执行的字节码的行号指示器,当线程被切换后,用来恢复线程执行的…

使用modelsim仿真调用Xilinx IP核的通用方法

使用modelsim仿真调用Xilinx IP核的通用方法 使用modelsim仿真调用Xilinx IP核的通用方法 https://blog.csdn.net/weixin_39789553/article/details/108595621vivado 和 modesim 联合仿真&&快速修改重仿 https…

2025 年 11 月企业管理咨询公司权威推荐榜:战略规划与组织优化专业服务口碑之选

2025 年 11 月企业管理咨询公司权威推荐榜:战略规划与组织优化专业服务口碑之选 在当今复杂多变的商业环境中,企业面临着前所未有的战略挑战与组织变革压力。随着数字化转型加速、市场竞争加剧以及全球化进程深入,企…

2025 年 11 月企业管理咨询公司权威推荐榜:战略规划、组织优化与绩效提升领域的专业服务与口碑之选

2025 年 11 月企业管理咨询公司权威推荐榜:战略规划、组织优化与绩效提升领域的专业服务与口碑之选 在当今快速变化的商业环境中,企业面临着前所未有的挑战与机遇。战略规划、组织优化与绩效提升作为企业管理咨询的核…

自写new

function myNew(constructor, ...args) {// 参数验证if (typeof constructor !== function) {throw new TypeError(myNew: First argument must be a function);}// 1. 创建新对象,继承构造函数的原型const instance …

寺庙小程序开发公司,3家特色寺庙小程序开发公司业务详解:北京小程序/支付宝小程序/抖音小程序/活动小程序全涵盖公司推荐

开头:数字化浪潮下,寺庙小程序的开发价值凸显随着数字化技术与传统场景的深度融合,寺庙作为承载文化传承与精神寄托的重要场所,也亟需通过数字化工具优化服务体验、拓宽传播路径。小程序凭借轻量化、易操作、无需下…

【源码+数据集+训练教程】基于YOLOv8+Flask+Layui的智能垃圾分类检测架构

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025年结实板材源头厂家权威推荐榜单:口碑板材/耐用板材/EO级板材源头厂家精选

在建筑与装饰材料领域,结实板材的耐久性、环保性及适配性直接影响工程质量与使用寿命。行业数据显示,高品质板材可提升装饰工程寿命30%-50%,而环保型板材的市场需求年增长率达15%。本文基于产品物理性能、行业应用覆…

万能欧几里得算法 笔记

\[\newcommand{\floor}[1]{\left\lfloor #1 \right\rfloor} \newcommand{\ceil}[1]{\left\lceil #1 \right\rceil} \renewcommand{\sf}[2]{\begin{bmatrix} #1 \\ #2 \end{bmatrix}} \renewcommand{\ss}[2]{\begin{Bma…

2025年11月中国十大咨询公司权威推荐榜:战略管理、财务顾问与数字化转型顶尖品牌深度解析

2025年11月中国十大咨询公司权威推荐榜:战略管理、财务顾问与数字化转型顶尖品牌深度解析 在当今复杂多变的商业环境中,专业咨询服务已成为企业提升竞争力的关键要素。随着数字化转型浪潮的深入推进,企业对战略管理…

课程小程序开发公司,3家专业课程小程序开发公司能力拆解:抖音小程序/支付宝小程序/微信小程序全涵盖

随着在线教育场景的深度渗透,课程小程序凭借轻量化、即用即走的特性,逐渐成为教育机构连接用户的核心入口。从知识付费到职业培训,从K12辅导到兴趣课程,不同类型的教学需求对小程序的功能、体验与扩展性提出了差异…

四川如何选到专业的PET塑钢打包带生产厂家?求靠谱推荐

四川如何选到专业的PET塑钢打包带生产厂家?求靠谱推荐一、公司介绍:技术积淀与规模实力并存四川省新展星包装制品有限公司是一家专业从事PET塑钢打包带生产的四川打包带厂家,成立于2023年7月25日,前身为深耕行业多…