Spring Security认证授权深度解析

一 Spring Security简介

Spring Security是Spring生态系统中的一个安全框架,主要用于处理认证(Authentication)和授权(Authorization)。它提供了一套完整的安全解决方案,可以轻松集成到Spring应用中。

二 核心概念

1. 认证(Authentication)

验证用户的身份,确认"你是谁"。例如:用户登录过程。

2. 授权(Authorization)

验证用户是否有权限执行某个操作,确认"你能做什么"。例如:检查用户是否有权访问某个API。

3. 主要组件

  • SecurityContextHolder:存储安全上下文信息
  • Authentication:存储当前用户的认证信息
  • UserDetails:用户信息的核心接口
  • UserDetailsService:加载用户信息的核心接口
  • AuthenticationProvider:认证的具体实现者

三 实战案例:基于JWT的认证授权系统

一、核心架构

1. 核心组件关系图

请求 → SecurityFilterChain → (多个Security Filter) → 目标资源↓SecurityContextHolder↓SecurityContext↓Authentication/            \Principal     GrantedAuthority

2. 核心组件说明

2.1 SecurityContextHolder
  • 作用:存储当前线程的安全上下文信息
  • 实现:使用ThreadLocal存储SecurityContext
  • 访问方式:
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
2.2 SecurityContext
  • 作用:持有Authentication对象和其他安全相关信息
  • 生命周期:请求开始到结束
  • 存储位置:ThreadLocal或Session中
2.3 Authentication
  • 作用:存储用户认证信息
  • 主要属性:
    • principal:用户身份信息
    • credentials:凭证信息(如密码)
    • authorities:用户权限集合
    • authenticated:是否已认证
  • 常用实现:UsernamePasswordAuthenticationToken

二、认证流程详解

1. 完整认证流程图

用户请求登录↓
UsernamePasswordAuthenticationFilter↓
AuthenticationManager↓
AuthenticationProvider↓
UserDetailsService↓
UserDetails↓
Authentication对象↓
SecurityContext

2. 详细流程说明

