SpringSecurity认证授权流程

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 的核心流程:

  1. 请求进入 FilterChain
  2. 认证过滤器 提取凭证
  3. AuthenticationManager 委托认证
  4. UserDetailsService 加载用户
  5. PasswordEncoder 比对密码
  6. SecurityContextHolder 存储结果
  7. 授权拦截器 检查权限

掌握这个流程,可以灵活定制各种安全策略。


   转载规则


《SpringSecurity认证授权流程》 小乐 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录