Spring Security 常规用法详解:从基础配置到高级控制

Spring Security 是 Java 领域最流行的安全框架之一,本文将深入讲解其常规用法,包括基础配置、自定义认证、方法级权限控制和 JWT 集成等核心功能。

一、基础配置

1.1 最小化安全配置

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login").permitAll()
            .and()
            .logout().permitAll();
    }
}

配置解析

  • authorizeRequests():开始权限配置
  • antMatchers("/public/**").permitAll():允许匿名访问/public路径
  • anyRequest().authenticated():其他所有请求需要认证
  • formLogin().loginPage("/login"):自定义登录页路径

实践建议

  • 生产环境应启用CSRF防护(默认已启用)
  • 静态资源(CSS/JS)应配置在permitAll路径中
  • 登录页和退出端点通常需要开放访问权限

二、自定义用户认证

2.1 基于数据库的用户认证

@Bean
public UserDetailsService userDetailsService() {
    return username -> {
        User user = userRepository.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("用户不存在");
        }
        return new org.springframework.security.core.userdetails.User(
            user.getUsername(), 
            user.getPassword(), 
            AuthorityUtils.createAuthorityList("ROLE_USER")
        );
    };
}

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

组件说明

  • UserDetailsService:核心接口,负责加载用户数据
  • PasswordEncoder:密码编码器,推荐使用BCrypt

认证流程

图1

实践建议

  • 密码必须加密存储,禁止明文
  • 用户状态(启用/禁用)应通过UserDetails的isEnabled等方法控制
  • 复杂系统可考虑实现自定义AuthenticationProvider

三、方法级权限控制

3.1 注解式权限控制

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
}

@Service
public class AdminService {
    
    @PreAuthorize("hasRole('ADMIN')")
    public void deleteUser(Long userId) {
        // 管理员专属操作
    }
    
    @PreAuthorize("#username == authentication.name")
    public void updateProfile(String username, Profile profile) {
        // 只能修改自己的资料
    }
    
    @PostAuthorize("returnObject.owner == authentication.name")
    public Document getDocument(Long docId) {
        // 返回后检查权限
    }
}

注解说明

  • @PreAuthorize:方法执行前检查权限
  • @PostAuthorize:方法执行后检查返回值权限
  • @Secured:简单的角色检查

SpEL表达式示例

  • hasRole('ADMIN'):检查管理员角色
  • #variable:访问方法参数
  • authentication.name:当前用户名

实践建议

  • 优先使用@PreAuthorize而非@Secured(功能更强大)
  • 复杂权限逻辑可封装为自定义PermissionEvaluator
  • 服务层方法应进行权限检查,而不仅依赖Web层防护

四、JWT集成

4.1 JWT认证配置

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .csrf().disable()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
        .authorizeRequests()
            .antMatchers("/api/auth/**").permitAll()
            .anyRequest().authenticated();
}

@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
    return new JwtAuthenticationFilter();
}

4.2 JWT过滤器示例

public class JwtAuthenticationFilter extends OncePerRequestFilter {
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                  HttpServletResponse response, 
                                  FilterChain chain) throws IOException, ServletException {
        
        String token = resolveToken(request);
        if (token != null && validateToken(token)) {
            Authentication auth = getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(auth);
        }
        chain.doFilter(request, response);
    }
    
    private String resolveToken(HttpServletRequest request) {
        // 从Header或Cookie中提取Token
    }
    
    private boolean validateToken(String token) {
        // 验证Token有效性
    }
    
    private Authentication getAuthentication(String token) {
        // 根据Token创建Authentication对象
    }
}

JWT流程

图2

实践建议

  • 令牌应设置合理的过期时间
  • 考虑实现令牌刷新机制
  • 敏感操作应使用短期有效的令牌
  • 生产环境应启用HTTPS保护令牌传输

五、最佳实践总结

  1. 分层防护:Web层+方法层双重防护
  2. 最小权限:遵循最小权限原则分配角色
  3. 防御CSRF:表单操作启用CSRF防护
  4. 密码安全:使用强哈希算法(BCrypt/Argon2)
  5. 监控审计:记录重要安全事件
  6. 定期复审:定期检查权限分配合理性

通过合理运用这些常规配置模式,可以构建既安全又灵活的应用程序防护体系。Spring Security的强大之处在于它的可扩展性,开发者可以根据实际需求在这些基础模式上进行定制开发。

评论已关闭