此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Security 6.3.1Spring中文文档

此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Security 6.3.1Spring中文文档

Spring 3.1 中添加了对 Java 配置的一般支持。 Spring Security 3.2 引入了 Java 配置,让用户无需使用任何 XML 即可配置 Spring Security。Spring中文文档

如果您熟悉安全命名空间配置,您应该会发现它与 Spring Security Java 配置之间有很多相似之处。Spring中文文档

Spring Security 提供了许多示例应用程序来演示 Spring Security Java 配置的使用。Spring中文文档

Spring Security 提供了许多示例应用程序来演示 Spring Security Java 配置的使用。Spring中文文档

Hello Web Security Java 配置

第一步是创建我们的 Spring Security Java 配置。 该配置将创建一个称为 的 Servlet 过滤器,该过滤器负责应用程序中的所有安全性(保护应用程序 URL、验证提交的用户名和密码、重定向到登录表单等)。 以下示例显示了 Spring Security Java 配置的最基本示例:springSecurityFilterChainSpring中文文档

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.*;
import org.springframework.security.config.annotation.authentication.builders.*;
import org.springframework.security.config.annotation.web.configuration.*;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

	@Bean
	public UserDetailsService userDetailsService() {
		InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
		manager.createUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build());
		return manager;
	}
}

此配置并不复杂或广泛,但它做了很多:Spring中文文档

AbstractSecurityWebApplicationInitializer

下一步是向 WAR 文件注册。 您可以在 Servlet 3.0+ 环境中使用 Spring 的 WebApplicationInitializer 支持在 Java 配置中执行此操作。 毫不奇怪,Spring Security 提供了一个基类 () 来确保为您注册。 我们的使用方式有所不同,这取决于我们是否已经在使用 Spring,或者 Spring Security 是否是我们应用程序中唯一的 Spring 组件。springSecurityFilterChainAbstractSecurityWebApplicationInitializerspringSecurityFilterChainAbstractSecurityWebApplicationInitializerSpring中文文档

没有现有 Spring 的 AbstractSecurityWebApplicationInitializer

如果您不使用 Spring 或 Spring MVC,则需要将 传递给超类以确保选取配置:WebSecurityConfigSpring中文文档

import org.springframework.security.web.context.*;

public class SecurityWebApplicationInitializer
	extends AbstractSecurityWebApplicationInitializer {

	public SecurityWebApplicationInitializer() {
		super(WebSecurityConfig.class);
	}
}

这:SecurityWebApplicationInitializerSpring中文文档

带有 Spring MVC 的 AbstractSecurityWebApplicationInitializer

如果我们在应用程序的其他地方使用 Spring,我们可能已经有一个正在加载我们的 Spring 配置。 如果我们使用以前的配置,我们会得到一个错误。 相反,我们应该将 Spring Security 注册到现有的 . 例如,如果我们使用 Spring MVC,我们的 MVC 可能如下所示:WebApplicationInitializerApplicationContextSecurityWebApplicationInitializerSpring中文文档

import org.springframework.security.web.context.*;

public class SecurityWebApplicationInitializer
	extends AbstractSecurityWebApplicationInitializer {

}

这只会在应用程序中注册每个 URL。 在那之后,我们需要确保它已加载到我们现有的 . 例如,如果我们使用 Spring MVC,它会添加到:springSecurityFilterChainWebSecurityConfigApplicationInitializergetServletConfigClasses()Spring中文文档

public class MvcWebApplicationInitializer extends
		AbstractAnnotationConfigDispatcherServletInitializer {

	@Override
	protected Class<?>[] getServletConfigClasses() {
		return new Class[] { WebSecurityConfig.class, WebMvcConfig.class };
	}

	// ... other overrides ...
}

这样做的原因是 Spring Security 需要能够检查某些 Spring MVC 配置,以便适当地配置底层请求匹配器,因此它们需要处于相同的应用程序上下文中。 将 Spring Security 放置在 Spring Security 中,该上下文可能无法找到 Spring MVC 的 .getRootConfigClassesHandlerMappingIntrospectorSpring中文文档

