SpringSecurity攔截器鏈的使用詳解
SpringSecurity攔截器鏈
Spring版本
<!--Spring Security過濾器鏈,注意過濾器名稱必須叫springSecurityFilterChain--> <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
SpringBoot 版本
攔截器鏈創(chuàng)建的過程
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) @Target(value = { java.lang.annotation.ElementType.TYPE }) @Documented @Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class }) @EnableGlobalAuthentication @Configuration public @interface EnableWebSecurity { /** * Controls debugging support for Spring Security. Default is false. * @return if true, enables debug support with Spring Security */ boolean debug() default false; }
當我們使用這個注解的時候,等同于把WebSecurityConfiguration類放到了Spring的IOC容器中,在這個時候進行了初始化。
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) public Filter springSecurityFilterChain() throws Exception { boolean hasConfigurers = webSecurityConfigurers != null && !webSecurityConfigurers.isEmpty(); if (!hasConfigurers) { WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor .postProcess(new WebSecurityConfigurerAdapter() { }); webSecurity.apply(adapter); } //調用webSecurity的build方法,生成過濾器鏈。 return webSecurity.build(); }
webSecurity的build方法最終調用的是doBuild方法。
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方法調用的市webSecurity的performBuild方法。
@Override protected final O doBuild() throws Exception { synchronized (configurers) { buildState = BuildState.INITIALIZING; beforeInit(); init(); buildState = BuildState.CONFIGURING; beforeConfigure(); configure(); buildState = BuildState.BUILDING; //在BUILDING階段調用webSecurity的performBuild方法 O result = performBuild(); buildState = BuildState.BUILT; return result; } }
webSecurity完成所有過濾器的插件,最終返回的是過濾器鏈代理類filterChainProxy
@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"); int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size(); List<SecurityFilterChain> securityFilterChains = new ArrayList<>( chainSize); //會創(chuàng)建要忽略的和要認證的攔截器鏈 for (RequestMatcher ignoredRequest : ignoredRequests) { securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest)); } for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) { securityFilterChains.add(securityFilterChainBuilder.build()); } //過濾器鏈實際是被filterChainProxy代理的 FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains); if (httpFirewall != null) { filterChainProxy.setFirewall(httpFirewall); } 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; }
斷點圖如下:
FilterChainProxy間接繼承了Filter,可以作為真正的過濾器使用。它會攜帶若干條過濾器鏈,并在承擔過濾器職責,將其派發(fā)到過濾器鏈上的每一個過濾器上。
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); //派發(fā)到過濾器鏈上 doFilterInternal(request, response, chain); } finally { SecurityContextHolder.clearContext(); request.removeAttribute(FILTER_APPLIED); } } else { doFilterInternal(request, response, chain); } } //是真正執(zhí)行虛擬過濾器鏈邏輯的方法。 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 VirtualFilterChain(FirewalledRequest firewalledRequest, FilterChain chain, List<Filter> additionalFilters) { this.originalChain = chain; this.additionalFilters = additionalFilters; this.size = additionalFilters.size(); this.firewalledRequest = firewalledRequest; } @Override public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if (currentPosition == size) { if (logger.isDebugEnabled()) { logger.debug(UrlUtils.buildRequestUrl(firewalledRequest) + " reached end of additional filter chain; proceeding with original chain"); } // Deactivate path stripping as we exit the security filter chain this.firewalledRequest.reset(); originalChain.doFilter(request, response); } else { currentPosition++; Filter nextFilter = additionalFilters.get(currentPosition - 1); if (logger.isDebugEnabled()) { logger.debug(UrlUtils.buildRequestUrl(firewalledRequest) + " at position " + currentPosition + " of " + size + " in additional filter chain; firing Filter: '" + nextFilter.getClass().getSimpleName() + "'"); } nextFilter.doFilter(request, response, this); } }
請求的過程
ApplicationFilterChain是tomcat中的攔截器鏈,ApplicationFilterChain對象的特點與創(chuàng)建特點:經過一路代碼跟蹤發(fā)現,每一個url匹配模式對應于一個ApplicationFilterChain對象,在應用的整個生命周期中只在第一次被訪問時被創(chuàng)建一次,有種單例模式的感覺,但是并沒有做線程安全方面的處理,后來發(fā)現可能做了池化處理;創(chuàng)建:ApplicationFilterChain對象是在StandardWrapperValve類中的invoke方法中調ApplicationFilterFactory.createFilterChain方法創(chuàng)建的,在實例化完成后緊接著就是為其設置Servlet,添加Filters以及設置其他屬性,添加Filters依賴于一個FilterMap[]數組,該數組的賦值與擴容過程在StandardContext類中實現,至于FilterMap的生成,則是在Spring的Bean初始化階段,通過掃描包下所有的類并結合注解通過反射機制進行實例化的。
@Override public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if( Globals.IS_SECURITY_ENABLED ) { final ServletRequest req = request; final ServletResponse res = response; try { java.security.AccessController.doPrivileged( new java.security.PrivilegedExceptionAction<Void>() { @Override public Void run() throws ServletException, IOException { internalDoFilter(req,res); return null; } } ); } catch( PrivilegedActionException pe) { Exception e = pe.getException(); if (e instanceof ServletException) throw (ServletException) e; else if (e instanceof IOException) throw (IOException) e; else if (e instanceof RuntimeException) throw (RuntimeException) e; else throw new ServletException(e.getMessage(), e); } } else { internalDoFilter(request,response); } } private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { // Call the next filter if there is one if (pos < n) { ApplicationFilterConfig filterConfig = filters[pos++]; try { Filter filter = filterConfig.getFilter(); if (request.isAsyncSupported() && "false".equalsIgnoreCase( filterConfig.getFilterDef().getAsyncSupported())) { request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE); } if( Globals.IS_SECURITY_ENABLED ) { final ServletRequest req = request; final ServletResponse res = response; Principal principal = ((HttpServletRequest) req).getUserPrincipal(); Object[] args = new Object[]{req, res, this}; SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal); } else { filter.doFilter(request, response, this); } } catch (IOException | ServletException | RuntimeException e) { throw e; } catch (Throwable e) { e = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(e); sthrow new ServletException(sm.getString("filterChain.filter"), e); } return; }
其中會優(yōu)先調用springsecurity的攔截器鏈,它的本質還是有一個fiter,注意,這里第5個過濾器顯示在攔截器鏈后執(zhí)行的,但是在攔截器鏈里面執(zhí)行過了,就不會這執(zhí)行了。
當請求到攔截鏈的時候
依次執(zhí)行過濾器鏈
到此這篇關于SpringSecurity攔截器鏈的使用詳解的文章就介紹到這了,更多相關SpringSecurity攔截器鏈內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- springsecurity實現攔截器的使用示例
- SpringSecurity整合JWT的使用示例
- SpringSecurity實現權限認證與授權的使用示例
- SpringSecurity默認登錄頁的使用示例教程
- 使用SpringSecurity+defaultSuccessUrl不跳轉指定頁面的問題解決方法
- SpringSecurity入門使用教程
- springsecurity實現用戶登錄認證快速使用示例代碼(前后端分離項目)
- Spring Security 使用 OncePerRequestFilter 過濾器校驗登錄過期、請求日志等操作
- Spring Security使用多種加密方式進行密碼校驗的代碼示例
- 新版SpringSecurity5.x使用與配置詳解
相關文章
websocket在springboot+vue中的使用教程
這篇文章主要介紹了websocket在springboot+vue中的使用教程,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-08-08Spring的循環(huán)依賴、三級緩存解決方案源碼詳細解析
這篇文章主要介紹了Spring的循環(huán)依賴、三級緩存解決方案源碼詳細解析,在Spring中,由于IOC的控制反轉,創(chuàng)建對象不再是簡單的new出來,而是交給Spring去創(chuàng)建,會經歷一系列Bean的生命周期才創(chuàng)建出相應的對象,需要的朋友可以參考下2024-01-01SpringBoot4.5.2 整合HikariCP 數據庫連接池操作
這篇文章主要介紹了SpringBoot4.5.2 整合HikariCP 數據庫連接池操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09java中throws與try...catch的區(qū)別點
在本篇文章里小編給大家整理了一篇關于java中throws與try...catch的區(qū)別點的內容,需要的朋友們跟著學習下。2020-02-02java多線程CyclicBarrier的使用案例,讓線程起步走
這篇文章主要介紹了java多線程CyclicBarrier的使用案例,讓線程起步走!具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02