【Spring 】Spring Security深度解析:过滤器链、认证授权架构与现代集成方案

Spring Security深度解析:过滤器链、认证授权架构与现代集成方案

一、过滤器链架构:请求处理的核心管道

Spring Security的核心是Servlet Filter链模式,所有请求都经过一系列安全过滤器的检查。


1.1 过滤器链完整流程

请求流程

复制代码
HTTP Request
   │
   └─▶ DelegatingFilterProxy (Servlet容器级别)
           │
           └─▶ FilterChainProxy (Spring Security入口)
                   │
                   └─▶ SecurityFilterChain (匹配请求的过滤器链)
                           │
                           ├─▶ SecurityContextPersistenceFilter  [恢复/保存SecurityContext]
                           ├─▶ LogoutFilter                      [处理登出]
                           ├─▶ UsernamePasswordAuthenticationFilter [表单认证]
                           ├─▶ BasicAuthenticationFilter          [Basic认证]
                           ├─▶ BearerTokenAuthenticationFilter    [JWT/OAuth2令牌]
                           ├─▶ RequestCacheAwareFilter            [请求缓存]
                           ├─▶ SecurityContextHolderAwareRequestFilter [包装请求]
                           ├─▶ AnonymousAuthenticationFilter      [匿名认证]
                           ├─▶ SessionManagementFilter            [会话管理]
                           ├─▶ ExceptionTranslationFilter         [异常转换]
                           │
                           └─▶ FilterSecurityInterceptor          [最终鉴权]
                                   │
                                   └─▶ 调用Controller/拒绝访问

关键组件关系

java 复制代码
// Spring Security 6.x 配置
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            // 禁用CSRF(REST API场景)
            .csrf(csrf -> csrf.disable())
            
            // 配置过滤器
            .addFilterBefore(new RequestIdFilter(), UsernamePasswordAuthenticationFilter.class)
            
            // 认证配置
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/public/ **").permitAll()
                .requestMatchers("/api/admin/** ").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            
            // OAuth2 JWT 配置
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))
            );
        
        return http.build();
    }
}

1.2 核心过滤器详解

1.2.1 SecurityContextPersistenceFilter(上下文管理)

java 复制代码
// 职责:请求开始时从Session加载SecurityContext,结束时保存
public class SecurityContextPersistenceFilter extends GenericFilter {
    
    private final SecurityContextRepository repository = new HttpSessionSecurityContextRepository();
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        
        // 1. 从Session或请求属性加载SecurityContext
        SecurityContext contextBeforeChainExecution = repository.loadContext(req);
        SecurityContextHolder.setContext(contextBeforeChainExecution);
        
        try {
            // 2. 继续过滤器链
            chain.doFilter(request, response);
        } finally {
            // 3. 清理ThreadLocal,防止内存泄漏
            SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
            SecurityContextHolder.clearContext();
            
            // 4. 保存到Session(如果有变化)
            repository.saveContext(contextAfterChainExecution, req, resp);
        }
    }
}

1.2.2 UsernamePasswordAuthenticationFilter(表单认证)

java 复制代码
// 处理POST /login请求
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    
    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
    
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, 
                                                 HttpServletResponse response) 
            throws AuthenticationException {
        
        String username = obtainUsername(request);
        String password = obtainPassword(request);
        
        // 构建未认证Authentication
        UsernamePasswordAuthenticationToken authRequest = 
            UsernamePasswordAuthenticationToken.unauthenticated(username, password);
        
        // 委托给AuthenticationManager
        return this.getAuthenticationManager().authenticate(authRequest);
    }
}

1.2.3 FilterSecurityInterceptor(最终鉴权)

java 复制代码
// Spring Security过滤器链最后一个过滤器
public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        
        // 1. 构建FilterInvocation(包装request, response, chain)
        FilterInvocation fi = new FilterInvocation(request, response, chain);
        
        // 2. 鉴权决策
        InterceptorStatusToken token = super.beforeInvocation(fi);
        
        try {
            // 3. 通过后放行
            fi.getChain().doFilter(request, response);
        } finally {
            // 4. 后置处理
            super.finallyInvocation(token);
        }
        super.afterInvocation(token, null);
    }
}

1.3 自定义过滤器实战

场景:请求ID追踪与日志

