1. 概述

本文将深入探讨 Spring Boot 对安全配置的约定优于配置理念。我们将聚焦默认安全配置的实现原理,以及如何根据实际需求禁用或自定义这些配置。

2. 默认安全配置

为 Spring Boot 应用添加安全功能,只需引入安全启动器依赖:

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

该依赖会自动加载 SecurityAutoConfiguration 类,其中包含初始/默认安全配置。注意这里未指定版本号,假设项目已使用 Boot 作为父 POM。

默认情况下,应用会启用认证功能,并通过内容协商机制决定使用 Basic 认证还是表单登录。

系统预定义了以下配置属性:

spring.security.user.name
spring.security.user.password

若未通过 spring.security.user.password 配置密码,启动时会随机生成默认密码并打印到控制台:

Using default security password: c8be15de-4488-4490-9dc6-fab3f91435c6

更多默认配置可参考 Spring Boot 通用应用属性 的安全属性章节。

3. 禁用自动配置

要禁用安全自动配置并添加自定义配置,需排除 SecurityAutoConfiguration 类。可通过以下两种方式实现:

方式一:注解排除

@SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
public class SpringBootSecurityApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootSecurityApplication.class, args);
    }
}

方式二:配置文件排除

spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration

⚠️ 踩坑提醒:某些场景下仅排除 SecurityAutoConfiguration 可能不够。例如当类路径包含 Actuator 时,另一个自动配置类依赖我们刚排除的类,会导致启动失败。此时需额外排除 ManagementWebSecurityAutoConfiguration

3.1 禁用 vs 覆盖自动配置

禁用与覆盖自动配置有本质区别:

  • 禁用:相当于完全移除 Spring Security 的自动配置,需从头构建安全体系。适用于:

    • 集成自定义安全提供商
    • 将已有安全配置的旧版 Spring 应用迁移至 Boot
  • 覆盖:保留自动配置基础,通过添加自定义配置类进行扩展。这是推荐方式,因为只需在现有基础上调整即可满足需求。

经验之谈:大多数情况下无需完全禁用自动配置,Spring Boot 的设计本就支持通过自定义配置覆盖默认行为。

4. 配置 Spring Boot 安全

选择禁用自动配置后,需提供完整的安全配置。最简单的方式是通过属性文件修改默认值,例如覆盖默认密码:

spring.security.user.password=password

若需更灵活配置(如多用户多角色),需使用完整的 @Configuration 类:

@Configuration
@EnableWebSecurity
public class BasicConfiguration {

    @Bean
    public InMemoryUserDetailsManager userDetailsService(PasswordEncoder passwordEncoder) {
        UserDetails user = User.withUsername("user")
            .password(passwordEncoder.encode("password"))
            .roles("USER")
            .build();

        UserDetails admin = User.withUsername("admin")
            .password(passwordEncoder.encode("admin"))
            .roles("USER", "ADMIN")
            .build();

        return new InMemoryUserDetailsManager(user, admin);
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http.authorizeHttpRequests(request -> request.anyRequest()
                .authenticated())
            .httpBasic(Customizer.withDefaults())
            .build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        return encoder;
    }
}

@EnableWebSecurity 注解在禁用默认配置时必不可少,缺失会导致启动失败。

⚠️ 重要:Spring Boot 2 中必须使用 PasswordEncoder 设置密码。详情参考 Spring Security 5 默认密码编码器指南

可通过以下集成测试验证配置:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT)
public class BasicConfigurationIntegrationTest {

    TestRestTemplate restTemplate;
    URL base;
    @LocalServerPort int port;

    @Before
    public void setUp() throws MalformedURLException {
        restTemplate = new TestRestTemplate("user", "password");
        base = new URL("http://localhost:" + port);
    }

    @Test
    public void whenLoggedUserRequestsHomePage_ThenSuccess()
     throws IllegalStateException, IOException {
        ResponseEntity<String> response =
          restTemplate.getForEntity(base.toString(), String.class);
 
        assertEquals(HttpStatus.OK, response.getStatusCode());
        assertTrue(response.getBody().contains("Baeldung"));
    }

