摘要:作为互联网软件开发人员,你是否曾在项目中遇到过这些问题?用户密码明文存储导致数据泄露,不同角色的用户能随意访问敏感接口,跨域请求时认证信息失效…… 这些安全隐患不仅会影响用户体验,更可能给项目带来严重的安全风险。而 Spring Security,作为 Jav
作为互联网软件开发人员,你是否曾在项目中遇到过这些问题?用户密码明文存储导致数据泄露,不同角色的用户能随意访问敏感接口,跨域请求时认证信息失效…… 这些安全隐患不仅会影响用户体验,更可能给项目带来严重的安全风险。而 Spring Security,作为 Java 生态中最主流的安全框架,正是解决这些问题的 “利器”。今天这篇文章,我们就从实战角度出发,手把手教你搞定 Spring Security 的认证与授权,让你的接口防护能力直接拉满。
在开始技术讲解前,我们先聊聊为什么 Spring Security 是后端开发者的 “必修课”。我曾接触过三个典型的开发案例,或许能让你更直观地理解它的重要性。
第一个案例是某电商平台的后台管理系统。初期开发时,团队为了赶进度,只简单做了 “用户名 + 密码” 的登录验证,没有做角色权限控制。结果上线后,运营人员通过普通账号竟能访问到订单支付接口,还能修改用户的支付金额 —— 这就是典型的 “授权缺失” 导致的安全漏洞。最后团队紧急引入 Spring Security,花了 3 天时间重构权限体系,才避免了更大的损失。
第二个案例是某 SaaS 系统的 API 开发。开发者在设计认证机制时,自己手写了 Token 生成和验证逻辑,却忽略了 Token 的过期刷新、签名加密等细节。上线后不久,就出现了 Token 被伪造的情况,导致多个企业用户的数据被非法获取。后来改用 Spring Security 的 OAuth2.0 组件,仅用半天就实现了安全合规的认证流程。
第三个案例更常见:很多开发者在集成 Spring Security 时,因为不了解其核心过滤器链的工作原理,盲目自定义配置,导致出现 “登录成功后仍无法访问接口”“权限注解不生效” 等问题,排查了一整天才发现是过滤器顺序配置错误。
其实这些问题的根源,都是对 Spring Security 的认证与授权核心逻辑理解不透彻。接下来,我们就从基础概念入手,一步步搭建实战案例,帮你彻底搞懂这套框架。
首先要明确:认证(Authentication)是确认用户身份的过程,简单说就是回答 “你是谁” 的问题。Spring Security 的认证流程围绕 “Authentication 对象” 和 “AuthenticationManager 接口” 展开,我们先拆解核心组件,再写代码。
2.1 3 个核心组件,搞懂认证底层逻辑
Authentication 对象:存储认证相关信息的载体,包含 3 个关键属性:
principal:用户身份信息(如用户名、用户实体类对象);credentials:用户凭证(如密码,认证成功后会被清空,避免泄露);authorities:用户拥有的权限(认证阶段暂不关注,授权阶段会用到)。认证前,我们会创建一个 “未认证” 的 Authentication 对象,传入用户名和密码;认证成功后,Spring Security 会返回一个 “已认证” 的对象,填充用户的完整信息。
AuthenticationManager:认证的核心接口,只有一个authenticate方法,负责执行认证逻辑。实际开发中,我们常用它的实现类ProviderManager,它可以管理多个AuthenticationProvider(不同的认证方式,如用户名密码认证、短信验证码认证)。
UserDetailsService:加载用户信息的接口,Spring Security 默认会调用它的loadUserByUsername方法,根据用户名查询数据库中的用户信息(如密码、角色)。这是我们自定义认证逻辑时最常扩展的接口。
简单理解:认证流程就是 “前端传入用户名密码 → 构建未认证 Authentication 对象 → AuthenticationManager 调用 UserDetailsService 查询用户 → 比对密码是否正确 → 返回已认证对象”。
2.2 实战:10 行代码实现数据库认证(Spring Boot 整合)
接下来我们用 Spring Boot 整合 Spring Security,实现 “从数据库查询用户信息” 的认证功能。假设你已经搭建好 Spring Boot 项目,且有一个sys_user表(包含 id、username、password、role 字段)。
步骤 1:引入依赖(pom.xml)
org.springframework.bootspring-boot-starter-securitycom.baomidoumybatis-plus-boot-starter3.5.3.1mysqlmysql-connector-javaruntime步骤 2:实现 UserDetailsService 接口(查询用户信息)
@Servicepublic class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate SysUserMapper sysUserMapper; // 自定义的Mapper接口@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundexception {// 1. 根据用户名查询数据库SysUser user = sysUserMapper.selectOne(new QueryWrapper.eq("username", username));if (user == null) {throw new UsernameNotFoundException("用户不存在");}// 2. 转换为Spring Security需要的UserDetails对象// 注意:密码必须是加密后的(这里假设数据库中存储的是BCrypt加密后的密码)Collection authorities = Collections.singletonList(new SimpleGrantedAuthority(user.getRole));return new User(user.getUsername, user.getPassword, authorities);}}步骤 3:配置 Spring Security(核心配置类)
@Configuration@EnableWebSecuritypublic class SecurityConfig {@Autowiredprivate UserDetailsService userDetailsService;// 密码加密器(使用BCrypt算法)@Beanpublic PasswordEncoder passwordEncoder {return new BCryptPasswordEncoder;}// 配置认证管理器@Beanpublic AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {AuthenticationManagerBuilder auth = http.getSharedObject(AuthenticationManagerBuilder.class);// 关联UserDetailsService和密码加密器auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);return auth.build;}// 配置接口访问规则、登录逻辑等@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http// 关闭csrf(前后端分离项目常用,若为传统项目需开启).csrf(csrf -> csrf.disable)// 配置接口访问权限.authorizeHttprequests(auth -> auth// 放行登录接口(不需要认证就能访问).RequestMatchers("/api/login").permitAll// 其他所有接口都需要认证.anyRequest.authenticated)// 配置表单登录(前后端分离可改用JSON登录,这里先演示默认表单).formLogin(form -> form// 自定义登录接口地址(默认是/login).loginProcessingUrl("/api/login")// 登录成功后的返回格式(默认是跳转页面,这里改为返回JSON).successHandler((request, response, authentication) -> {response.setContentType("application/json;charset=utf-8");Map result = new HashMap;result.put("code", 200);result.put("message", "登录成功");result.put("data", authentication.getPrincipal);response.getWriter.write(new ObjectMapper.writeValueAsString(result));})// 登录失败后的返回格式.failureHandler((request, response, exception) -> {response.setContentType("application/json;charset=utf-8");Map result = new HashMap;result.put("code", 401);result.put("message", "登录失败:" + exception.getMessage);response.getWriter.write(new ObjectMapper.writeValueAsString(result));}));return http.build;}}步骤 4:测试认证功能
此时启动项目,发送 POST 请求到/api/login,传入参数username和password(注意:前端传入的是明文密码,Spring Security 会自动用 BCrypt 加密后与数据库中的加密密码比对)。若用户存在且密码正确,会返回如下 JSON:
{"code": 200,"message": "登录成功","data": {"username": "admin","authorities": [{"authority": "ROLE_ADMIN"}],"accountNonExpired": true,"accountNonLocked": true,"credentialsNonExpired": true,"enabled": true}}至此,我们就完成了最基础的数据库认证功能。但这还不够 —— 如果所有认证通过的用户都能访问所有接口,那管理员和普通用户就没有区别了,这就需要 “授权” 来解决。
授权(Authorization)是确认用户拥有哪些权限的过程,也就是回答 “你能做什么” 的问题。Spring Security 的授权核心是 “基于角色的访问控制(RBAC)”,常用的实现方式有 3 种:接口级授权、方法级授权、动态权限授权。我们逐一讲解实战用法。
3.1 方案 1:接口级授权(在 SecurityConfig 中配置)
这种方式最直接,在securityFilterChain方法中,通过requestMatchers指定接口路径,并搭配hasRole或hasAuthority设置访问权限。
例如,我们希望:
/api/admin/**接口只能由 “ADMIN” 角色访问;/api/user/**接口只能由 “USER” 角色访问;/api/public/**接口无需认证即可访问。修改 SecurityConfig 中的authorizeHttpRequests配置:
.authorizeHttpRequests(auth -> auth.requestMatchers("/api/public/**").permitAll // 放行公开接口.requestMatchers("/api/admin/**").hasRole("ADMIN") // ADMIN角色可访问.requestMatchers("/api/user/**").hasRole("USER") // USER角色可访问.anyRequest.authenticated)注意:hasRole会自动给角色名加上 “ROLE_” 前缀,所以如果数据库中存储的角色是 “ROLE_ADMIN”,这里用hasRole("ADMIN")即可;若数据库中是 “ADMIN”,则需要用hasAuthority("ADMIN")(hasAuthority不会加前缀)。
3.2 方案 2:方法级授权(用注解控制)
如果接口较多,在配置类中逐个配置会很繁琐,此时可以用注解实现 “方法级授权”。只需两步:
步骤 1:在 SecurityConfig 上添加@EnableMethodSecurity注解
@Configuration@EnableWebSecurity@EnableMethodSecurity(prePostEnabled = true) // 开启方法级授权注解public class SecurityConfig {// 其他配置不变}步骤 2:在 Controller 方法上添加@PreAuthorize注解
@RestController@RequestMapping("/api/admin")public class AdminController {// 只有ADMIN角色能访问该方法@PreAuthorize("hasRole('ADMIN')")@GetMapping("/user/list")public String getUserList {return "管理员查看用户列表";}// 只有同时拥有ADMIN角色和MANAGE_USER权限的用户能访问@PreAuthorize("hasRole('ADMIN') and hasAuthority('MANAGE_USER')")@PostMapping("/user/add")public String addUser {return "管理员添加用户";}}这种方式的优势是 “权限与方法绑定”,代码更直观,后期维护也更方便。
3.3 方案 3:动态权限授权(从数据库加载权限)
前面两种方案的权限都是 “写死” 在代码中的,如果需要动态修改权限(比如在后台管理系统中,管理员可以随时调整角色的权限),就需要 “从数据库加载权限”。实现思路是:自定义Filter,在请求到达时,从数据库查询当前用户的权限,再与请求的接口进行匹配。
核心步骤:
设计权限表结构:新增sys_role(角色表)、sys_permission(权限表)、sys_user_role(用户 - 角色关联表)、sys_role_permission(角色 - 权限关联表);
自定义权限过滤器:继承OncePerRequestFilter,在doFilterInternal方法中:
获取当前登录用户;从数据库查询用户拥有的所有权限(如/api/admin/user/list);构建SecurityContext,将权限设置到 Authentication 对象中;将过滤器加入 Spring Security 的过滤器链。
这里给出关键代码(自定义过滤器):
@Componentpublic class DynamicPermissionFilter extends OncePerRequestFilter {@Autowiredprivate SysPermissionMapper permissionMapper;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 1. 获取当前登录用户Authentication authentication = SecurityContextHolder.getContext.getAuthentication;if (authentication != null && authentication.isAuthenticated) {// 2. 获取用户名String username = authentication.getName;// 3. 从数据库查询用户拥有的权限(这里需要关联用户-角色-权限表)List permissions = permissionMapper.getPermissionsByUsername(username);// 4. 构建权限集合Collection authorities = permissions.stream.map(SimpleGrantedAuthority::new).collect(Collectors.toList);// 5. 重新构建Authentication对象,设置动态权限Authentication newAuth = new UsernamePasswordAuthenticationToken(authentication.getPrincipal,authentication.getCredentials,authorities);// 6. 更新SecurityContextSecurityContextHolder.getContext.setAuthentication(newAuth);}// 继续执行过滤器链filterChain.doFilter(request, response);}}然后在 SecurityConfig 中,将这个过滤器加入过滤器链:
@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http, DynamicPermissionFilter dynamicPermissionFilter) throws Exception {http// 其他配置不变.addFilterBefore(dynamicPermissionFilter, UsernamePasswordAuthenticationFilter.class); // 在用户名密码过滤器前执行return http.build;}这样一来,用户的权限就会从数据库动态加载,后续修改权限时,只需更新数据库即可,无需修改代码。
在集成 Spring Security 的过程中,很多开发者会因为对细节不熟悉而踩坑。我整理了 5 个最常见的问题,帮你少走弯路。
坑 1:密码加密方式不匹配
问题表现:明明输入的密码正确,却始终提示 “Bad credentials”(密码错误)。
原因:数据库中存储的密码是明文,而 Spring Security 默认使用 BCrypt 加密器,会将前端传入的明文密码加密后与数据库中的明文比对,自然不匹配。
解决方案:确保数据库中存储的是加密后的密码。可以用PasswordEncoder的encode方法生成加密密码,再存入数据库:
public static void main(String args) {PasswordEncoder encoder = new BCryptPasswordEncoder;String encodedPassword = encoder.encode("123456"); // 加密明文密码“123456”System.out.println(encodedPassword); // 输出加密后的密码,如$2a$10$...}坑 2:CSRF 保护导致接口无法访问
问题表现:前后端分离项目中,发送 POST 请求时,提示 “403 Forbidden”,且响应信息包含 “CSRF token”。
原因:Spring Security 默认开启 CSRF 保护,会要求请求中携带 CSRF Token,而前后端分离项目中,前端通常不会处理这个 Token。
解决方案:前后端分离项目中,直接关闭 CSRF 保护(如前面的配置中csrf(csrf -> csrf.disable));若为传统服务器渲染项目(如 JSP),则需要在表单中添加 CSRF Token:
坑 3:过滤器顺序配置错误
问题表现:自定义的过滤器不生效,或者权限判断逻辑出现异常。
原因:Spring Security 的过滤器链有严格的执行顺序,若自定义过滤器的位置放错,会导致逻辑混乱。例如,将动态权限过滤器放在UsernamePasswordAuthenticationFilter之后,会导致权限判断时,用户还未完成认证。
解决方案:牢记核心过滤器的执行顺序(从先到后):
CsrfFilter(CSRF 保护过滤器)UsernamePasswordAuthenticationFilter(用户名密码认证过滤器)RememberMeAuthenticationFilter(记住我过滤器)FilterSecurityInterceptor(权限判断过滤器)自定义过滤器时,需根据功能确定位置:比如动态权限过滤器需要在 “认证后、权限判断前” 执行,所以放在UsernamePasswordAuthenticationFilter之后、FilterSecurityInterceptor之前;而验证码过滤器则需要在UsernamePasswordAuthenticationFilter之前执行(先验证验证码,再进行账号密码认证)。
坑 4:@PreAuthorize 注解不生效
问题表现:在 Controller 方法上添加了@PreAuthorize("hasRole('ADMIN')"),但普通用户仍能访问该接口。
原因:常见有 3 种原因:
未在 SecurityConfig 上添加@EnableMethodSecurity(prePostEnabled = true)注解,导致方法级授权注解未开启;
注解中的表达式错误,比如角色名大小写不匹配(hasRole('admin')与数据库中的 “ADMIN” 不匹配);
Spring Security 版本问题:Spring Boot 3.x 后,默认使用@EnableMethodSecurity,而 Spring Boot 2.x 使用@EnableGlobalMethodSecurity,若版本与注解不匹配会导致失效。
解决方案:
确认注解开启:Spring Boot 3.x 用@EnableMethodSecurity(prePostEnabled = true),Spring Boot 2.x 用@EnableGlobalMethodSecurity(prePostEnabled = true);
检查表达式:角色名严格区分大小写,若用hasRole需确认数据库中角色是否带 “ROLE_” 前缀;
排除版本冲突:在 pom.xml 中指定 Spring Security 的版本,与 Spring Boot 版本匹配(如 Spring Boot 3.2.x 对应 Spring Security 6.2.x)。
坑 5:跨域请求时认证信息丢失
问题表现:前后端分离项目中,前端发送跨域请求(如 Vue 项目部署在localhost:8080,后端在localhost:8081),登录成功后,后续请求仍提示 “未认证”。
原因:跨域请求默认不会携带 Cookie,而 Spring Security 的 Session 认证依赖 Cookie 中的 JSESSIONID,导致后续请求无法识别用户身份;若用 Token 认证(如 JWT),则可能是前端未在请求头中携带 Token,或后端未配置跨域允许携带请求头。
解决方案:
若用 Session 认证:
后端配置跨域允许携带 Cookie:@Beanpublic WebMvcConfigurer corsConfigurer {return new WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOrigins("http://localhost:8080") // 允许的前端域名.allowedMethods("GET", "POST", "PUT", "DELETE").allowCredentials(true) // 允许携带Cookie.maxAge(3600);}};}前端请求时开启 withCredentials:// Axios示例axios.post("http://localhost:8081/api/user/info", {}, {withCredentials: true // 携带Cookie});若用 Token 认证:
前端在请求头中携带 Token:axios.post("http://localhost:8081/api/user/info", {}, {headers: {"Authorization": "Bearer " + localStorage.getItem("token") // Bearer + Token格式}});后端配置跨域允许请求头:在addCorsMappings中添加.allowedHeaders("Authorization"),允许携带 Authorization 头。
前面我们讲的是基于 Session 的认证,但在分布式系统中(如多台后端服务器部署),Session 认证会出现 “Session 共享” 问题,此时更推荐用 Token 认证(如 JWT)。下面我们补充 JWT 认证的实战实现,以及常用的 “记住我” 功能。
5.1 JWT 认证:实现无状态的身份验证
JWT(JSON Web Token)是一种无状态的认证方式,核心是将用户信息加密到 Token 中,后端无需存储 Session,只需验证 Token 的有效性即可。
步骤 1:引入 JWT 依赖
io.jsonwebtokenjjwt-api0.11.5io.jsonwebtokenjjwt-impl0.11.5runtimeio.jsonwebtokenjjwt-jackson0.11.5runtime步骤 2:编写 JWT 工具类(生成 Token、验证 Token)
@Componentpublic class JwtUtils {// 密钥(实际项目中需放在配置文件,避免硬编码)@Value("${jwt.secret}")private String secret;// Token过期时间(1小时,单位:毫秒)@Value("${jwt.expiration}")private long expiration;// 生成Tokenpublic String generateToken(String username) {Date now = new Date;Date expireDate = new Date(now.getTime + expiration);return Jwts.builder.setSubject(username) // 设置用户名.setIssuedAt(now) // 签发时间.setExpiration(expireDate) // 过期时间.signWith(SignatureAlgorithm.HS512, secret) // 签名算法.compact;}// 从Token中获取用户名public String getUsernameFromToken(String token) {Claims claims = Jwts.parser.setSigningKey(secret).parseClaimsJws(token).getBody;return claims.getSubject;}// 验证Token是否有效(未过期、签名正确)public boolean validateToken(String token) {try {Jwts.parser.setSigningKey(secret).parseClaimsJws(token);return true;} catch (Exception e) {// Token过期、签名错误等都会抛出异常return false;}}}步骤 3:自定义 JWT 认证过滤器
@Componentpublic class JwtAuthenticationFilter extends OncePerRequestFilter {@Autowiredprivate JwtUtils jwtUtils;@Autowiredprivate UserDetailsService userDetailsService;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 1. 从请求头中获取TokenString token = getTokenFromRequest(request);// 2. 验证Token有效性if (token != null && jwtUtils.validateToken(token)) {// 3. 从Token中获取用户名String username = jwtUtils.getUsernameFromToken(token);// 4. 加载用户信息UserDetails userDetails = userDetailsService.loadUserByUsername(username);// 5. 构建Authentication对象,设置到SecurityContextAuthentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities);SecurityContextHolder.getContext.setAuthentication(authentication);}// 继续执行过滤器链filterChain.doFilter(request, response);}// 从Authorization头中获取Token(格式:Bearer )private String getTokenFromRequest(HttpServletRequest request) {String authHeader = request.getHeader("Authorization");if (authHeader != null && authHeader.startsWith("Bearer ")) {return authHeader.substring(7); // 截取“Bearer ”后的Token}return null;}}步骤 4:更新 SecurityConfig,集成 JWT 过滤器
@Configuration@EnableWebSecurity@EnableMethodSecurity(prePostEnabled = true)public class SecurityConfig {// 省略其他注入...@Autowiredprivate JwtAuthenticationFilter jwtAuthenticationFilter;@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.csrf(csrf -> csrf.disable)// 关闭Session(JWT是无状态认证,无需Session).sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)).authorizeHttpRequests(auth -> auth.requestMatchers("/api/login").permitAll.anyRequest.authenticated)// 登录成功后生成Token并返回.formLogin(form -> form.loginProcessingUrl("/api/login").successHandler((request, response, authentication) -> {response.setContentType("application/json;charset=utf-8");Map result = new HashMap;String username = authentication.getName;String token = jwtUtils.generateToken(username); // 生成JWT Tokenresult.put("code", 200);result.put("message", "登录成功");result.put("data", token); // 返回Token给前端response.getWriter.write(new ObjectMapper.writeValueAsString(result));}).failureHandler(/* 省略失败处理,同之前 */))// 添加JWT过滤器(在用户名密码过滤器之前执行).addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);return http.build;}}步骤 5:测试 JWT 认证
发送 POST 请求到/api/login,获取返回的 Token(如eyJhbGciOiJIUzUxMiJ9...);发送后续请求(如/api/user/info)时,在请求头中添加Authorization: Bearer eyJhbGciOiJIUzUxMiJ9...;后端会通过 JWT 过滤器验证 Token,若有效则允许访问接口。5.2 “记住我” 功能:实现自动登录
“记住我” 功能允许用户在关闭浏览器后,再次访问时无需重新登录。Spring Security 默认基于数据库存储 “记住我” 令牌,实现步骤如下:
步骤 1:创建 “记住我” 令牌表(MySQL)
CREATE TABLE persistent_logins (username VARCHAR(64) NOT NULL,series VARCHAR(64) PRIMARY KEY,token VARCHAR(64) NOT NULL,last_used TIMESTAMP NOT NULL);步骤 2:配置 “记住我” 功能
在 SecurityConfig 中添加rememberMe配置:
@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http// 省略其他配置....rememberMe(rememberMe -> rememberMe.tokenRepository(jdbcTokenRepository) // 基于数据库存储令牌.tokenValiditySeconds(60 * 60 * 24 * 7) // 令牌有效期(7天).userDetailsService(userDetailsService) // 加载用户信息);return http.build;}// 配置JDBC令牌仓库@Beanpublic JdbcTokenRepositoryImpl jdbcTokenRepository {JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl;tokenRepository.setDataSource(dataSource); // 注入数据源// 首次启动时自动创建persistent_logins表(若已手动创建,注释掉这行)// tokenRepository.setCreateTableOnStartup(true);return tokenRepository;}步骤 3:前端添加 “记住我” 复选框
在登录表单中添加复选框,参数名固定为remember-me:
记住我登录若为前后端分离项目,前端需在登录请求中携带remember-me参数(布尔值)。
步骤 4:测试 “记住我” 功能
勾选 “记住我” 并登录;关闭浏览器,再次打开并访问需要认证的接口(如/api/user/info);无需重新登录即可正常访问,说明 “记住我” 功能生效。看到这里,相信你已经对 Spring Security 的认证与授权有了清晰的理解。最后我们用一张流程图,总结核心逻辑:
认证流程:
前端传入认证信息(用户名密码 / Token)→ 后端过滤器(如 UsernamePasswordAuthenticationFilter/JwtAuthenticationFilter)接收请求 → 调用 AuthenticationManager 执行认证 → 通过 UserDetailsService 加载用户信息 → 比对认证信息(密码 / Token 有效性)→ 认证成功后,将 Authentication 对象存入 SecurityContext。
授权流程:
请求到达 FilterSecurityInterceptor → 从 SecurityContext 获取 Authentication 对象 → 调用 AccessDecisionManager 判断用户权限是否满足接口要求 → 权限足够则允许访问,不足则返回 403。
掌握这套流程后,无论遇到简单的单体项目,还是复杂的分布式系统,你都能快速搭建安全可靠的接口防护体系。如果在实际开发中遇到具体问题,欢迎在评论区留言讨论,我们一起解决!
来源:从程序员到架构师一点号