java 复制代码
// 1. 自定义过滤器
public class RequestIdFilter extends OncePerRequestFilter {
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                    HttpServletResponse response, 
                                    FilterChain filterChain) throws ServletException, IOException {
        
        // 生成或获取请求ID
        String requestId = request.getHeader("X-Request-ID");
        if (StringUtils.isEmpty(requestId)) {
            requestId = UUID.randomUUID().toString();
        }
        
        // 放入MDC(日志上下文)
        MDC.put("requestId", requestId);
        response.setHeader("X-Request-ID", requestId);
        
        try {
            filterChain.doFilter(request, response);
        } finally {
            MDC.clear();
        }
    }
}

// 2. 注册过滤器(Spring Boot 3.x方式)
@Configuration
public class FilterConfig {
    
    @Bean
    public FilterRegistrationBean<RequestIdFilter> requestIdFilter() {
        FilterRegistrationBean<RequestIdFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new RequestIdFilter());
        registration.addUrlPatterns("/api/*");
        registration.setOrder(Ordered.HIGHEST_PRECEDENCE); // 最高优先级
        return registration;
    }
    
    // 或直接在SecurityFilterChain中配置
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.addFilterBefore(new RequestIdFilter(), UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }
}

二、认证/授权架构:核心组件与流程

2.1 认证架构:AuthenticationManager

核心组件关系

java 复制代码
// 1. 认证请求
Authentication unauthenticated = 
    UsernamePasswordAuthenticationToken.unauthenticated(username, password);

// 2. AuthenticationManager委托给Provider
authenticationManager.authenticate(unauthenticated)
    │
    └─▶ ProviderManager.delegateToProviders()
           │
           ├─▶ DaoAuthenticationProvider.authenticate() [表单认证]
           │      │
           │      ├─▶ UserDetailsService.loadUserByUsername()
           │      │       └─▶ 查询数据库返回UserDetails
           │      │
           │      └─▶ PasswordEncoder.matches() [密码校验]
           │
           └─▶ JwtAuthenticationProvider.authenticate() [JWT认证]
                  │
                  └─▶ JwtDecoder.decode() [解析令牌]
                  │
                  └─▶ JwtAuthenticationConverter [转换Authentication]

2.2 AuthenticationManager配置

Spring Security 6.x 方式

java 复制代码
@Configuration
@EnableWebSecurity
public class AuthConfig {
    
    @Bean
    public AuthenticationManager authenticationManager(
            UserDetailsService userDetailsService,
            PasswordEncoder passwordEncoder) {
        
        DaoAuthenticationProvider daoProvider = new DaoAuthenticationProvider();
        daoProvider.setUserDetailsService(userDetailsService);
        daoProvider.setPasswordEncoder(passwordEncoder);
        
        JwtAuthenticationProvider jwtProvider = new JwtAuthenticationProvider(jwtDecoder());
        
        return new ProviderManager(daoProvider, jwtProvider);
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Bean
    public UserDetailsService userDetailsService(UserRepository userRepository) {
        return username -> userRepository.findByUsername(username)
            .map(user -> User.builder()
                .username(user.getUsername())
                .password(user.getPassword())
                .authorities(mapRoles(user.getRoles()))
                .accountExpired(!user.isActive())
                .build())
            .orElseThrow(() -> new UsernameNotFoundException("用户不存在"));
    }
}

2.3 自定义AuthenticationProvider

场景:多因素认证(MFA)

java 复制代码
// 1. 自定义认证Token
public class OtpAuthenticationToken extends AbstractAuthenticationToken {
    private final String username;
    private final String otpCode;
    
    public OtpAuthenticationToken(String username, String otpCode) {
        super(null);
        this.username = username;
        this.otpCode = otpCode;
        setAuthenticated(false);
    }
    
    public OtpAuthenticationToken(String username, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.username = username;
        this.otpCode = null;
        setAuthenticated(true);
    }
    
    @Override
    public Object getCredentials() {
        return otpCode;
    }
    
    @Override
    public Object getPrincipal() {
        return username;
    }
}

// 2. 自定义认证提供者
@Component
public class OtpAuthenticationProvider implements AuthenticationProvider {
    
    @Autowired
    private OtpService otpService;
    
    @Autowired
    private UserDetailsService userDetailsService;
    
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        OtpAuthenticationToken token = (OtpAuthenticationToken) authentication;
        String username = token.getUsername();
        String otpCode = token.getOtpCode();
        
        // 验证OTP
        if (!otpService.validateOtp(username, otpCode)) {
            throw new BadCredentialsException("OTP验证失败");
        }
        
