51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Spring Boot 中 Spring Security 自动配置

1、概览 {#1概览}

本文将带你了解 Spring Boot 中 Spring Security 的自动配置、默认安全配置,以及如何在需要时禁用或自定义它。

2、默认的 Spring Security 设置 {#2默认的-spring-security-设置}

首先添加 security starter 依赖:

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

这包含初始/默认 Security 配置的 SecurityAutoConfiguration 类。

这里没有指定版本,因为项目使用了 spring-boot-starter-parent 作为 parent

默认情况下,应用会启用身份验证,内容协商(Content Negotiation)用于确定应使用 basic 还是 formLogin

有一些预定义的配置属性:

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

如果不使用预定义属性 spring.security.user.password 配置密码并启动应用,默认密码将随机生成并打印在控制台日志中:

Using default security password: c8be15da-4489-4491-9dc6-fab3f91435c7

有关更多默认值,请参阅 Spring Boot 中文文档中的 属性配置

3、禁用自动配置 {#3禁用自动配置}

要禁止 Security 自动配置并添加我们的自定义配置,需要排除 SecurityAutoConfiguration 自动配置类。

可以通过 @SpringBootApplication 注解中的 exclude 属性来实现:

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

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

或者,可以在 application.properties 文件中添加一些配置:

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

不过,在某些特殊情况下,这种设置也是不够的。

例如,几乎每个 Spring Boot 应用都在 classpath 中启动了 Actuator。这会导致问题,因为另一个自动配置类需要我们刚刚排除掉的那个。因此,应用将无法启动。

为了解决这个问题,需要排除那个类;特别是在 Actuato 的情况下,还需要排除 ManagementWebSecurityAutoConfiguration

3.1、"禁用" 和 "绕过" Security 自动配置 {#31禁用-和-绕过-security-自动配置}

禁用自动配置和绕过自动配置之间有很大区别。

禁用它就像添加 Spring Security 依赖和从头开始整个设置一样。这在多种情况下都很有用:

  1. 将应用 Security 与自定义 Security Provider 集成。
  2. 将已存在 Security 设置的遗留 Spring 应用迁移到 Spring Boot。

但大多数情况下,我们并不需要完全禁用 Security 自动配置。

这是因为 Spring Boot 的配置允许我们通过添加自定义配置类来绕过自动配置的 Security。这通常比较容易,因为我们只需要自定义现有的 Security 设置,以满足我们的需求。

4、Spring Boot 配置 Spring Security {#4spring-boot-配置-spring-security}

如果选择了禁用 Security 自动配置,自然就需要提供自己的配置。

如上所述,默认的 Security 配置,可以通过修改 properties 文件对其进行自定义。

例如,可以通过添加自己的密码来覆盖默认密码:

spring.security.user.password=123456

如果想要更灵活的配置,例如多用户和多角色配置,就需要使用完整的 @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;
    }
}

如果我们禁用默认的 Security 配置,@EnableWebSecurity 注解就显得至关重要。

如果缺失,应用将无法启动。

另外,在使用 Spring Boot 2 时,需要使用 PasswordEncoder 来设置密码。更多详情,请参阅 官方文档

现在,通过几个测试来验证我们的 Security 配置是否正确应用:

@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 背后是 Spring Boot Security,因此可以在 Spring Boot 中完成的任何 Security 配置或支持的任何集成也都可以在 Spring Boot 中实现。

5、Spring Boot OAuth2 自动配置(传统技术栈) {#5spring-boot-oauth2-自动配置传统技术栈}

Spring Boot 有专门的 OAuth2 自动配置支持。

随着 Spring Boot 1.x 后续版本的推出,Spring Security OAuth 支持被移除,取而代之的是与 Spring Security 5 捆绑在一起的 OAuth 支持。在下一节中,我们将看到如何使用它

对于传统技术栈(使用 Spring Security OAuth),首先需要添加一个 Maven 依赖项

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

该依赖包括一组能够触发 OAuth2AutoConfiguration 类中定义的自动配置机制的类。

5.1、OAuth2 Authorization Server 的自动配置 {#51oauth2-authorization-server-的自动配置}

如果希望应用成为 OAuth2 Provider,可以使用 @EnableAuthorizationServer

启动时,可以在日志中看到,自动配置类将为授权服务器生成一个 client id 和一个 client secret ,当然还有一个随机密码用于 basic authentication

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

这些凭证可用于获取 Access Token:

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

5.2、其他 Spring Boot OAuth2 自动配置设置 {#52其他-spring-boot-oauth2-自动配置设置}

Spring Boot OAuth2 还涵盖其他的一些东西:

  1. Resource Server(资源服务器) - @EnableResourceServer
  2. Client Application(客户端应用) - @EnableOAuth2Sso@EnableOAuth2Client

如果需要我们的应用成为这些类型中的一种,只需在 application properties 中添加一些配置即可,详情请参阅 属性中文文档

6、Spring Boot OAuth2 自动配置(使用新技术栈) {#6spring-boot-oauth2-自动配置使用新技术栈}

要使用新的技术栈,需要根据要配置的内容(授权服务器、资源服务器或客户端应用)添加依赖。

让我们一个一个来看看。

6.1、OAuth2 授权服务器支持 {#61oauth2-授权服务器支持}

Spring Security OAuth 提供了将授权服务器设置为 Spring 应用的可能性。但该项目已被弃用,Spring 目前也不支持自己的授权服务器。建议使用现有的成熟 Provider,如 Okta、Keycloak 和 ForgeRock。

不过,Spring Boot 可以让我们轻松配置此类 Provider。有关 Keycloak 配置的示例,可以参考《在 Spring Boot 中使用 Keycloak》或《在 Spring Boot 中嵌入 Keycloak 服务器》。

6.2、OAuth2 资源服务器支持 {#62oauth2-资源服务器支持}

要加入对资源服务器的支持,需要添加此依赖:

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

最新版,可以在 Maven Central 中找到。

此外,在 Security 配置中,需要包含 oauth2ResourceServer() DSL:

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

更多细节,你可以参阅 《使用 Spring Security 构建 OAuth 2.0 资源服务器》。

6.3、OAuth2 客户端支持 {#63oauth2-客户端支持}

与配置资源服务器的方式类似,客户端应用也需要其依赖和 DSL。

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

同样,最新版可以在 Maven Central 中找到。

Spring Security 5 还通过其 oath2Login() DSL 提供了登录支持。

有关新技术栈中 SSO 支持的详细信息,可以参阅文章《使用 Spring Security OAuth2 实现 SSO 单点登录》。

7、总结 {#7总结}

本文介绍了 Spring Boot 中 Spring Security 提供的默认安全(Security)配置,以及如何禁用或覆盖 Security 自动配置机制。最后,还介绍了如何应用新的 Security 配置。


Ref:https://www.baeldung.com/spring-boot-security-autoconfiguration

赞(1)
未经允许不得转载:工具盒子 » Spring Boot 中 Spring Security 自动配置