Spring Security实战:全方位保护Spring Boot应用(附完整代码)

153 阅读3分钟

在当今数字化时代,应用安全已成为开发者的必修课。本文将带您深入Spring Security核心机制,构建企业级安全防护体系,涵盖认证、授权、攻击防护等关键领域。

一、Spring Security核心架构解析

​编辑

安全四层防护体系:
​​认证层​​:验证用户身份(你是谁?)
​​授权层​​:控制访问权限(你能做什么?)
​​会话层​​:管理用户会话状态
​​攻击防护​​:抵御CSRF/XSS等攻击

二、快速搭建安全防护

项目初始化

    org.springframework.boot     spring-boot-starter-security     org.springframework.boot     spring-boot-starter-web

基础安全配置

@Configuration
@EnableWebSecurity
public class BasicSecurityConfig extends WebSecurityConfigurerAdapter {

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

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

三、企业级安全实践

数据库动态认证

@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("用户不存在"));

return org.springframework.security.core.userdetails.User
.withUsername(user.getUsername())
.password(user.getPassword())
.authorities(user.getRoles())
.accountExpired(!user.isAccountNonExpired())
.credentialsExpired(!user.isCredentialsNonExpired())
.disabled(!user.isEnabled())
.accountLocked(!user.isAccountNonLocked())
.build();
}
}

RBAC权限控制模型

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

@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler handler = 
new DefaultMethodSecurityExpressionHandler();
handler.setPermissionEvaluator(new CustomPermissionEvaluator());
return handler;
}
}

// 控制器权限控制
@RestController
@RequestMapping("/api/admin")
public class AdminController {

    @PreAuthorize("hasRole('ADMIN')")
@GetMapping("/users")
public List getAllUsers() {
// 管理员专属接口
}

    @PreAuthorize("hasPermission(#id, 'user', 'DELETE')")
@DeleteMapping("/users/{id}")
public void deleteUser(@PathVariable Long id) {
// 自定义权限校验
}
}

JWT无状态认证

@Configuration
public class JwtSecurityConfig {

    @Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

return http.build();
}

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

四、高级安全防护策略

防止暴力破解

@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}

@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder());
provider.setPreAuthenticationChecks(preAuthenticationChecks());
return provider;
}

private PreAuthenticationChecks preAuthenticationChecks() {
return user -> {
if (user instanceof CustomUserDetails) {
CustomUserDetails details = (CustomUserDetails) user;
if (loginAttemptService.isBlocked(details.getUsername())) {
throw new LockedException("账户已被锁定");
}
}
};
}

CSRF与CORS防护

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringAntMatchers("/api/public/**")
)
.cors(cors -> cors
.configurationSource(corsConfigurationSource())
);
return http.build();
}

@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(Arrays.asList("trusted-domain.com"));
config.setAllowedMethods(Arrays.asList("GET", "POST"));
config.setAllowCredentials(true);

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

安全头部防护

http.headers(headers -> headers
.contentSecurityPolicy(csp -> csp
.policyDirectives("default-src 'self'; script-src 'self' 'unsafe-inline'")
)
.frameOptions().sameOrigin()
.httpStrictTransportSecurity().includeSubDomains().maxAgeInSeconds(31536000)
.xssProtection().block(true)
);

五、OAuth2社交登录集成

配置GitHub登录

@Configuration
public class OAuth2LoginConfig {

    @Bean
public ClientRegistrationRepository clientRegistrationRepository() {
return new InMemoryClientRegistrationRepository(githubClientRegistration());
}

    private ClientRegistration githubClientRegistration() {
return ClientRegistration.withRegistrationId("github")
.clientId("your-client-id")
.clientSecret("your-client-secret")
.scope("read:user")
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")
.authorizationUri("github.com/login/oauth…")
.tokenUri("github.com/login/oauth…")
.userInfoUri("api.github.com/user")
.userNameAttributeName("login")
.clientName("GitHub")
.build();
}
}

自定义OAuth2用户服务

@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {

    @Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) {
OAuth2User user = super.loadUser(userRequest);

Map<String, Object> attributes = user.getAttributes();
String email = (String) attributes.get("email");

// 查找或创建本地用户
User localUser = userService.findOrCreateUser(email, 
userRequest.getClientRegistration().getRegistrationId());

return new CustomOAuth2User(user, localUser.getRoles());
}
}

六、安全事件监控与审计

审计日志配置

@Bean
public AuditEventRepository auditEventRepository() {
return new InMemoryAuditEventRepository();
}

@Bean
public AuditorAware auditorAware() {
return () -> Optional.ofNullable(SecurityContextHolder.getContext())
.map(SecurityContext::getAuthentication)
.map(Authentication::getName)
.orElse("SYSTEM");
}

自定义审计事件

@Component
public class SecurityAuditListener {

    @EventListener
public void onAuthSuccess(AuthenticationSuccessEvent event) {
log.info("登录成功: {}", event.getAuthentication().getName());
}

    @EventListener
public void onAuthFailure(AbstractAuthenticationFailureEvent event) {
log.warn("登录失败: {}", event.getAuthentication().getName());
}
}

七、完整项目结构

spring-security-demo/
├── src/main/
│   ├── java/
│   │   └── com/example/demo/
│   │       ├── config/       # 安全配置
│   │       ├── controller/   # API控制器
│   │       ├── model/        # 数据模型
│   │       ├── repository/   # 数据访问
│   │       ├── security/     # 安全核心
│   │       │   ├── jwt/      # JWT组件
│   │       │   ├── oauth2/   # OAuth2集成
│   │       │   └── rbac/     # 权限模型
│   │       └── DemoApplication.java
│   └── resources/
│       ├── application.yml
│       └── static/
├── docker-compose.yml
└── pom.xml

├── application.yml

└── static/

├── docker-compose.yml
└── pom.xml

八、最佳实践总结

认证策略选择​​:

  • 内部系统:Session + CSRF Token
  • 微服务架构:JWT/OAuth2
  • 社交登录:OAuth2 + OpenID Connect

密码安全原则:

// 使用BCrypt加密
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(12);
String encodedPassword = encoder.encode("plainPassword");

// 验证密码
boolean matches = encoder.matches("plainPassword", encodedPassword);

安全测试要点:

   @SpringBootTest
@AutoConfigureMockMvc
class SecurityTests {

@Test
@WithMockUser(roles = "USER")
void userAccessTest() throws Exception {
mockMvc.perform(get("/user/profile"))
.andExpect(status().isOk());
}

@Test
void adminAccessDeniedTest() throws Exception {
mockMvc.perform(get("/admin/dashboard"))
.andExpect(status().isForbidden());
}
}

PS:安全不是功能,而是持续的过程。定期进行安全审计和渗透测试,保持依赖库更新,才能构建真正可靠的防护体系。

扩展阅读:
Spring Security官方文档:spring.io/projects/sp…

OWASP Top 10防护指南:owasp.org/www-project…