        // 加载用户详情
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        
        // 返回已认证Token
        return new OtpAuthenticationToken(username, userDetails.getAuthorities());
    }
    
    @Override
    public boolean supports(Class<?> authentication) {
        return OtpAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

// 3. 注册到AuthenticationManager
@Bean
public AuthenticationManager authenticationManager(
        UserDetailsService userDetailsService,
        PasswordEncoder passwordEncoder,
        OtpAuthenticationProvider otpProvider) {
    
    DaoAuthenticationProvider daoProvider = new DaoAuthenticationProvider();
    daoProvider.setUserDetailsService(userDetailsService);
    daoProvider.setPasswordEncoder(passwordEncoder);
    
    return new ProviderManager(daoProvider, otpProvider);
}

// 4. 自定义过滤器使用
public class OtpAuthenticationFilter extends OncePerRequestFilter {
    
    @Autowired
    private AuthenticationManager authenticationManager;
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                    HttpServletResponse response, 
                                    FilterChain chain) throws ServletException, IOException {
        
        String otpCode = request.getHeader("X-OTP-Code");
        String username = request.getHeader("X-Username");
        
        if (StringUtils.isNotEmpty(otpCode) && StringUtils.isNotEmpty(username)) {
            try {
                OtpAuthenticationToken token = new OtpAuthenticationToken(username, otpCode);
                Authentication authentication = authenticationManager.authenticate(token);
                
                SecurityContextHolder.getContext().setAuthentication(authentication);
            } catch (AuthenticationException e) {
                SecurityContextHolder.clearContext();
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                return;
            }
        }
        
        chain.doFilter(request, response);
    }
}

三、JWT集成:无状态认证

3.1 JWT过滤器模式(比认证提供者更常见)

java 复制代码
// 1. JWT工具类
@Component
public class JwtTokenProvider {
    
    private final String secret = "my-secret-key";
    private final long validityMs = 3600000; // 1小时
    
    public String generateToken(String username, Collection<? extends GrantedAuthority> authorities) {
        Claims claims = Jwts.claims().setSubject(username);
        claims.put("roles", authorities.stream()
                    .map(GrantedAuthority::getAuthority)
                    .collect(Collectors.toList()));
        
        Date now = new Date();
        Date expiry = new Date(now.getTime() + validityMs);
        
        return Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(now)
                .setExpiration(expiry)
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
    }
    
    public Authentication getAuthentication(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
        
        String username = claims.getSubject();
        List<String> roles = claims.get("roles", List.class);
        
        List<GrantedAuthority> authorities = roles.stream()
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());
        
        return new UsernamePasswordAuthenticationToken(username, "", authorities);
    }
    
    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
            return true;
        } catch (JwtException | IllegalArgumentException e) {
            return false;
        }
    }
}

// 2. JWT过滤器
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    
    @Autowired
    private JwtTokenProvider tokenProvider;
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                    HttpServletResponse response, 
                                    FilterChain filterChain) throws ServletException, IOException {
        
        String token = getTokenFromRequest(request);
        
        if (StringUtils.hasText(token) && tokenProvider.validateToken(token)) {
            // 从令牌获取认证信息
            Authentication authentication = tokenProvider.getAuthentication(token);
            
            // 设置到SecurityContext
            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;
    }
}

// 3. Security配置
@Configuration
@EnableWebSecurity
public class JwtSecurityConfig {
    
    @Autowired
    private JwtAuthenticationFilter jwtFilter;
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态
            )
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/ **").permitAll()
                .requestMatchers("/api/admin/** ").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class); // 添加JWT过滤器
            
        return http.build();
    }
}

3.2 Spring Security OAuth2 Resource Server

Spring Boot 3.x 推荐方式

java 复制代码
// application.yml
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://auth.example.com
          jwk-set-uri: https://auth.example.com/.well-known/jwks.json

// Security配置
@Configuration
@EnableWebSecurity
public class OAuth2ResourceServerConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/public").permitAll()
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(jwt -> jwt
                    .jwtAuthenticationConverter(jwtAuthenticationConverter())
                )
            );
        
        return http.build();
    }
    
    // 自定义JWT转换器(提取权限)
    @Bean
    public JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
        converter.setJwtGrantedAuthoritiesConverter(jwt -> {
            List<String> roles = jwt.getClaimAsStringList("roles");
            return roles.stream()
                    .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
                    .collect(Collectors.toSet());
        });
        return converter;
    }
}