2.1 认证入口(以登录为例)
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {// 1. 创建未认证的AuthenticationAuthentication authentication = new UsernamePasswordAuthenticationToken(loginRequest.getUsername(),loginRequest.getPassword());// 2. 执行认证Authentication authenticated = authenticationManager.authenticate(authentication);// 3. 认证成功,生成JWTSecurityContextHolder.getContext().setAuthentication(authenticated);String jwt = tokenProvider.generateToken(authenticated);return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
}
2.2 认证管理器(AuthenticationManager)
public class ProviderManager implements AuthenticationManager {private List<AuthenticationProvider> providers;@Overridepublic Authentication authenticate(Authentication authentication) {// 遍历所有Provider尝试认证for (AuthenticationProvider provider : providers) {if (!provider.supports(authentication.getClass())) {continue;}try {return provider.authenticate(authentication);} catch (AuthenticationException e) {// 处理认证异常}}throw new AuthenticationException("无法认证");}
}
2.3 自定义认证提供者
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {private final CustomUserDetailsService userDetailsService;private final PasswordEncoder passwordEncoder;@Overridepublic Authentication authenticate(Authentication authentication) {// 1. 获取认证信息String username = authentication.getName();String password = authentication.getCredentials().toString();// 2. 加载用户信息UserDetails userDetails = userDetailsService.loadUserByUsername(username);// 3. 验证密码if (!passwordEncoder.matches(password, userDetails.getPassword())) {throw new BadCredentialsException("密码错误");}// 4. 创建已认证的Authenticationreturn new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());}
}

三、授权流程详解

1. 授权流程图

请求 → FilterSecurityInterceptor↓SecurityContextHolder获取Authentication↓AccessDecisionManager↓AccessDecisionVoter↓权限判断结果

2. 详细授权步骤

2.1 配置安全规则
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) {http.authorizeRequests()// 1. URL级别的权限控制.antMatchers("/api/public/**").permitAll().antMatchers("/api/admin/**").hasRole("ADMIN")// 2. 自定义权限判断.anyRequest().access("@customSecurityService.hasPermission(request,authentication)");}
}
2.2 方法级别权限控制
@Service
public class UserService {// 使用Spring EL表达式进行权限控制@PreAuthorize("hasRole('ADMIN') or #username == authentication.name")public UserDetails getUser(String username) {// 方法实现}
}

四、JWT集成原理

1. JWT认证流程

请求 → JwtAuthenticationFilter↓提取JWT令牌↓验证JWT有效性↓解析用户信息↓创建Authentication↓存入SecurityContext

2. JWT过滤器实现

public class JwtAuthenticationFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain filterChain) {try {// 1. 从请求中提取JWTString jwt = getJwtFromRequest(request);if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {// 2. 从JWT中获取用户信息String username = tokenProvider.getUsernameFromJWT(jwt);String roles = tokenProvider.getRolesFromJWT(jwt);// 3. 创建AuthenticationList<GrantedAuthority> authorities = Arrays.stream(roles.split(",")).map(SimpleGrantedAuthority::new).collect(Collectors.toList());UsernamePasswordAuthenticationToken authentication =new UsernamePasswordAuthenticationToken(username, null, authorities);// 4. 设置认证信息SecurityContextHolder.getContext().setAuthentication(authentication);}} catch (Exception ex) {logger.error("无法设置用户认证", ex);}filterChain.doFilter(request, response);}
}

五、数据校验流程

1. 请求数据校验

@PostMapping("/login")
public ResponseEntity<?> login(@Valid @RequestBody LoginRequest request) {// 1. @Valid触发数据校验// 2. 校验失败抛出MethodArgumentNotValidException
}public class LoginRequest {@NotBlank(message = "用户名不能为空")private String username;@NotBlank(message = "密码不能为空")private String password;
}

2. 认证数据校验

public class CustomUserDetailsService implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String username) {// 1. 检查用户是否存在UserPrincipal user = userMap.get(username);if (user == null) {throw new UsernameNotFoundException("用户不存在");}// 2. 检查用户状态if (!user.isEnabled()) {throw new DisabledException("用户已禁用");}// 3. 检查账户是否过期if (!user.isAccountNonExpired()) {throw new AccountExpiredException("账户已过期");}// 4. 检查账户是否锁定if (!user.isAccountNonLocked()) {throw new LockedException("账户已锁定");}return user;}
}

3. JWT数据校验

public class JwtTokenProvider {public boolean validateToken(String authToken) {try {// 1. 验证签名Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);// 2. 验证是否过期Claims claims = getClaimsFromJWT(authToken);return !claims.getExpiration().before(new Date());} catch (SignatureException ex) {logger.error("无效的JWT签名");} catch (MalformedJwtException ex) {logger.error("无效的JWT令牌");} catch (ExpiredJwtException ex) {logger.error("JWT令牌已过期");} catch (UnsupportedJwtException ex) {logger.error("不支持的JWT令牌");} catch (IllegalArgumentException ex) {logger.error("JWT声明为空");}return false;}
}

六、异常处理流程

1. 认证异常处理

@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request,HttpServletResponse response,AuthenticationException e) throws IOException {// 1. 未认证异常处理response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);response.setContentType(MediaType.APPLICATION_JSON_VALUE);String message = "请先进行认证";if (e instanceof BadCredentialsException) {message = "用户名或密码错误";} else if (e instanceof JwtExpiredTokenException) {message = "token已过期";}response.getWriter().write(new ObjectMapper().writeValueAsString(new ApiResponse(false, message)));}
}

2. 授权异常处理

@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request,HttpServletResponse response,AccessDeniedException e) throws IOException {// 1. 权限不足异常处理response.setStatus(HttpServletResponse.SC_FORBIDDEN);response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.getWriter().write(new ObjectMapper().writeValueAsString(new ApiResponse(false, "没有足够的权限")));}
}

七、安全上下文传递

1. 异步方法中的安全上下文

@Async
public CompletableFuture<String> asyncMethod() {// 1. 获取当前安全上下文SecurityContext context = SecurityContextHolder.getContext();return CompletableFuture.supplyAsync(() -> {try {// 2. 设置安全上下文到新线程SecurityContextHolder.setContext(context);// 3. 执行业务逻辑return "success";} finally {// 4. 清理安全上下文SecurityContextHolder.clearContext();}});
}

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

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

