安全架构与最佳实践

知识库
知识库文档
/tech-stacks/spring-security/tutorial/安全架构与最佳实践.md

文档

Spring Security 安全架构与最佳实践教程

第一章:Security Filter Chain 架构

1.1 过滤器链执行顺序

请求 → ChannelProcessingFilter (HTTPS)
     → SecurityContextPersistenceFilter (加载 SecurityContext)
     → ConcurrentSessionFilter (会话并发)
     → UsernamePasswordAuthenticationFilter (表单登录)
     → BasicAuthenticationFilter (HTTP Basic)
     → CsrfFilter (CSRF 防护)
     → ExceptionTranslationFilter (异常转换)
     → FilterSecurityInterceptor (授权决策)
     → 业务 Controller

1.2 自定义过滤器插入位置

http.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter.class);
http.addFilterAfter(myFilter, CsrfFilter.class);
http.addFilterAt(myFilter, UsernamePasswordAuthenticationFilter.class);

第二章:认证(Authentication)

2.1 认证流程

UsernamePasswordAuthenticationToken (未认证)
  → AuthenticationManager
    → AuthenticationProvider (多个)
      → UserDetailsService.loadUserByUsername()
      → PasswordEncoder.matches()
    → 返回已认证的 Authentication
  → SecurityContextHolder 保存

2.2 自定义 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("用户不存在: " + username));

        return org.springframework.security.core.userdetails.User
            .withUsername(user.getUsername())
            .password(user.getPassword())
            .roles(user.getRoles().toArray(new String[0]))
            .accountLocked(!user.isActive())
            .build();
    }
}

2.3 多种认证方式组合

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .oauth2Login(oauth -> oauth
            .loginPage("/login")
            .defaultSuccessUrl("/dashboard"))
        .formLogin(form -> form
            .loginPage("/login"))
        .httpBasic(Customizer.withDefaults());

    return http.build();
}
// 同时支持:OAuth2 登录 + 表单登录 + Basic Auth

第三章:授权(Authorization)

3.1 基于角色的访问控制

// URL 级别
http.authorizeHttpRequests(auth -> auth
    .requestMatchers("/admin/**").hasRole("ADMIN")
    .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
    .requestMatchers(HttpMethod.POST, "/api/products").hasRole("EDITOR")
);

// 方法级别
@PreAuthorize("hasRole('ADMIN')")
public void adminOnly() { }

@PreAuthorize("hasRole('USER') and #userId == authentication.name")
public void updateOwnProfile(Long userId) { }

@PostAuthorize("returnObject.owner == authentication.name")
public Document getDocument(Long id) { }

3.2 自定义权限评估器

@Component("authz")
public class CustomAuthorization {
    public boolean isOwner(Authentication auth, Long resourceId) {
        // 自定义逻辑
        return true;
    }
}

// 使用
@PreAuthorize("@authz.isOwner(authentication, #id)")
public void updateResource(Long id) { }

第四章:常见安全漏洞与防护

4.1 CSRF

// REST API(无状态)可禁用
http.csrf(AbstractHttpConfigurer::disable);

// 传统 MVC 应用保留 CSRF Token
// Thymeleaf 自动在表单中注入 _csrf

4.2 CORS 配置

@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowedOrigins(List.of("https://frontend.example.com"));
    config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
    config.setAllowedHeaders(List.of("*"));
    config.setAllowCredentials(true);

    UrlBasedCorsConfigurationSource source =
        new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/api/**", config);
    return source;
}

4.3 密码安全

// 推荐 BCrypt
@Bean
public PasswordEncoder passwordEncoder() {
    return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    // {bcrypt}$2a$10$... 自动识别算法
}

// 密码强度规则
// 最低8位,含大小写字母+数字+特殊字符

第五章:OAuth2 与 JWT 最佳实践

5.1 Token 有效期策略

Access Token:15-30 分钟(短期)
Refresh Token:7-30 天(长期,存服务端)

5.2 Token 刷新机制

@PostMapping("/api/auth/refresh")
public Map<String, String> refresh(@RequestBody RefreshRequest req) {
    // 1. 验证 Refresh Token
    // 2. 检查是否在数据库中存在且未过期
    // 3. 签发新的 Access Token
    String newToken = jwtUtils.generateToken(username, role);
    return Map.of("accessToken", newToken);
}

5.3 黑名单机制

// 登出时将 Token 加入黑名单(Redis)
redisTemplate.opsForValue()
    .set("blacklist:" + token, "1", expiration, TimeUnit.MILLISECONDS);

// 过滤器检查
if (redisTemplate.hasKey("blacklist:" + token)) {
    throw new AccessDeniedException("Token 已失效");
}

思考题

  1. SecurityContextHolder 中的 SecurityContext 在线程间如何传递?
  2. 如何设计一个支持多租户的权限系统?
  3. JWT vs Session 认证各自的优缺点和适用场景?
  4. 如何防止暴力破解和撞库攻击?

信息

路径
/tech-stacks/spring-security/tutorial/安全架构与最佳实践.md
更新时间
2026/5/30