四、OAuth2集成:现代授权

4.1 OAuth2客户端与资源服务器

场景:微服务调用外部OAuth2授权服务器(如Keycloak、Auth0)

java 复制代码
@Configuration
@EnableWebSecurity
public class OAuth2ClientConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .anyRequest().authenticated()
            )
            .oauth2Login(oauth2 -> oauth2
                .loginPage("/login")
                .defaultSuccessUrl("/home")
                .failureUrl("/login?error=true")
            )
            .oauth2Client(withDefaults()); // OAuth2客户端
        
        return http.build();
    }
    
    @Bean
    public OAuth2AuthorizedClientService authorizedClientService(
            ClientRegistrationRepository clientRegistrationRepository) {
        return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository);
    }
    
    @Bean
    public ClientRegistrationRepository clientRegistrationRepository() {
        ClientRegistration registration = ClientRegistration
                .withRegistrationId("keycloak")
                .clientId("my-client")
                .clientSecret("{noop}my-secret")
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")
                .scope("openid", "profile", "roles")
                .authorizationUri("https://keycloak.example.com/realms/my-realm/protocol/openid-connect/auth")
                .tokenUri("https://keycloak.example.com/realms/my-realm/protocol/openid-connect/token")
                .userInfoUri("https://keycloak.example.com/realms/my-realm/protocol/openid-connect/userinfo")
                .userNameAttributeName("preferred_username")
                .clientName("Keycloak")
                .build();
        
        return new InMemoryClientRegistrationRepository(registration);
    }
}

4.2 资源服务器保护API

java 复制代码
@Configuration
@EnableWebSecurity
public class ResourceServerConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .securityMatcher("/api/** ") // 仅保护API端点
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/public").permitAll()
                .requestMatchers("/api/admin").hasAuthority("ROLE_ADMIN")
                .anyRequest().hasAuthority("ROLE_USER")
            )
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(jwt -> jwt
                    .jwkSetUri("https://keycloak.example.com/.well-known/jwks.json")
                    .jwtAuthenticationConverter(jwt -> {
                        // 自定义权限映射
                        List<String> roles = jwt.getClaimAsStringList("roles");
                        Collection<GrantedAuthority> authorities = roles.stream()
                            .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
                            .toList();
                        
                        return new JwtAuthenticationToken(jwt, authorities, jwt.getClaimAsString("sub"));
                    })
                )
            );
        
        return http.build();
    }
}

五、生产实践:高级场景

5.1 动态权限(RBAC模型)

java 复制代码
// 数据库模型:User → Role → Permission
@Entity
public class Permission {
    private Long id;
    private String resource; // "order"
    private String action;   // "create", "read", "update", "delete"
}

// 自定义PermissionEvaluator
@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
    
    @Autowired
    private PermissionService permissionService;
    
    @Override
    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
        String username = authentication.getName();
        String resource = targetDomainObject.getClass().getSimpleName().toLowerCase();
        String action = permission.toString();
        
        return permissionService.hasPermission(username, resource, action);
    }
    
    @Override
    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
        // 基于ID的权限检查
        return false;
    }
}

// Security配置启用
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/api/orders").access(
                "@customPermissionEvaluator.hasPermission(authentication, 'order', 'create')"
            )
        );
    return http.build();
}

5.2 多租户安全隔离

java 复制代码
// 自定义TenantContext
public class TenantContext {
    private static final ThreadLocal<String> CURRENT_TENANT = new ThreadLocal<>();
    
    public static void setTenantId(String tenantId) {
        CURRENT_TENANT.set(tenantId);
    }
    
    public static String getTenantId() {
        return CURRENT_TENANT.get();
    }
    
    public static void clear() {
        CURRENT_TENANT.remove();
    }
}

// 租户过滤器
@Component
public class TenantFilter extends OncePerRequestFilter {
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                    HttpServletResponse response, 
                                    FilterChain chain) throws ServletException, IOException {
        
        String tenantId = request.getHeader("X-Tenant-ID");
        if (StringUtils.hasText(tenantId)) {
            TenantContext.setTenantId(tenantId);
        }
        
        try {
            chain.doFilter(request, response);
        } finally {
            TenantContext.clear();
        }
    }
}

// 自定义AuthenticationProvider
@Component
public class TenantAuthenticationProvider implements AuthenticationProvider {
    