配置多个 Spring MVC 调度程序

如果需要,任何与 Spring MVC 无关的 Spring Security 配置都可以放置在不同的配置类中,如下所示:Spring中文文档

public class MvcWebApplicationInitializer extends
		AbstractAnnotationConfigDispatcherServletInitializer {

	@Override
    protected Class<?>[] getRootConfigClasses() {
		return new Class[] { NonWebSecurityConfig.class };
    }

	@Override
	protected Class<?>[] getServletConfigClasses() {
		return new Class[] { WebSecurityConfig.class, WebMvcConfig.class };
	}

	// ... other overrides ...
}

如果您有多个实例,并且不想在这两个实例之间复制常规安全配置,这可能很有帮助。AbstractAnnotationConfigDispatcherServletInitializerSpring中文文档

Http安全

到目前为止,我们的 WebSecurityConfig 仅包含有关如何对用户进行身份验证的信息。 Spring Security 如何知道我们要要求所有用户都进行身份验证? Spring Security 如何知道我们要支持基于表单的身份验证? 实际上,有一个配置类(称为 )正在后台调用。 它配置了以下默认实现:SecurityFilterChainSpring中文文档

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

默认配置(如前面的示例所示):Spring中文文档

请注意,此配置与 XML 命名空间配置并行:Spring中文文档

<http>
	<intercept-url pattern="/**" access="authenticated"/>
	<form-login />
	<http-basic />
</http>

多个 HttpSecurity 实例

我们可以配置多个实例,就像我们可以在 XML 中拥有多个块一样。 关键是注册多个 s。 以下示例对以 开头的 URL 具有不同的配置。HttpSecurity<http>SecurityFilterChain@Bean/api/Spring中文文档

@Configuration
@EnableWebSecurity
public class MultiHttpSecurityConfig {
	@Bean                                                             (1)
	public UserDetailsService userDetailsService() throws Exception {
		// ensure the passwords are encoded properly
		UserBuilder users = User.withDefaultPasswordEncoder();
		InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
		manager.createUser(users.username("user").password("password").roles("USER").build());
		manager.createUser(users.username("admin").password("password").roles("USER","ADMIN").build());
		return manager;
	}

	@Bean
	@Order(1)                                                        (2)
	public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
		http
			.securityMatcher("/api/**")                              (3)
			.authorizeHttpRequests(authorize -> authorize
				.anyRequest().hasRole("ADMIN")
			)
			.httpBasic(withDefaults());
		return http.build();
	}

	@Bean                                                            (4)
	public SecurityFilterChain formLoginFilterChain(HttpSecurity http) throws Exception {
		http
			.authorizeHttpRequests(authorize -> authorize
				.anyRequest().authenticated()
			)
			.formLogin(withDefaults());
		return http.build();
	}
}
1 像往常一样配置身份验证。
2 创建一个包含的实例,以指定应首先考虑哪个实例。SecurityFilterChain@OrderSecurityFilterChain
3 声明这仅适用于以 开头的 URL。http.securityMatcherHttpSecurity/api/
4 创建 的另一个实例。 如果 URL 不以 开头,则使用此配置。 此配置被视为 after ,因为它有一个 after 值(没有默认值为 last)。SecurityFilterChain/api/apiFilterChain@Order1@Order
1 像往常一样配置身份验证。
2 创建一个包含的实例,以指定应首先考虑哪个实例。SecurityFilterChain@OrderSecurityFilterChain
3 声明这仅适用于以 开头的 URL。http.securityMatcherHttpSecurity/api/
4 创建 的另一个实例。 如果 URL 不以 开头,则使用此配置。 此配置被视为 after ,因为它有一个 after 值(没有默认值为 last)。SecurityFilterChain/api/apiFilterChain@Order1@Order

自定义 DSL

您可以在 Spring Security 中提供自己的自定义 DSL:Spring中文文档

