目錄
簡單示例
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 的訪問權限控制規則。