    @Autowired
    private TenantUserDetailsService userDetailsService;
    
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();
        String tenantId = TenantContext.getTenantId();
        
        if (tenantId == null) {
            throw new BadCredentialsException("租户ID未提供");
        }
        
        UserDetails user = userDetailsService.loadUserByUsernameAndTenant(username, tenantId);
        
        if (!passwordEncoder.matches(password, user.getPassword())) {
            throw new BadCredentialsException("密码错误");
        }
        
        return new UsernamePasswordAuthenticationToken(user, password, user.getAuthorities());
    }
}

5.3 API速率限制

java 复制代码
// 结合Spring Security和Bucket4j
@Component
public class RateLimitFilter extends OncePerRequestFilter {
    
    private final Map<String, Bucket> buckets = new ConcurrentHashMap<>();
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                    HttpServletResponse response, 
                                    FilterChain chain) throws ServletException, IOException {
        
        String clientId = getClientId(request);
        Bucket bucket = buckets.computeIfAbsent(clientId, this::createBucket);
        
        // 尝试消耗1个令牌
        if (bucket.tryConsume(1)) {
            chain.doFilter(request, response);
        } else {
            response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
            response.getWriter().write("Rate limit exceeded");
        }
    }
    
    private Bucket createBucket(String clientId) {
        Bandwidth bandwidth = Bandwidth.classic(100, Refill.intervally(100, Duration.ofMinutes(1)));
        return Bucket.builder().addLimit(bandwidth).build();
    }
    
    private String getClientId(HttpServletRequest request) {
        String apiKey = request.getHeader("X-API-Key");
        if (StringUtils.hasText(apiKey)) {
            return apiKey;
        }
        return request.getRemoteAddr();
    }
}

// 配置到SecurityFilterChain
http.addFilterBefore(new RateLimitFilter(), UsernamePasswordAuthenticationFilter.class);

六、总结:Spring Security核心要点

核心概念 关键组件 配置方式 生产建议
过滤器链 SecurityFilterChain HttpSecurity配置 理解顺序,自定义Filter
认证 AuthenticationManager, AuthenticationProvider AuthenticationManager Bean 自定义Provider实现MFA
授权 FilterSecurityInterceptor .authorizeHttpRequests() 使用RBAC,避免硬编码
JWT BearerTokenAuthenticationFilter .oauth2ResourceServer() 使用成熟库,校验签名
OAuth2 ClientRegistrationRepository OAuth2 Client/Resource Server 使用授权服务器
代理与缓存 SecurityContextHolder SessionCreationPolicy STATELESS for API

最佳实践

  1. API优先 :REST API使用JWT + BearerTokenAuthenticationFilter
  2. 会话策略 :明确SessionCreationPolicy.STATELESSIF_REQUIRED
  3. 密码编码 :始终使用BCryptPasswordEncoder
  4. 异常处理 :自定义AuthenticationEntryPointAccessDeniedHandler
  5. 测试覆盖 :使用@WithMockUser@WithAnonymousUser测试安全规则

理解过滤器链的顺序、认证架构的委托模式、JWT的无状态特性,是掌握现代Spring Security的关键。

相关推荐
郑州光合科技余经理17 小时前
开发实战:海外版同城o2o生活服务平台核心模块设计
开发语言·git·python·架构·uni-app·生活·智慧城市
蛐蛐蜉蝣耶17 小时前
Spring Boot实现DynamicMethodMatcherPointcut示例
java·spring boot·后端
tech-share17 小时前
【无标题】IOMMU功能测试软件设计及实现 (二)
linux·架构·系统架构·gpu算力
阿巴~阿巴~17 小时前
从IP到MAC,从内网到公网:解密局域网通信与互联网连接的完整路径
服务器·网络·网络协议·架构·智能路由器·tcp·arp
de之梦-御风17 小时前
【视频投屏】最小可用(MVP)局域网投屏”开源项目架构
架构·开源·音视频
予枫的编程笔记17 小时前
Elasticsearch聚合分析与大规模数据处理:解锁超越搜索的进阶能力
java·大数据·人工智能·分布式·后端·elasticsearch·全文检索
BD_Marathon17 小时前
PostMan简介
java
一勺菠萝丶17 小时前
宝塔 vs 1Panel 有什么区别?能不能同时安装?
java
码农小卡拉17 小时前
Springboot “钩子”:@PostConstruct注解
java·spring boot·后端·spring·spring cloud