# Java 配置
在 Spring 3.1 中,对Java 配置 (opens new window)的一般支持被添加到 Spring 框架中。自 Spring Security3.2 以来,已经有了 Spring Security Java 配置支持,它使用户能够轻松地配置 Spring 安全性,而无需使用任何 XML。
如果你熟悉安全名称空间配置,那么你应该会发现它与安全 Java 配置支持之间有很多相似之处。
Spring 安全性提供了大量的示例应用程序 (opens new window),其中演示了 Spring 安全性 Java 配置的使用。 |
---|
# Hello Web 安全 Java 配置
第一步是创建我们的 Spring 安全 Java 配置。该配置创建了一个名为springSecurityFilterChain
的 Servlet 过滤器,该过滤器负责应用程序中的所有安全性(保护应用程序的 URL,验证提交的用户名和密码,重定向到表单中的日志,等等)。你可以在下面找到 Spring 安全性 Java 配置的最基本示例:
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.*;
@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;
}
}
这种配置实际上没有太多的东西,但它做了很多事情。你可以找到以下功能的摘要:
要求对应用程序中的每个 URL 进行身份验证
为你生成一个登录表单
允许使用用户 NameUser和密码密码的用户使用基于表单的身份验证进行身份验证
允许用户注销
安全报头集成
[X-Content-Type-Options](https://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).ASPX)集成
缓存控制(可以稍后由应用程序重写,以允许缓存静态资源)
[X-XSS-保护](https://msdn.microsoft.com/en-us/library/dd565647(v=vs.85).ASPX)积分
x-frame-options 集成,帮助防止点击劫持 (opens new window)
与以下 Servlet API 方法集成
# WebApplicationInitializer
下一步是在 WAR 中注册springSecurityFilterChain
。这可以在 Servlet 3.0+ 环境中使用Spring’s WebApplicationInitializer support (opens new window)在 Java 配置中完成。 Spring Security 提供了一个基类AbstractSecurityWebApplicationInitializer
,它将确保springSecurityFilterChain
为你注册。我们使用AbstractSecurityWebApplicationInitializer
的方式有所不同,这取决于我们是否已经在使用 Spring 或者 Spring 安全性是我们应用程序中唯一的 Spring 组件。
不存在的安全 WebApplicationInitializer Spring-如果你尚未使用 Spring,请使用以下说明
带有 Spring MVC 的安全 WebApplicationInitializer-如果你已经在使用这些指令 Spring
# AbstractSecurityWebApplicationInitializer without Existing Spring
如果不使用 Spring 或 Spring MVC,则需要将WebSecurityConfig
传递到超类中,以确保拾取配置。你可以在下面找到一个例子:
import org.springframework.security.web.context.*;
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
public SecurityWebApplicationInitializer() {
super(WebSecurityConfig.class);
}
}
SecurityWebApplicationInitializer
将执行以下操作:
为应用程序中的每个 URL 自动注册 SpringSecurityFilterchain 过滤器
添加一个加载WebSecurityConfig的 ContextLoaderListener。
# AbstractSecurityWebApplicationInitializer with Spring MVC
如果我们在应用程序的其他地方使用 Spring,我们可能已经有一个WebApplicationInitializer
正在加载我们的 Spring 配置。如果我们使用以前的配置,我们将得到一个错误。相反,我们应该使用现有的ApplicationContext
注册 Spring 安全性。例如,如果我们使用 Spring MVC,我们的SecurityWebApplicationInitializer
将如下所示:
import org.springframework.security.web.context.*;
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
}
这只会为应用程序中的每个 URL 注册 SpringSecurityFilterchain 过滤器。在此之后,我们将确保WebSecurityConfig
已加载到我们现有的应用程序初始化器中。例如,如果我们使用 Spring MVC,它将被添加到getRootConfigClasses()
中
public class MvcWebApplicationInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { WebSecurityConfig.class };
}
// ... other overrides ...
}
# HttpSecurity
到目前为止,我们的WebSecurityConfig仅包含有关如何对用户进行身份验证的信息。 Spring 安全性如何知道我们希望要求对所有用户进行身份验证? Spring 安全性如何知道我们希望支持基于表单的身份验证?实际上,有一个正在幕后调用的配置类叫做WebSecurityConfigurerAdapter
。它有一个名为configure
的方法,其默认实现如下:
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
)
.formLogin(withDefaults())
.httpBasic(withDefaults());
}
上面的默认配置:
确保对我们的应用程序的任何请求都需要对用户进行身份验证。
允许用户通过基于表单的登录进行身份验证
允许用户使用 HTTP Basic 身份验证进行身份验证
你将注意到此配置与 XML 名称空间配置非常相似:
<http>
<intercept-url pattern="/**" access="authenticated"/>
<form-login />
<http-basic />
</http>
# 多重 HttpSecurity
我们可以配置多个 HttpSecurity 实例,就像我们可以配置多个<http>
块一样。关键是将WebSecurityConfigurerAdapter
多次扩展。例如,下面是一个以/api/
开头的不同 URL 配置的示例。
@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;
}
@Configuration
@Order(1) (2)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**") (3)
.authorizeHttpRequests(authorize -> authorize
.anyRequest().hasRole("ADMIN")
)
.httpBasic(withDefaults());
}
}
@Configuration (4)
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.formLogin(withDefaults());
}
}
}
1 | 将身份验证配置为常规身份验证 |
---|---|
2 | 创建一个包含@Order 的WebSecurityConfigurerAdapter 实例,以指定应该首先考虑哪个WebSecurityConfigurerAdapter 。 |
3 | http.antMatcher 声明此HttpSecurity 将仅适用于以/api/ 开头的 URL |
4 | 创建WebSecurityConfigurerAdapter 的另一个实例。如果 URL 不以 /api/ 开始,将使用此配置。此配置在 ApiWebSecurityConfigurationAdapter 之后被考虑,因为它在1 之后有一个@Order 值(不@Order 默认为后)。 |
# 定制 DSL
你可以在 Spring Security 中提供自己的自定义 DSL。例如,你可能有这样的东西:
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();
}
}
这实际上是HttpSecurity.authorizeRequests() 之类的方法的实现方式。 |
---|
然后可以这样使用定制的 DSL:
@EnableWebSecurity
public class Config extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.apply(customDsl())
.flag(true)
.and()
...;
}
}
该代码按以下顺序调用:
调用
Config
s configure 方法中的代码调用
MyCustomDsl
s init 方法中的代码调用
MyCustomDsl
s configure 方法中的代码
如果需要,可以使用SpringFactories
,在默认情况下将WebSecurityConfigurerAdapter
添加到MyCustomDsl
。例如,你将在 Classpath 上创建一个名为META-INF/ Spring.工厂
的资源,其内容如下:
META-INF/spring.factories
org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyCustomDsl
希望禁用默认值的用户可以显式地这样做。
@EnableWebSecurity
public class Config extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.apply(customDsl()).disable()
...;
}
}
# 已配置对象的后处理
Spring Security 的 Java 配置不会公开它配置的每个对象的每个属性。这简化了大多数用户的配置。毕竟,如果每个属性都被公开,那么用户可以使用标准的 Bean 配置。
尽管有充分的理由不直接公开每个属性,但用户可能仍然需要更高级的配置选项。 Spring 为了解决这一安全问题,引入了ObjectPostProcessor
的概念,该概念可用于修改或替换由 Java 配置创建的许多对象实例。例如,如果要在FilterSecurityInterceptor
上配置filterSecurityPublishAuthorizationSuccess
属性,可以使用以下方法:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
public <O extends FilterSecurityInterceptor> O postProcess(
O fsi) {
fsi.setPublishAuthorizationSuccess(true);
return fsi;
}
})
);
}