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 依赖和从头开始整个设置一样。这在多种情况下都很有用:
- 将应用 Security 与自定义 Security Provider 集成。
- 将已存在 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 还涵盖其他的一些东西:
- Resource Server(资源服务器) - @EnableResourceServer
- 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