SpringSecurity认证授权流程
Spring Security 是一个强大的安全框架,提供认证(Authentication)和授权(Authorization)功能。理解其内部流程对定制安全策略至关重要。
核心组件
| 组件 |
作用 |
| SecurityContextHolder |
存储当前安全上下文 |
| Authentication |
认证令牌 |
| AuthenticationManager |
认证管理器 |
| Provider |
具体认证逻辑 |
| UserDetailsService |
加载用户信息 |
| FilterChain |
安全过滤器链 |
| AccessDecisionManager |
授权决策 |
认证流程
HTTP Request | v SecurityFilterChain | ├── UsernamePasswordAuthenticationFilter │ └── 提取用户名密码 │ └── 创建 UsernamePasswordAuthenticationToken │ └── 调用 AuthenticationManager.authenticate() │ ├── AuthenticationManager │ └── ProviderManager │ └── DaoAuthenticationProvider │ ├── UserDetailsService.loadUserByUsername() │ │ └── 从数据库加载用户 │ ├── 密码比对(PasswordEncoder) │ └── 创建已认证的 Authentication │ └── SecurityContextHolder └── 存入认证信息
|
过滤器链
请求 | ├── ChannelProcessingFilter ├── WebAsyncManagerIntegrationFilter ├── SecurityContextPersistenceFilter # 加载/保存 SecurityContext ├── HeaderWriterFilter ├── CorsFilter ├── CsrfFilter # CSRF 防护 ├── LogoutFilter # 登出处理 ├── UsernamePasswordAuthenticationFilter # 表单登录 ├── DefaultLoginPageGeneratingFilter ├── DefaultLogoutPageGeneratingFilter ├── BasicAuthenticationFilter # HTTP Basic ├── RequestCacheAwareFilter ├── SecurityContextHolderAwareRequestFilter ├── AnonymousAuthenticationFilter # 匿名认证 ├── SessionManagementFilter ├── ExceptionTranslationFilter # 异常转换 └── FilterSecurityInterceptor # 授权拦截
|
自定义认证
1. UserDetailsService
@Service public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username) .orElseThrow(() -> new UsernameNotFoundException("用户不存在")); return org.springframework.security.core.userdetails.User.builder() .username(user.getUsername()) .password(user.getPassword()) .roles(user.getRole()) .build(); } }
|
2. SecurityConfig
@Configuration @EnableWebSecurity public class SecurityConfig { @Autowired private CustomUserDetailsService userDetailsService; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth .requestMatchers("/login", "/register").permitAll() .requestMatchers("/admin/**").hasRole("ADMIN") .requestMatchers("/api/**").authenticated() .anyRequest().permitAll() ) .formLogin(form -> form .loginPage("/login") .loginProcessingUrl("/doLogin") .defaultSuccessUrl("/home") .failureUrl("/login?error") ) .logout(logout -> logout .logoutUrl("/logout") .logoutSuccessUrl("/login") ); return http.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AuthenticationManager authenticationManager( AuthenticationConfiguration config) throws Exception { return config.getAuthenticationManager(); } }
|
JWT 认证
JWT Filter
@Component public class JwtAuthenticationFilter extends OncePerRequestFilter { @Autowired private JwtTokenProvider tokenProvider; @Autowired private CustomUserDetailsService userDetailsService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = getTokenFromRequest(request); if (StringUtils.hasText(token) && tokenProvider.validateToken(token)) { String username = tokenProvider.getUsernameFromToken(token); UserDetails userDetails = userDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); authentication.setDetails( new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } filterChain.doFilter(request, response); } private String getTokenFromRequest(HttpServletRequest request) { String bearerToken = request.getHeader("Authorization"); if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { return bearerToken.substring(7); } return null; } }
|
JWT 工具类
@Component public class JwtTokenProvider { @Value("${jwt.secret}") private String jwtSecret; @Value("${jwt.expiration:86400000}") private long jwtExpiration; public String generateToken(UserDetails userDetails) { Date expiryDate = new Date(System.currentTimeMillis() + jwtExpiration); return Jwts.builder() .setSubject(userDetails.getUsername()) .setIssuedAt(new Date()) .setExpiration(expiryDate) .signWith(SignatureAlgorithm.HS512, jwtSecret) .compact(); } public String getUsernameFromToken(String token) { Claims claims = Jwts.parser() .setSigningKey(jwtSecret) .parseClaimsJws(token) .getBody(); return claims.getSubject(); } public boolean validateToken(String token) { try { Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token); return true; } catch (Exception e) { return false; } } }
|
授权流程
方法级授权
@Service public class OrderService { @PreAuthorize("hasRole('USER')") public Order createOrder(OrderRequest request) { } @PreAuthorize("hasRole('ADMIN') or @securityService.isOwner(#orderId)") public void cancelOrder(Long orderId) { } @PostAuthorize("returnObject.userId == authentication.principal.id") public Order getOrder(Long orderId) { } }
|
配置
@EnableGlobalMethodSecurity(prePostEnabled = true) @Configuration public class MethodSecurityConfig { }
|
总结
Spring Security 的核心流程:
- 请求进入 FilterChain
- 认证过滤器 提取凭证
- AuthenticationManager 委托认证
- UserDetailsService 加载用户
- PasswordEncoder 比对密码
- SecurityContextHolder 存储结果
- 授权拦截器 检查权限
掌握这个流程,可以灵活定制各种安全策略。