Spring Security 配置

Spring Security 6 模式:SecurityFilterChain DSL、JWT 过滤器、BCrypt 密码编码、方法级安全和 CORS 配置。

1. SecurityFilterChain

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
            .csrf(csrf -> csrf.disable())
            .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/auth/**", "/public/**").permitAll()
                .requestMatchers(HttpMethod.GET, "/articles/**").permitAll()
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
            .build();
    }
}

2. JWT 认证过滤器

@Component
public class JwtAuthFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest req,
                                    HttpServletResponse res,
                                    FilterChain chain) throws ServletException, IOException {
        String header = req.getHeader("Authorization");
        if (header == null || !header.startsWith("Bearer ")) {
            chain.doFilter(req, res);
            return;
        }
        String token = header.substring(7);
        String username = jwtService.extractUsername(token);
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails user = userDetailsService.loadUserByUsername(username);
            if (jwtService.isValid(token, user)) {
                var auth = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(auth);
            }
        }
        chain.doFilter(req, res);
    }
}

3. 方法级安全

@DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN') or @articleSecurity.isOwner(#id, authentication)")
public void delete(@PathVariable Long id) { ... }

@Component("articleSecurity")
public class ArticleSecurity {
    public boolean isOwner(Long articleId, Authentication auth) {
        return repo.findById(articleId)
            .map(a -> a.getAuthor().getUsername().equals(auth.getName()))
            .orElse(false);
    }
}

4. URL 授权表达式

表达式含义
permitAll()允许所有人
authenticated()需要登录
hasRole("ADMIN")具有 ROLE_ADMIN 权限
hasAnyRole("A","B")具有任意指定角色
anonymous()必须是匿名用户