FilterSecurityInterceptor最后一個過濾器,主要做認證和授權攔截,比如我們未登錄訪問需要登錄的頁面或者我們配置了授權的頁面
http.authorizeRequests() .antMatchers("/hello").hasRole("admin").antMatchers("/hello2").hasAnyAuthority("au-test")
初始化處
org.springframework.security.config.annotation.web.configurers.AbstractInterceptUrlConfigurer#configure
@Override public void configure(H http) throws Exception { /** * 調用子類獲取FilterInvocationSecurityMetadataSource * 主要是為了管理我們配置 url映射 對應的配置比如antMatchers("/hello").hasRole("admin") * 后續就可以快速通過url獲取對應的匹配配置 */ FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http); if (metadataSource == null) { return; } //創建FilterSecurityInterceptor FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor(http, metadataSource, http.getSharedObject(AuthenticationManager.class)); if (this.filterSecurityInterceptorOncePerRequest != null) { securityInterceptor.setObserveOncePerRequest(this.filterSecurityInterceptorOncePerRequest); } //注入 securityInterceptor = postProcess(securityInterceptor); //加入到Fitler http.addFilter(securityInterceptor); http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor); }
org.springframework.security.web.access.intercept.FilterSecurityInterceptor#doFilter
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //<1>通過FilterInvocation封裝 invoke(new FilterInvocation(request, response, chain)); }
<1>
org.springframework.security.web.access.intercept.FilterSecurityInterceptor#invoke
public void invoke(FilterInvocation filterInvocation) throws IOException, ServletException { //是否需要驗證權限和會話 if (isApplied(filterInvocation) && this.observeOncePerRequest) { // filter already applied to this request and user wants us to observe // once-per-request handling, so don't re-do security checking filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse()); return; } // first time this request being called, so perform security checking if (filterInvocation.getRequest() != null && this.observeOncePerRequest) { filterInvocation.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE); } //<2>調用DispatcherFilter之前驗證 InterceptorStatusToken token = super.beforeInvocation(filterInvocation); try { //繼續放行 因為和這個是最后一個過濾器 所以我們看 放行的過濾器<5> filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse()); } finally { super.finallyInvocation(token); } //調用Dispatcher之后驗證 super.afterInvocation(token, null); }
<2>
org.springframework.security.access.intercept.AbstractSecurityInterceptor#beforeInvocation
/** * Object封裝了我們的Url * @param object * @return */ protected InterceptorStatusToken beforeInvocation(Object object) { Assert.notNull(object, "Object was null"); if (!getSecureObjectClass().isAssignableFrom(object.getClass())) { throw new IllegalArgumentException("Security invocation attempted for object " + object.getClass().getName() + " but AbstractSecurityInterceptor only configured to support secure objects of type: " + getSecureObjectClass()); } //根據Url獲取ConfigAttribute 就是我們配置的url的hasRole的運行是數據結構 通過ConfigAttribute封裝 Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object); //如果沒有配置則不校驗 if (CollectionUtils.isEmpty(attributes)) { Assert.isTrue(!this.rejectPublicInvocations, () -> "Secure object invocation " + object + " was denied as public invocations are not allowed via this interceptor. " + "This indicates a configuration error because the " + "rejectPublicInvocations property is set to 'true'"); if (this.logger.isDebugEnabled()) { this.logger.debug(LogMessage.format("Authorized public object %s", object)); } publishEvent(new PublicInvocationEvent(object)); return null; // no further work post-invocation } //如果沒有用戶信息拋錯由ExceptionTranslationFilter 捕獲處理 if (SecurityContextHolder.getContext().getAuthentication() == null) { credentialsNotFound(this.messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound", "An Authentication object was not found in the SecurityContext"), object, attributes); } //<3>有用戶信息 校驗是否認證通過 Authentication authenticated = authenticateIfRequired(); if (this.logger.isTraceEnabled()) { this.logger.trace(LogMessage.format("Authorizing %s with attributes %s", object, attributes)); } //<4>根據權限配置 進行權限校驗 attemptAuthorization(object, attributes, authenticated); if (this.logger.isDebugEnabled()) { this.logger.debug(LogMessage.format("Authorized %s with attributes %s", object, attributes)); } if (this.publishAuthorizationSuccess) { publishEvent(new AuthorizedEvent(object, attributes, authenticated)); } // A權限通過的處理器 我們應該可以根據這個重新設置用戶信息 Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes); if (runAs != null) { SecurityContext origCtx = SecurityContextHolder.getContext(); SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext()); SecurityContextHolder.getContext().setAuthentication(runAs); if (this.logger.isDebugEnabled()) { this.logger.debug(LogMessage.format("Switched to RunAs authentication %s", runAs)); } // need to revert to token.Authenticated post-invocation return new InterceptorStatusToken(origCtx, true, attributes, object); } this.logger.trace("Did not switch RunAs authentication since RunAsManager returned null"); // no further work post-invocation return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object); }
<3>
private Authentication authenticateIfRequired() { //獲得用戶信息 Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication.isAuthenticated() && !this.alwaysReauthenticate) { if (this.logger.isTraceEnabled()) { this.logger.trace(LogMessage.format("Did not re-authenticate %s before authorizing", authentication)); } return authentication; } //如果未認證則交給authenticationManager 重新認證 authentication = this.authenticationManager.authenticate(authentication); // Don't authenticated.setAuthentication(true) because each provider does that if (this.logger.isDebugEnabled()) { this.logger.debug(LogMessage.format("Re-authenticated %s before authorizing", authentication)); } //set到Authentication SecurityContextHolder.getContext().setAuthentication(authentication); return authentication; }
<4>
private void attemptAuthorization(Object object, Collection<ConfigAttribute> attributes, Authentication authenticated) { try { //交給accessDecisionManager 感興趣可以打斷點繼續查看比如通過eval 表達式 或者對應的處理類獲取Authentication的權限或則判斷 this.accessDecisionManager.decide(authenticated, object, attributes); } catch (AccessDeniedException ex) {//權限認證未通過拋出異常 由ExceptionTranslationFilter 捕獲處理 if (this.logger.isTraceEnabled()) { this.logger.trace(LogMessage.format("Failed to authorize %s with attributes %s using %s", object, attributes, this.accessDecisionManager)); } else if (this.logger.isDebugEnabled()) { this.logger.debug(LogMessage.format("Failed to authorize %s with attributes %s", object, attributes)); } publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, ex)); throw ex; } }
<5>
可以參考https://www.cnblogs.com/LQBlog/p/15508248.html#autoid-11-0-0
secrityFilter最終由FilterChainProxy封裝作為入口
@Override protected Filter performBuild() throws Exception { Assert.state(!this.securityFilterChainBuilders.isEmpty(), () -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. " + "Typically this is done by exposing a SecurityFilterChain bean " + "or by adding a @Configuration that extends WebSecurityConfigurerAdapter. " + "More advanced users can invoke " + WebSecurity.class.getSimpleName() + ".addSecurityFilterChainBuilder directly"); /** * 得我們設置的忽略檢查為他們添加一個filter * public void configure(WebSecurity web) throws Exception { * web.ignoring().antMatchers("/js/**", "/css/**", "/images/**"); * } */ int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size(); List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize); for (RequestMatcher ignoredRequest : this.ignoredRequests) { securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest)); } //securityFilterChainBuilders為HttpSecurity<6>處初始化 for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) { //執行build<1> 最終會構建成<13> securityFilterChains.add(securityFilterChainBuilder.build()); } //通過FilterChainProxy 代理管理 它實現了ServletFilter 通過FilterChainProxy為Servlet入口 進入security的自己的filter FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains); if (this.httpFirewall != null) { filterChainProxy.setFirewall(this.httpFirewall); } if (this.requestRejectedHandler != null) { filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler); } filterChainProxy.afterPropertiesSet(); Filter result = filterChainProxy; if (this.debugEnabled) { result = new DebugFilter(filterChainProxy); } this.postBuildAction.run(); //返回filter 我們請求都會到filterChainProxy 通過他調用security的filter實現securityfilter 注入邏輯 return result; }
FilterChainProxy的doFilter最終是委托給VirtualFilterChain
org.springframework.security.web.FilterChainProxy.VirtualFilterChain#doFilter
@Override public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { //表示是最后一個Filter if (this.currentPosition == this.size) { if (logger.isDebugEnabled()) { logger.debug(LogMessage.of(() -> "Secured " + requestLine(this.firewalledRequest))); } // Deactivate path stripping as we exit the security filter chain this.firewalledRequest.reset(); //originalChain 就是我們的DispatcherFilter this.originalChain.doFilter(request, response); return; } //不是最后一個則依次++遍歷所有Filter this.currentPosition++; Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1); if (logger.isTraceEnabled()) { logger.trace(LogMessage.format("Invoking %s (%d/%d)", nextFilter.getClass().getSimpleName(), this.currentPosition, this.size)); } nextFilter.doFilter(request, response, this); }