相关文章

[学成在线]10-课程审核

课程审核 需求分析 根据模块需求分析&#xff0c;课程发布前要先审核&#xff0c;审核通过方可发布。下图是课程审核及发布的流程图&#xff1a; 为什么课程审核通过才可以发布呢&#xff1f; 这样做为了防止课程信息有违规情况&#xff0c;课程信息不完善对网站用户体验也不…

【SpringBoot + MyBatis + MySQL + Thymeleaf 的使用】

目录&#xff1a; 一&#xff1a;创建项目二&#xff1a;修改目录三&#xff1a;添加配置四&#xff1a;创建数据表五&#xff1a;创建实体类六&#xff1a;创建数据接口七&#xff1a;编写xml文件八&#xff1a;单元测试九&#xff1a;编写服务层十&#xff1a;编写控制层十一…

Elasticsearch索引的字段映射

目录 type String类型 text和keyword的区别 数值类型 日期类型 index doc_values fields analyzer store index_options ElasticSearch索引映射示例 Elasticsearch中的字段设置直接影响数据的存储、索引和查询行为。结合索引查询场景合理设置mapping信息可以起到提…

【已解决】实际参数列表和形式参数列表长度不同、java: 无法将类xxx中的构造器xxx应用到给定类型| lombok相关

idea运行maven项目时&#xff0c;报错这个&#xff08;如标题&#xff09; 解决方案记录&#xff1a; 找到了之前的、能运行成功不报错的 maven项目。参考其pom.xml文件中lombok相关部分&#xff0c;将<path>标签下的lombok加个版本号&#xff0c;就运行成功了&#xff1…

4. 学习网站:学习新的技能或培养兴趣爱好

文章目录 前言英文网站&#xff1a;培养兴趣爱好的应用总结 前言 学习网站以及具体提供的内容。 英文网站&#xff1a; Coursera&#xff1a;提供来自全球顶尖大学和机构的在线课程&#xff0c;涵盖广泛的学科&#xff0c;包括编程、数据科学、商业和艺术等。Udemy&#xff1…

LabVIEW 开发中 TCP 与 UDP 协议的差异

在 LabVIEW 开发的网络通信场景中&#xff0c;TCP 和 UDP 是常用的两种传输层协议&#xff0c;它们各自具有独特的特点&#xff0c;适用于不同的应用需求。理解它们之间的差异&#xff0c;有助于开发者根据具体项目需求选择合适的协议&#xff0c;以实现高效、稳定的网络通信。…

04-深入解析 Spring 事务管理原理及源码

深入解析 Spring 事务管理原理及源码 Spring 事务管理&#xff08;Transaction Management&#xff09;是企业级应用开发中至关重要的功能之一&#xff0c;它确保数据操作的 原子性、一致性、隔离性、持久性&#xff08;ACID&#xff09;。 本篇博客将从 Spring 事务的基本概…

【Linux】用户向硬件寄存器写入值过程理解

思考一下&#xff0c;当我们咋用户态向寄存器写入一个值&#xff0c;这个过程是怎么样的呢&#xff1f;以下是应用程序通过标准库函数&#xff08;如 write()、ioctl() 或 mmap()&#xff09;向硬件寄存器写入值的详细过程&#xff0c;从用户空间到内核再到硬件的完整流程&…

自动驾驶02:点云预处理——02(运动补偿篇LIO-SAM)

当激光雷达&#xff08;LiDAR&#xff09;在运动中采集点云时&#xff0c;每个点的时间戳不同&#xff0c;而车辆在移动&#xff0c;导致点云在不同时间点的坐标与实际情况不符&#xff0c;这种现象称为运动畸变&#xff08;Motion Distortion&#xff09;。为了得到无畸变的点…

基础算法篇(3)(蓝桥杯常考点)—图论

前言 这期是基础算法篇的第三节&#xff0c;其中的dijkstra算法更是蓝桥杯中的高频考点 图的基本相关概念 有向图和无向图 自环和重边 稠密图和稀疏图 对于不带权的图&#xff0c;一条路径的路径长度是指该路径上各边权值的总和 对于带权的图&#xff0c;一条路径长度时指该路…

