Spring Security : Web安全构建器 WebSecurity

WebSecuritySpring Security的一个SecurityBuilder。它的任务是基于一组WebSecurityConfigurer构建出一个Servlet Filter,具体来讲就是构建一个Spring SecurityFilterChainProxy实例。这个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. 这里 1 HttpSecurity 对应1URL pattern,用于匹配一组需要进行安全配置的请求;
  2. 这里 1 RequestMatcher 对应 1URL pattern,用于匹配一组需要忽略安全控制的请求,比如静态公开资源或者其他动态公开资源;
  3. 这里的每个 HttpSecurity或者RequestMatcher最终对应构建一个SecurityFilterChain,这里 x+y 会等于上面助记公式中的 n
  4. 1 WebSecurity最终用于构建1 FilterChainProxy, WebSecurityHttpFirewall如果不存在,则目标FilterChainProxy会使用缺省值,一个StrictHttpFirewall
  5. 每个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及其基类之间的关系

WebSecurity类图

参考文章

Spring Security : 配置 HttpSecurity 的 SecurityConfigurer
Spring Security : 安全构建器HttpSecurity和WebSecurity的区别
Spring Security : HTTP请求安全构建器 HttpSecurity

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值