Spring Security 回顾一

Table of Contents

Spring Security 框架介绍

1. Spring Security 是什么?

Spring Security 是 Spring 框架的安全管理框架,用于提供 身份认证(Authentication)授权(Authorization) 机制。它主要用于保护 Web 应用REST API,防止 未授权访问常见安全攻击(如 CSRF、XSS、Session Fixation、Clickjacking 等)。


2. Spring Security 的核心概念

Spring Security 主要涉及以下核心概念:

2.1 身份认证(Authentication)

身份认证是指 验证用户身份 的过程。Spring Security 支持多种认证方式:

  • 用户名 + 密码(默认基于 UserDetailsService
  • JWT 令牌(适用于 REST API)
  • OAuth 2.0 / OpenID Connect(支持 Google、GitHub 等第三方登录)
  • LDAP(企业级身份认证)
  • SSO(单点登录)
  • 自定义认证提供者(Custom AuthenticationProvider)

2.2 授权(Authorization)

授权是指 确定用户是否有权限访问资源 的过程。Spring Security 主要有两种授权方式:

  • 基于角色(Role-Based Access Control, RBAC)
    • 例如:hasRole('ADMIN')
  • 基于权限(Permission-Based Access Control, PBAC)
    • 例如:hasAuthority('READ_PRIVILEGE')

授权方式:

  • 方法级别授权(基于 @PreAuthorize, @Secured
  • URL 级别授权(基于 HttpSecurity
  • 自定义授权逻辑

2.3 安全过滤器(Security Filters)

Spring Security 采用 过滤器链(Filter Chain) 进行请求拦截,每个请求都会经过 多个安全过滤器

  • UsernamePasswordAuthenticationFilter(处理表单登录)
  • BasicAuthenticationFilter(处理 HTTP Basic 认证)
  • JwtAuthenticationFilter(自定义 JWT 认证)
  • SecurityContextPersistenceFilter(管理用户 Session)
  • ExceptionTranslationFilter(异常处理)

2.4 Spring Security 的安全特性

  • CSRF 保护(Cross-Site Request Forgery)
  • Session 管理(防止会话固定攻击)
  • 密码加密(BCryptPasswordEncoder)
  • 跨域资源共享(CORS)支持
  • 两步验证(2FA)
  • 安全头部管理(Security Headers)

3. Spring Security 的基本使用

3.1 添加 Spring Security 依赖

如果使用 Spring Boot,可以直接添加 spring-boot-starter-security

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

3.2 配置简单的用户名密码登录

Spring Security 默认提供一个 UserDetailsService,默认用户名是 user,密码在控制台生成。我们可以自定义用户名密码:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/admin/**").hasRole("ADMIN") // 只有 ADMIN 角色可以访问
                .requestMatchers("/user/**").hasRole("USER") // 只有 USER 角色可以访问
                .anyRequest().authenticated() // 其他请求必须登录
            )
            .formLogin(withDefaults()) // 启用默认的登录表单
            .httpBasic(withDefaults()); // 启用 HTTP Basic 认证
        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user = User.withDefaultPasswordEncoder()
                .username("admin")
                .password("admin123")
                .roles("ADMIN")
                .build();
        return new InMemoryUserDetailsManager(user);
    }
}

4. Spring Security + JWT 实现无状态认证

对于 REST API,我们通常使用 JWT(JSON Web Token) 进行无状态认证,而不是 Session 机制。

4.1 添加 JWT 依赖

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.11.5</version>
</dependency>

4.2 实现 JWT 生成和解析

@Component
public class JwtUtil {
    private final String SECRET_KEY = "my_secret_key";

    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60)) // 1小时
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }

    public String extractUsername(String token) {
        return Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token)
                .getBody()
                .getSubject();
    }

    public boolean validateToken(String token, UserDetails userDetails) {
        return userDetails.getUsername().equals(extractUsername(token));
    }
}

4.3 JWT 过滤器

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        String token = request.getHeader("Authorization");
        if (token != null && token.startsWith("Bearer ")) {
            token = token.substring(7);
            String username = jwtUtil.extractUsername(token);
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                if (jwtUtil.validateToken(token, userDetails)) {
                    UsernamePasswordAuthenticationToken authToken =
                            new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    SecurityContextHolder.getContext().setAuthentication(authToken);
                }
            }
        }
        chain.doFilter(request, response);
    }
}

4.4 在 SecurityConfig 中注册 JWT 过滤器

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Autowired
    private JwtAuthenticationFilter jwtFilter;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(AbstractHttpConfigurer::disable) // 关闭 CSRF(因为是无状态的)
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated()
            )
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class); // 加入 JWT 过滤器
        return http.build();
    }
}

5. 总结

Spring Security 是 Spring 生态系统中最强大的安全框架,它提供了:
强大的身份认证和授权机制
默认安全策略,防止常见攻击
支持多种认证方式(JWT、OAuth2、LDAP、SSO)
可扩展的安全过滤器链

对于 Web 应用,可以使用 默认表单登录OAuth2 登录
对于 REST API,推荐使用 JWT 进行无状态认证

如果你正在开发 Spring Boot 应用,Spring Security 是 最推荐的安全框架!🚀

Comments |0|

Legend *) Required fields are marked
**) You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
Category: 似水流年