public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {
	private boolean flag;

	@Override
	public void init(HttpSecurity http) throws Exception {
		// any method that adds another configurer
		// must be done in the init method
		http.csrf().disable();
	}

	@Override
	public void configure(HttpSecurity http) throws Exception {
		ApplicationContext context = http.getSharedObject(ApplicationContext.class);

		// here we lookup from the ApplicationContext. You can also just create a new instance.
		MyFilter myFilter = context.getBean(MyFilter.class);
		myFilter.setFlag(flag);
		http.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter.class);
	}

	public MyCustomDsl flag(boolean value) {
		this.flag = value;
		return this;
	}

	public static MyCustomDsl customDsl() {
		return new MyCustomDsl();
	}
}
class MyCustomDsl : AbstractHttpConfigurer<MyCustomDsl, HttpSecurity>() {
    var flag: Boolean = false

    override fun init(http: HttpSecurity) {
        // any method that adds another configurer
        // must be done in the init method
        http.csrf().disable()
    }

    override fun configure(http: HttpSecurity) {
        val context: ApplicationContext = http.getSharedObject(ApplicationContext::class.java)

        // here we lookup from the ApplicationContext. You can also just create a new instance.
        val myFilter: MyFilter = context.getBean(MyFilter::class.java)
        myFilter.setFlag(flag)
        http.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter::class.java)
    }

    companion object {
        @JvmStatic
        fun customDsl(): MyCustomDsl {
            return MyCustomDsl()
        }
    }
}

这实际上是 LIKE 方法的实现方式。HttpSecurity.authorizeHttpRequests()Spring中文文档

然后,您可以使用自定义 DSL:Spring中文文档

@Configuration
@EnableWebSecurity
public class Config {
	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.with(MyCustomDsl.customDsl(), (dsl) -> dsl
				.flag(true)
			)
			// ...
		return http.build();
	}
}
@Configuration
@EnableWebSecurity
class Config {

    @Bean
    fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http
            .with(MyCustomDsl.customDsl()) {
                flag = true
            }
            // ...

        return http.build()
    }
}

代码按以下顺序调用:Spring中文文档

如果需要,默认情况下可以使用 添加。 例如,您可以在类路径上创建具有以下内容的资源:HttpSecurityMyCustomDslSpringFactoriesMETA-INF/spring.factoriesSpring中文文档

META-INF/spring.factories
org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyCustomDsl

您还可以显式禁用默认值:Spring中文文档

@Configuration
@EnableWebSecurity
public class Config {
	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.with(MyCustomDsl.customDsl(), (dsl) -> dsl
				.disable()
			)
			...;
		return http.build();
	}
}
@Configuration
@EnableWebSecurity
class Config {

    @Bean
    fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http
            .with(MyCustomDsl.customDsl()) {
                disable()
            }
            // ...
        return http.build()
    }

}

这实际上是 LIKE 方法的实现方式。HttpSecurity.authorizeHttpRequests()Spring中文文档

后处理配置的对象

Spring Security 的 Java 配置不会公开它配置的每个对象的每个属性。 这简化了大多数用户的配置。 毕竟,如果每个属性都公开,用户可以使用标准的 Bean 配置。Spring中文文档

虽然有充分的理由不直接公开每个属性,但用户可能仍然需要更高级的配置选项。 为了解决这个问题,Spring Security 引入了 的概念,它可用于修改或替换 Java 配置创建的许多实例。 例如,若要配置 上的属性,可以使用以下命令:ObjectPostProcessorObjectfilterSecurityPublishAuthorizationSuccessFilterSecurityInterceptorSpring中文文档

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
	http
		.authorizeHttpRequests(authorize -> authorize
			.anyRequest().authenticated()
			.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
				public <O extends FilterSecurityInterceptor> O postProcess(
						O fsi) {
					fsi.setPublishAuthorizationSuccess(true);
					return fsi;
				}
			})
		);
	return http.build();
}