目录
简单示例
WebSecurityConfig
requestMatchers
requestMatchers
antMatchers
chainRequestMatchers
setMatchers
requestMatcher
WebSecurity
performBuild
securityFilterChainBuilder对象
init
addSecurityFilterChainBuilder
securityFilterChainBuilder.build
build
doBuild
httpSecurity.performBuild
FilterChainProxy
doFilterInternal
getFilters
matches
OrRequestMatcher.matches
authorizeRequests
authorizeRequests
ExpressionUrlAuthorizationConfigurer
AbstractInterceptUrlConfigurer
FilterSecurityInterceptor
invoke
beforeInvocation
总结
本篇文章我们来研究spring-security框架里http.requestMatchers和http.authorizeRequests的作用。只要使用了spring-security框架,那么,开发人员就免不了要对这两个配置项的配置了。
简单示例
首先,新建一个config包用于存放spring-security通用配置;然后,新建一个WebSecurityConfig类,使其继承WebSecurityConfigurerAdapter。
然后,给WebSecutiryConfig类中加上@EnableWebSecurity 注解后,这样便会自动被 Spring发现并注册。
WebSecurityConfig
@EnableWebSecurity
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("web/oauth/authorize", "/oauth/**", "/admin/**")
.and().authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
... ...
}
|
在这里,首先调用httpSecurity的requestMatchers()方法,紧接着调用antMatchers()方法,并输入web/oauth/authorize, /oauth/**, /admin/**三个通配符路径,表示当前配置只允许满足这三个路径的请求通过;
然后,调用authorizeRequests()方法,再调用antMatchers()方法,并输入/admin/**通配符路径,紧接着调用hasRole()方法,并输入ADMIN参数,表示对于满足/admin/**路径的请求,需要登录用户有ADMIN的角色;
最后,调用anyRequest()方法,紧接着调用authenticated()方法,表示/admin/**之外的所有请求,需要登录用户认证后才能访问。
requestMatchers
在简单示例里,点击requestMatchers()方法,如下所示:
requestMatchers
public RequestMatcherConfigurer requestMatchers() {
return requestMatcherConfigurer;
}
|
在这里,返回配置器RequestMatcherConfigurer对象。
在简单示例里,点击requestMatchers()后面的antMatchers方法,如下所示:
antMatchers
public abstract class AbstractRequestMatcherRegistry<C> {
... ...
public C antMatchers(String... antPatterns) {
return chainRequestMatchers(RequestMatchers.antMatchers(antPatterns));
}
|
点击chainRequestMatchers()方法,如下所示:
chainRequestMatchers
@Override
protected RequestMatcherConfigurer chainRequestMatchers(List<RequestMatcher> requestMatchers) {
setMatchers(requestMatchers);
return this;
}
|
在这里,点击setMatchers()方法,如下所示:
setMatchers
private void setMatchers(List<? extends RequestMatcher> requestMatchers) {
this.matchers.addAll(requestMatchers);
requestMatcher(new OrRequestMatcher(this.matchers));
}
|
在这里,将配置的请求地址转换为OrRequestMatcher对象。然后调用requestMatcher()方法进行后续的处理。
点击requestMatcher()方法,如下所示:
requestMatcher
public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity> implements SecurityBuilder<DefaultSecurityFilterChain>,
HttpSecurityBuilder<HttpSecurity> {
private RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;
... ...
public HttpSecurity requestMatcher(RequestMatcher requestMatcher) {
this.requestMatcher = requestMatcher;
return this;
}
|
在这里,将配置的请求地址OrRequestMatcher对象保存到HttpSecurity对象的requestMatcher 属性对象里。
从前面的文章里,我们知道spring-security框架最终会创建一个FilterChainProxy类的过滤器对象,接下来我们探究一下该FilterChainProxy过滤器如何引用HttpSecurity对象的requestMatcher 属性对象。
从前面的文章里,我们也了解到过滤器FilterChainProxy对象,是在WebSecurity类里创建的。
WebSecurity
点击performBuild()方法,如下所示:
protected Filter performBuild() throws Exception {
int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList(chainSize);
... ...
var3 = this.securityFilterChainBuilders.iterator();
while(var3.hasNext()) {
SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder = (SecurityBuilder)var3.next();
securityFilterChains.add(securityFilterChainBuilder.build());
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (this.httpFirewall != null) {
filterChainProxy.setFirewall(this.httpFirewall);
}
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
this.postBuildAction.run();
return (Filter)result;
}
|
在这里,FilterChainProxy实例的构造参数是securityFilterChainBuilder.build()。
接下来我们探究两个问题:
- securityFilterChainBuilder对象来自哪里?
- securityFilterChainBuilder.build()方法跟HttpSecurity对象的requestMatcher 属性对象有什么关系?
- 过滤器FilterChainProxy对象如何使用requestMatcher 属性?
securityFilterChainBuilder对象
回顾前面的文章,我们知道在WebSecurityConfigurerAdapter 类里会创建HttpSecurity对象,找到WebSecurityConfigurerAdapter 类的init()方法,如下所示:
init
public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {
... ...
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
});
}
|
在这里,通过getHttp()方法创建了HttpSecurity对象,然后调用web.addSecurityFilterChainBuilder(http)方法,如下所示:
addSecurityFilterChainBuilder
public WebSecurity addSecurityFilterChainBuilder(SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder) {
this.securityFilterChainBuilders.add(securityFilterChainBuilder);
return this;
}
|
在这里,将HttpSecurity对象加入到WebSecurity 对象的属性securityFilterChainBuilders里。
接下来, 我们探究securityFilterChainBuilder.build()方法跟HttpSecurity对象的requestMatcher 属性对象有什么关系?
securityFilterChainBuilder.build
在WebSecurity类的performBuild()方法,点击securityFilterChainBuilder.build(),如下所示:
build
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");
}
|
点击doBuild()方法,如下所示:
doBuild
@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING;
beforeInit();
init();
buildState = BuildState.CONFIGURING;
beforeConfigure();
configure();
buildState = BuildState.BUILDING;
O result = performBuild();
buildState = BuildState.BUILT;
return result;
}
}
|
点击performBuild()方法,如下所示:
@Override
protected DefaultSecurityFilterChain performBuild() {
filters.sort(comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}
|
在这里,会创建DefaultSecurityFilterChain对象,该对象的第一个构造参数就是httpSecurity对象的属性requestMatcher对象了。
接下来,我们探究过滤器FilterChainProxy对象如何使用requestMatcher属性?
FilterChainProxy
点击FilterChainProxy 类的doFilter()方法,如下所示:
public class FilterChainProxy extends GenericFilterBean {
... ...
@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);
}
}
|
点击doFilterInternal()方法,如下所示:
doFilterInternal
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) {
fwRequest.reset();
chain.doFilter(fwRequest, fwResponse);
return;
}
VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
vfc.doFilter(fwRequest, fwResponse);
}
|
点击getFilters()方法,如下所示:
getFilters
private List<Filter> getFilters(HttpServletRequest request) {
for (SecurityFilterChain chain : filterChains) {
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}
|
点击chain.matches()方法,如下所示:
matches
public boolean matches(HttpServletRequest request) {
return requestMatcher.matches(request);
}
|
点击requestMatcher.matches()方法,如下所示:
OrRequestMatcher.matches
public boolean matches(HttpServletRequest request) {
for (RequestMatcher matcher : requestMatchers) {
if (logger.isDebugEnabled()) {
logger.debug("Trying to match using " + matcher);
}
if (matcher.matches(request)) {
logger.debug("matched");
return true;
}
}
logger.debug("No matches found");
return false;
}
|
至此,我们总算把spring-security框架的http.requestMatchers研究透彻了。
接下来,我们探究spring-security框架的http.authorizeRequests的作用。
authorizeRequests
在简单示例里,点击authorizeRequests()方法,如下所示:
authorizeRequests
public ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests() throws Exception {
ApplicationContext context = getContext();
return getOrApply(new ExpressionUrlAuthorizationConfigurer<>(context)).getRegistry();
}
|
在这里,使用的是配置器ExpressionUrlAuthorizationConfigurer对象。点击ExpressionUrlAuthorizationConfigurer类,如下所示:
public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractInterceptUrlConfigurer<ExpressionUrlAuthorizationConfigurer<H>, H> {
... ...
private final ExpressionInterceptUrlRegistry REGISTRY;
... ...
|
在这里,找不到configure(H http)方法,点击继承类AbstractInterceptUrlConfigurer,如下所示:
abstract class AbstractInterceptUrlConfigurer<C extends AbstractInterceptUrlConfigurer<C, H>, H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<C, H> {
private Boolean filterSecurityInterceptorOncePerRequest;
private AccessDecisionManager accessDecisionManager;
@Override
public void configure(H http) throws Exception {
FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http);
if (metadataSource == null) {
return;
}
FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor(
http, metadataSource, http.getSharedObject(AuthenticationManager.class));
if (filterSecurityInterceptorOncePerRequest != null) {
securityInterceptor
.setObserveOncePerRequest(filterSecurityInterceptorOncePerRequest);
}
securityInterceptor = postProcess(securityInterceptor);
http.addFilter(securityInterceptor);
http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor);
}
|
在这里,创建了过滤器FilterSecurityInterceptor 对象。
点击FilterSecurityInterceptor 类,如下所示:
FilterSecurityInterceptor
public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}
|
点击invoke()方法,如下所示:
invoke
public void invoke(FilterInvocation fi) throws IOException, ServletException {
if ((fi.getRequest() != null) && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
&& observeOncePerRequest) {
... ...
} else {
if (fi.getRequest() != null && observeOncePerRequest) {
fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
}
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.finallyInvocation(token);
}
super.afterInvocation(token, null);
}
}
|
点击beforeInvocation()方法,如下所示:
beforeInvocation
protected InterceptorStatusToken beforeInvocation(Object object) {
boolean debug = this.logger.isDebugEnabled();
if (!this.getSecureObjectClass().isAssignableFrom(object.getClass())) {
... ...
} else {
Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
if (attributes != null && !attributes.isEmpty()) {
... ...
Authentication authenticated = this.authenticateIfRequired();
try {
this.accessDecisionManager.decide(authenticated, object, attributes);
} catch (AccessDeniedException var7) {
... ...
}
... ...
}
}
|
在这里,通过this.obtainSecurityMetadataSource().getAttributes(object)获取配置的权限信息,通过this.accessDecisionManager.decide进行授权决策。
总结
http.requestMatchers()是一个请求过滤器,决定是否要应用安全配置。对于不需要进行安全控制的URL,可以不要配置在requestMatchers里,这样可以避免走到下一个环节。
http.authorizeRequests()是一个授权配置器,决定如何应用安全配置。用于配置 URL 的访问权限控制规则。