WebSecurity
是Spring Security
的一个SecurityBuilder
。它的任务是基于一组WebSecurityConfigurer
构建出一个Servlet Filter
,具体来讲就是构建一个Spring Security
的FilterChainProxy
实例。这个FilterChainProxy
实现了Filter
接口,也是通常我们所说的Spring Security Filter Chain
,所构建的FilterChainProxy
实例缺省会使用名称springSecurityFilterChain
作为bean
注册到容器,运行时处理web
请求过程中会使用该bean
进行安全控制。
每个FilterChainProxy
包装了一个HttpFirewall
和若干个SecurityFilterChain
, 这里每个 SecurityFilterChain
要么对应于一个要忽略安全控制的URL
通配符(RequestMatcher
);要么对应于一个要进行安全控制的URL
通配符(HttpSecurity
)。
助记公式 :
1 FilterChainProxy = 1 HttpFirewall + n SecurityFilterChain
WebSecurity
构建目标FilterChainProxy
所使用的WebSecurityConfigurer
实现类通常会继承自WebSecurityConfigurerAdapter
(当然也可以完全实现接口WebSecurityConfigurer
)。每个WebSecurityConfigurerAdapter
可以配置若干个要忽略安全控制的URL
通配符(RequestMatcher
)和一个要进行安全控制的URL
通配符(HttpSecurity
)。
助记公式 :
1 WebSecurity = 1 HttpFirewall + x HttpSecurity (securityFilterChainBuilders) + y RequestMatcher (ignoredRequests)
- 这里
1 HttpSecurity
对应1
个URL pattern
,用于匹配一组需要进行安全配置的请求;- 这里
1 RequestMatcher
对应1
个URL pattern
,用于匹配一组需要忽略安全控制的请求,比如静态公开资源或者其他动态公开资源;- 这里的每个
HttpSecurity
或者RequestMatcher
最终对应构建一个SecurityFilterChain
,这里x+y
会等于上面助记公式中的n
;1 WebSecurity
最终用于构建1 FilterChainProxy
,WebSecurity
的HttpFirewall
如果不存在,则目标FilterChainProxy
会使用缺省值,一个StrictHttpFirewall
;- 每个
WebSecurity
虽然允许设置多个securityFilterChainBuilder
,但Spring Security
在一个WebSecurityConfigurerAdapter
中缺省只向其中添加一个securityFilterChainBuilder
也就是HttpSecurity
。
开发人员可以在应用中提供多个WebSecurityConfigurerAdapter
用于配置Spring Web Security
,但要注意它们的优先级必须不同,这一点可以通过@Order
注解来设置。
package org.springframework.security.config.annotation.web.builders;
// 这里省略了各个 import 导入行
/**
*
* WebSecurity 由 WebSecurityConfiguration 创建,用于创建 FilterChainProxy, 这个 FilterChainProxy
* 也就是通常我们所说的 Spring Security Filter Chain (springSecurityFilterChain)。
* springSecurityFilterChain 是一个 Servlet 过滤器 Filter, DelegatingFilterProxy 会把真正的
* 安全处理逻辑代理给这个 Filter 。
*
* 通过创建一个或者多个 WebSecurityConfigurer, 或者重写 WebSecurityConfigurerAdapter 的某些方法,
* 可以对 WebSecurity 进行定制。
*
*
* @see EnableWebSecurity
* @see WebSecurityConfiguration
*
* @author Rob Winch
* @since 3.2
*/
public final class WebSecurity extends
AbstractConfiguredSecurityBuilder<Filter, WebSecurity> implements
SecurityBuilder<Filter>, ApplicationContextAware {
private final Log logger = LogFactory.getLog(getClass());
// 记录开发人员通过类似下面例子语句指定忽略的URL :
// webSecurity.ignoring().antMatchers("/images/**", "/favicon.ico")
// 在该例子中,会在 ignoredRequests 添加两个元素,分别对应 /images/**, /favicon.ico
private final List<RequestMatcher> ignoredRequests = new ArrayList<RequestMatcher>();
// 最终被构建目标对象FilterChainProxy管理的多个安全过滤器链 SecurityFilterChain 的构建器列表,
// 每个构建器用于构建一个 SecurityFilterChain
private final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders
= new ArrayList<SecurityBuilder<? extends SecurityFilterChain>>();
private IgnoredRequestConfigurer ignoredRequestRegistry;
private FilterSecurityInterceptor filterSecurityInterceptor;
private HttpFirewall httpFirewall;
private boolean debugEnabled;
private WebInvocationPrivilegeEvaluator privilegeEvaluator;
// 初始化缺省的Web安全表达式处理器
private DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler
= new DefaultWebSecurityExpressionHandler();
// 实际使用的Web安全表达式处理器,缺省设置为使用缺省的Web安全表达式处理器
private SecurityExpressionHandler<FilterInvocation> expressionHandler =
defaultWebSecurityExpressionHandler;
private Runnable postBuildAction = new Runnable() {
public void run() {
}
};
/**
* Creates a new instance
* @param objectPostProcessor theObjectPostProcessor to use , 用于对象初始化和设置对象的销毁
* @see WebSecurityConfiguration
*/
public WebSecurity(ObjectPostProcessor<Object> objectPostProcessor) {
super(objectPostProcessor);
}
/**
*
* Allows addingRequestMatcher instances that Spring Security
* should ignore. Web Security provided by Spring Security (including the
* SecurityContext) will not be available on HttpServletRequest that
* match. Typically the requests that are registered should be that of only static
* resources. For requests that are dynamic, consider mapping the request to allow all
* users instead.
*
*
* Example Usage:
*
*
* webSecurityBuilder.ignoring()
* // ignore all URLs that start with /resources/ or /static/
* .antMatchers("/resources/**", "/static/**");
*
*
* Alternatively this will accomplish the same result:
*
*
* webSecurityBuilder.ignoring()
* // ignore all URLs that start with /resources/ or /static/
* .antMatchers("/resources/**").antMatchers("/static/**");
*
*
* Multiple invocations of ignoring() are also additive, so the following is also
* equivalent to the previous two examples:
*
*
* webSecurityBuilder.ignoring()
* // ignore all URLs that start with /resources/
* .antMatchers("/resources/**");
* webSecurityBuilder.ignoring()
* // ignore all URLs that start with /static/
* .antMatchers("/static/**");
* // now both URLs that start with /resources/ and /static/ will be ignored
*
*
* @return the IgnoredRequestConfigurer to use for registering request that
* should be ignored
*/
public IgnoredRequestConfigurer ignoring() {
return ignoredRequestRegistry;
}
/**
* Allows customizing the HttpFirewall. The default is
* DefaultHttpFirewall.
*
* @param httpFirewall the custom HttpFirewall
* @return the WebSecurity for further customizations
*/
public WebSecurity httpFirewall(HttpFirewall httpFirewall) {
this.httpFirewall = httpFirewall;
return this;
}
/**
* Controls debugging support for Spring Security.
*
* 是否启用了调试模式,来自注解 @EnableWebSecurity 的属性 debug,缺省值 false
* @param debugEnabled if true, enables debug support with Spring Security. Default is
* false.
*
* @return the WebSecurity for further customization.
* @see EnableWebSecurity#debug()
*/
public WebSecurity debug(boolean debugEnabled) {
this.debugEnabled = debugEnabled;
return this;
}
/**
*
* Adds builders to create SecurityFilterChain instances.
*
*
*
* Typically this method is invoked automatically within the framework from
* WebSecurityConfigurerAdapter#init(WebSecurity)
*
*
* @param securityFilterChainBuilder the builder to use to create the
* SecurityFilterChain instances
* @return the WebSecurity for further customizations
*/
public WebSecurity addSecurityFilterChainBuilder(
SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder) {
this.securityFilterChainBuilders.add(securityFilterChainBuilder);
return this;
}
/**
* Set the WebInvocationPrivilegeEvaluator to be used. If this is null, then a
* DefaultWebInvocationPrivilegeEvaluator will be created when
* #securityInterceptor(FilterSecurityInterceptor) is non null.
*
* @param privilegeEvaluator the WebInvocationPrivilegeEvaluator to use
* @return the WebSecurity for further customizations
*/
public WebSecurity privilegeEvaluator(
WebInvocationPrivilegeEvaluator privilegeEvaluator) {
this.privilegeEvaluator = privilegeEvaluator;
return this;
}
/**
* 设置实际要使用的 SecurityExpressionHandler. 如果不设置,则缺省使用
* DefaultWebSecurityExpressionHandler .
*
* @param expressionHandler the SecurityExpressionHandler to use
* @return the WebSecurity for further customizations
*/
public WebSecurity expressionHandler(
SecurityExpressionHandler<FilterInvocation> expressionHandler) {
Assert.notNull(expressionHandler, "expressionHandler cannot be null");
this.expressionHandler = expressionHandler;
return this;
}
/**
* Gets the SecurityExpressionHandler to be used.
* @return
*/
public SecurityExpressionHandler<FilterInvocation> getExpressionHandler() {
return expressionHandler;
}
/**
* Gets the WebInvocationPrivilegeEvaluator to be used.
* @return
*/
public WebInvocationPrivilegeEvaluator getPrivilegeEvaluator() {
if (privilegeEvaluator != null) {
return privilegeEvaluator;
}
return filterSecurityInterceptor == null ? null
: new DefaultWebInvocationPrivilegeEvaluator(filterSecurityInterceptor);
}
/**
* Sets the FilterSecurityInterceptor. This is typically invoked by
* WebSecurityConfigurerAdapter.
* @param securityInterceptor the FilterSecurityInterceptor to use
* @return the WebSecurity for further customizations
*/
public WebSecurity securityInterceptor(FilterSecurityInterceptor securityInterceptor) {
this.filterSecurityInterceptor = securityInterceptor;
return this;
}
/**
* Executes the Runnable immediately after the build takes place
* 指定一段逻辑,以Runnable形式组织,在 build 完成后立即执行,该类实际上是放在 performBuild()
* 函数结束前执行的
*
* @param postBuildAction
* @return the WebSecurity for further customizations
*/
public WebSecurity postBuildAction(Runnable postBuildAction) {
this.postBuildAction = postBuildAction;
return this;
}
/**
* 各种配置信息已经搜集齐备,通过该方法执行构建过程,构建 Filter FilterChainProxy 实例并返回该 Filter
**/
@Override
protected Filter performBuild() throws Exception {
Assert.state(
!securityFilterChainBuilders.isEmpty(),
"At least one SecurityBuilder extends SecurityFilterChain> needs to be specified. "
+ "Typically this done by adding a @Configuration that extends "
+ " WebSecurityConfigurerAdapter."
+ " More advanced users can invoke " + WebSecurity.class.getSimpleName()
+ ".addSecurityFilterChainBuilder directly");
// 计算出要创建的过滤器链 SecurityFilterChain 的个数 :
// ignoredRequests 中URL通配符的个数 + securityFilterChainBuilders元素的个数,
// 这里每个 securityFilterChainBuilders 元素实际上是一个 HttpSecurity
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>(
chainSize);
// 对于每个要忽略的URL通配符,构建一个 SecurityFilterChain 实例,使用的实现类为
// DefaultSecurityFilterChain , 该实现类实例初始化时不包含任何过滤器,从而对给定的URL,
// 可以达到不对其进行安全检查的目的
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
// 对每个 securityFilterChainBuilder 执行其构建过程,生成一个 securityFilterChain,
// 这里每个 securityFilterChainBuilders 元素实际上是一个 HttpSecurity
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder :
securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
// 由多个 SecurityFilterChain securityFilterChains 构造一个 FilterChainProxy,这就是最终要构建的
// Filter : FilterChainProxy : springSecurityFilterChain
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (httpFirewall != null) {
filterChainProxy.setFirewall(httpFirewall);
}
// 对根据配置新建的 filterChainProxy 进行验证,
// FilterChainProxy 的缺省验证器是一个 NullFilterChainValidator,相应的验证逻辑为空方法
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
if (debugEnabled) {
logger.warn("\n\n"
+ "********************************************************************\n"
+ "********** Security debugging is enabled. *************\n"
+ "********** This may include sensitive information. *************\n"
+ "********** Do not use in a production system! *************\n"
+ "********************************************************************\n\n");
result = new DebugFilter(filterChainProxy);
}
postBuildAction.run();
return result;
}
/**
* An IgnoredRequestConfigurer that allows optionally configuring the
* MvcRequestMatcher#setMethod(HttpMethod)
*
* @author Rob Winch
*/
public final class MvcMatchersIgnoredRequestConfigurer
extends IgnoredRequestConfigurer {
private final List<MvcRequestMatcher> mvcMatchers;
private MvcMatchersIgnoredRequestConfigurer(ApplicationContext context,
List<MvcRequestMatcher> mvcMatchers) {
super(context);
this.mvcMatchers = mvcMatchers;
}
public IgnoredRequestConfigurer servletPath(String servletPath) {
for (MvcRequestMatcher matcher : this.mvcMatchers) {
matcher.setServletPath(servletPath);
}
return this;
}
}
/**
* 嵌套类,用于注册 Spring Security 需要忽略的 RequestMatcher 实例
*
* @author Rob Winch
* @since 3.2
*/
public class IgnoredRequestConfigurer
extends AbstractRequestMatcherRegistry<IgnoredRequestConfigurer> {
private IgnoredRequestConfigurer(ApplicationContext context) {
setApplicationContext(context);
}
@Override
public MvcMatchersIgnoredRequestConfigurer mvcMatchers(HttpMethod method,
String... mvcPatterns) {
List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns);
WebSecurity.this.ignoredRequests.addAll(mvcMatchers);
return new MvcMatchersIgnoredRequestConfigurer(getApplicationContext(),
mvcMatchers);
}
@Override
public MvcMatchersIgnoredRequestConfigurer mvcMatchers(String... mvcPatterns) {
return mvcMatchers(null, mvcPatterns);
}
@Override
protected IgnoredRequestConfigurer chainRequestMatchers(
List<RequestMatcher> requestMatchers) {
WebSecurity.this.ignoredRequests.addAll(requestMatchers);
return this;
}
/**
* 返回当前 WebSecurity 实例,方便链式调用.
*/
public WebSecurity and() {
return WebSecurity.this;
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.defaultWebSecurityExpressionHandler
.setApplicationContext(applicationContext);
this.ignoredRequestRegistry = new IgnoredRequestConfigurer(applicationContext);
}
}
WebSecurity
及其基类之间的关系Spring Security : 配置 HttpSecurity 的 SecurityConfigurer
Spring Security : 安全构建器HttpSecurity和WebSecurity的区别
Spring Security : HTTP请求安全构建器 HttpSecurity