Crawl4AI:专为AI设计的开源网页爬虫工具,释放大语言模型的潜能

在当今数据驱动的AI时代,高效获取结构化网页数据是模型训练和应用落地的关键。Crawl4AI作为一款专为大型语言模型(LLMs)设计的开源爬虫工具,凭借其极速性能、AI友好输出和模块化设计,正在成为开发者社区的热门选择。本文将深入解析其核心特性与技术优势。 一、Crawl4AI的核…

前后端数据序列化:从数组到字符串的旅程(附优化指南)

&#x1f310; 前后端数据序列化&#xff1a;从数组到字符串的旅程&#xff08;附优化指南&#xff09; &#x1f4dc; 背景&#xff1a;为何需要序列化&#xff1f; 在前后端分离架构中&#xff0c;复杂数据类型&#xff08;如数组、对象&#xff09;的传输常需序列化为字符…

汇编学习之《移位指令》

这章节学习前需要回顾之前的标志寄存器的内容&#xff1a; 汇编学习之《标志寄存器》 算数移位指令 SAL (Shift Arithmetic Left)算数移位指令 : 左移一次&#xff0c;最低位用0补位&#xff0c;最高位放入EFL标志寄存器的CF位&#xff08;进位标志&#xff09; OllyDbg查看…

NLP高频面试题(二十九)——大模型解码常见参数解析

在大语言模型的实际应用中&#xff0c;如何更有效地控制文本生成的质量与多样性&#xff0c;一直是热门研究话题。其中&#xff0c;模型解码&#xff08;decode&#xff09;策略至关重要&#xff0c;涉及的主要参数包括 top_k、top_p 和 temperature 等。本文将详细介绍这些常见…

【C#】Task 线程停止

CancellationTokenSource cts 是用于控制任务&#xff08;线程&#xff09;停止运行的。我们一步步来解释它的作用。 &#x1f50d; 现在的代码结构大概是这样的&#xff1a; Task.Run(() > {while (true){// 不断循环采集图像} });这种写法虽然简单&#xff0c;但最大的问…

WebRTC的ICE之TURN协议的交互流程中继转发Relay媒体数据的turnserver的测试

WebRTC的ICE之TURN协议的交互流程和中继转发Relay媒体数据的turnserver的测试 WebRTC的ICE之TURN协议的交互流程中继转发Relay媒体数据的turnserver的测试 WebRTC的ICE之TURN协议的交互流程和中继转发Relay媒体数据的turnserver的测试前言一、TURN协议1、连接Turn Server 流程①…

Redis + Caffeine多级缓存电商场景深度解析

Redis Caffeine多级缓存 Redis Caffeine多级缓存电商场景深度解析一、实施目的二、具体实施2.1 架构设计2.2 组件配置2.3 核心代码实现 三、实施效果3.1 性能指标对比3.2 业务指标改善3.3 系统稳定性 四、关键策略4.1 缓存预热4.2 一致性保障4.3 监控配置Prometheus监控指标 …

前端开发3D-基于three.js

基于 three.js 渲染任何画面&#xff0c;都要基于这 3 个要素来实现 1场景scene&#xff1a;放置物体的容器 2摄像机&#xff1a;类似人眼&#xff0c;可调整位置&#xff0c;角度等信息&#xff0c;展示不同画面 3渲染器&#xff1a;接收场景和摄像机对象&#xff0c;计算在浏…

代码随想录算法训练营--打卡day4

一.移除链表元素 1.题目链接 203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; 2.思路 通过 while 循环来遍历链表&#xff0c;只要 cur 的下一个节点不为空&#xff0c;就继续循环。在循环中&#xff0c;对 cur 的下一个节点的值进行判断&#xff1a; 值不等于…

虚拟电厂:多元能源聚合,开启绿色电力新时代

虚拟电厂&#xff1a;多元能源聚合&#xff0c;开启绿色电力新时代 在“双碳”目标驱动下&#xff0c;电力系统正经历从集中式向分布式、从单一能源向多能互补的深刻变革。 作为能源互联网的核心载体&#xff0c;虚拟电厂通过数字化技术整合多种能源资源&#xff0c;而是像指…