    @Test
    public void whenUserWithWrongCredentials_thenUnauthorizedPage() 
      throws Exception {
 
        restTemplate = new TestRestTemplate("user", "wrongpassword");
        ResponseEntity<String> response =
          restTemplate.getForEntity(base.toString(), String.class);
 
        assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
        assertTrue(response.getBody().contains("Unauthorized"));
    }
}

所有 Spring Security 支持的功能均可通过 Boot 实现,因为其底层依赖 Spring Security。

5. Spring Boot OAuth2 自动配置(旧版实现)

Spring Boot 为 OAuth2 提供专用自动配置支持。Spring Boot 1.x 的 Spring Security OAuth 在后续版本中被移除,改用 Spring Security 5 原生支持(见下节)。

使用旧版实现需添加 Maven 依赖:

<dependency>
   <groupId>org.springframework.security.oauth</groupId>
   <artifactId>spring-security-oauth2</artifactId>
</dependency>

该依赖会触发 OAuth2AutoConfiguration 中的自动配置机制。

5.1 OAuth2 授权服务器自动配置

若需将应用作为 OAuth2 提供商,使用 @EnableAuthorizationServer。启动时日志会自动生成:

  • 客户端 ID 和密钥
  • Basic 认证随机密码
Using default security password: a81cb256-f243-40c0-a585-81ce1b952a98
security.oauth2.client.client-id = 39d2835b-1f87-4a77-9798-e2975f36972e
security.oauth2.client.client-secret = f1463f8b-0791-46fe-9269-521b86c55b71

使用这些凭据获取访问令牌:

curl -X POST -u 39d2835b-1f87-4a77-9798-e2975f36972e:f1463f8b-0791-46fe-9269-521b86c55b71 \
 -d grant_type=client_credentials \
 -d username=user \
 -d password=a81cb256-f243-40c0-a585-81ce1b952a98 \
 -d scope=write  http://localhost:8080/oauth/token

更多细节参考我们的 OAuth2 文章

5.2 其他 OAuth2 自动配置场景

Spring Boot OAuth2 还支持:

  1. 资源服务器@EnableResourceServer配置示例
  2. 客户端应用@EnableOAuth2Sso@EnableOAuth2Client配置示例

所有 OAuth2 专用属性见 Spring Boot 通用应用属性

6. Spring Boot OAuth2 自动配置(新版实现)

新版实现需根据目标类型(授权服务器/资源服务器/客户端)添加对应依赖。

6.1 OAuth2 授权服务器支持

Spring Security OAuth 项目已弃用,Spring 官方不再提供自有授权服务器实现。推荐使用成熟方案如:

  • Okta
  • Keycloak
  • ForgeRock

Spring Boot 简化了这些提供商的集成,Keycloak 配置示例参考:

6.2 OAuth2 资源服务器支持

添加资源服务器依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>    
</dependency>

最新版本见 Maven Central

在安全配置中添加 oauth2ResourceServer() DSL:

@Configuration
public class JWTSecurityConfig {
 
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
          ...
          .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
          ...
    }
}

深入解析见 OAuth 2.0 资源服务器指南

6.3 OAuth2 客户端支持

客户端应用需专用依赖:

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

最新版本见 Maven Central

Spring Security 5 通过 oauth2Login() DSL 提供原生登录支持。新版 SSO 实现参考 Spring Security OAuth2 单点登录

7. 总结

本文深入剖析了 Spring Boot 的默认安全配置机制,重点涵盖:

  • 安全自动配置的禁用与覆盖方法
  • 自定义安全配置的实现
  • OAuth2 新旧版本实现的配置差异

核心要点

  1. 默认配置通过 spring-boot-starter-security 激活
  2. 禁用自动配置需排除 SecurityAutoConfiguration
  3. 推荐通过自定义配置类覆盖而非完全禁用
  4. OAuth2 新版实现依赖 spring-boot-starter-oauth2-* 系列依赖

OAuth2 示例代码见 GitHub 仓库:

完整代码参考 Spring Boot 安全模块


原始标题:Spring Boot Security Auto-Configuration

« 上一篇: Java Weekly, 第212期
» 下一篇: 开发Jenkins插件