Spring Boot 自动装配 Spring Security 详解
文章目录
Spring Security 是 Java Web 技术中,管理认证和授权的安全框架。
Spring Security 原理(Servlet)
在 Spring MVC 项目中,Spring Security 在 Web 容器中插入一个 Filter,从而在 Http 请求进入 DispatcherServlet 前对认证(Authentication)和授权(Authorization)进行校验。
关于 Spring Security 的更多架构原理请参看 Servlet 架构
关键结构
springSecurityFilterrChain
:
:
DelegatingFilterProxy --> FilterChainProxy --> SecurityFilterChain
^ ^
| |
WebSecurity HttpSecurity
Spring Security 框架应用了职责链模式,通过多级 Filter 的对请求的层层拦截实现了对不同职责关注点的解耦。
1. SecurityFilterChain 和 FilterChainProxy
Spring Security 框架中表示职责链的接口是 SecurityFilterChain,具有 matches() 和 getFilters() 两个方法。因为 Spring Security 可以配置多个 SecurityFilterChain,所以需要 matches() 方法根据具体的请求完成选择。getFilters() 方法则是用来获取职责链中的 Filter 的有序列表。
package org.springframework.security.web;
// ...
public interface SecurityFilterChain {
boolean matches(HttpServletRequest request);
List<Filter> getFilters();
}
FilterChainProxy 是 SecurityFilterChain 集合的代理,它将多个 SecurityFilterChain 包装起来,对外暴露统一的入口。
package org.springframework.security.web;
// ...
public class FilterChainProxy extends GenericFilterBean {
// ...
private List<SecurityFilterChain> filterChains;
// ...
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
if (clearContext) {
try {
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
doFilterInternal(request, response, chain);
}
finally {
SecurityContextHolder.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
}
else {
doFilterInternal(request, response, chain);
}
}
private void doFilterInternal(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FirewalledRequest fwRequest = firewall
.getFirewalledRequest((HttpServletRequest) request);
HttpServletResponse fwResponse = firewall
.getFirewalledResponse((HttpServletResponse) response);
List<Filter> filters = getFilters(fwRequest);
if (filters == null || filters.size() == 0) {
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(fwRequest)
+ (filters == null ? " has no matching filters"
: " has an empty filter list"));
}
fwRequest.reset();
chain.doFilter(fwRequest, fwResponse);
return;
}
VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
vfc.doFilter(fwRequest, fwResponse);
}
private List<Filter> getFilters(HttpServletRequest request) {
for (SecurityFilterChain chain : filterChains) {
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}
// ...
}
2. DelegatingFilterProxy
DelegatingFilterProxy 是 FilterChainProxy 的代理,是 Spring Security 框架与 Web 容器关联的 Filter 对象,其 initDelegate() 方法根据 targetBeanName 来获取真正作为 Filter 的 Bean。
事实上,代理 FilterChainProxy 对象的 DelegatingFilterProxy 对象的 targetBeanName 成员的值是 springSecurityFilterChain,即 DelegatingFilterProxy 对象代理了名为 springSecurityFilterChain 的 Bean。
package org.springframework.web.filter;
// ...
public class DelegatingFilterProxy extends GenericFilterBean {
// ...
@Nullable
private volatile Filter delegate;
// ...
public DelegatingFilterProxy(String targetBeanName, @Nullable WebApplicationContext wac) {
Assert.hasText(targetBeanName, "Target Filter bean name must not be null or empty");
this.setTargetBeanName(targetBeanName);
this.webApplicationContext = wac;
if (wac != null) {
this.setEnvironment(wac.getEnvironment());
}
}
// ...
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// Lazily initialize the delegate if necessary.
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized (this.delegateMonitor) {
delegateToUse = this.delegate;
if (delegateToUse == null) {
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: " +
"no ContextLoaderListener or DispatcherServlet registered?");
}
delegateToUse = initDelegate(wac);
}
this.delegate = delegateToUse;
}
}
// Let the delegate perform the actual doFilter operation.
invokeDelegate(delegateToUse, request, response, filterChain);
}
// ...
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
String targetBeanName = getTargetBeanName();
Assert.state(targetBeanName != null, "No target bean name set");
Filter delegate = wac.getBean(targetBeanName, Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}
// ...
}
3. WebSecurity 和 HttpSecurity
最后,WebSecurity 是 FilterChainProxy 的 Builder 类,HttpSecurity 是 DefaultSecurityFilterChain 的 Builder 类。它们都继承了 AbstractConfiguredSecurityBuilder 类,而 AbstractConfiguredSecurityBuilder 又继承 AbstractSecurityBuilder 类,AbstractSecurityBuilder 则实现了 SecurityBuilder 接口。
3.1. SecurityBuilder 接口
SecurityBuilder 接口中只定义了 build() 方法。
package org.springframework.security.config.annotation;
// ...
public interface SecurityBuilder<O> {
O build() throws Exception;
}
3.2. AbstractSecurityBuilder 抽象类
AbstractSecurityBuilder 的 build() 方法直接调用 doBuild() 方法。
package org.springframework.security.config.annotation;
// ...
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
// ...
public final O build() throws Exception {
if (this.building.compareAndSet(false, true)) {
this.object = doBuild();
return this.object;
}
throw new AlreadyBuiltException("This object has already been built");
}
// ...
protected abstract O doBuild() throws Exception;
// ...
}
3.4. AbstractConfiguredSecurityBuilder 抽象类
AbstractConfiguredSecurityBuilder 重写了 doBuild() 方法,依次调用 beforeInit(), init(), beforeConfigure(), configure() 方法进行初始化和配置,最后调用 performBuild() 方法完成对象创建过程。
package org.springframework.security.config.annotation;
// ...
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
extends AbstractSecurityBuilder