欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringSecurity攔截器鏈的使用詳解

 更新時(shí)間:2023年11月21日 10:25:21   作者:dalianpai  
這篇文章主要介紹了SpringSecurity攔截器鏈的使用詳解,webSecurity的build方法最終調(diào)用的是doBuild方法,doBuild方法調(diào)用的是webSecurity的performBuild方法,webSecurity完成所有過(guò)濾器的插件,最終返回的是過(guò)濾器鏈代理類filterChainProxy,需要的朋友可以參考下

SpringSecurity攔截器鏈

Spring版本

<!--Spring Security過(guò)濾器鏈,注意過(guò)濾器名稱必須叫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)建的過(guò)程

@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;
}

當(dāng)我們使用這個(gè)注解的時(shí)候,等同于把WebSecurityConfiguration類放到了Spring的IOC容器中,在這個(gè)時(shí)候進(jìn)行了初始化。

@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);
		}
        //調(diào)用webSecurity的build方法,生成過(guò)濾器鏈。
		return webSecurity.build();
	}

webSecurity的build方法最終調(diào)用的是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方法調(diào)用的市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階段調(diào)用webSecurity的performBuild方法
			O result = performBuild();
 
			buildState = BuildState.BUILT;
 
			return result;
		}
	}

webSecurity完成所有過(guò)濾器的插件,最終返回的是過(guò)濾器鏈代理類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);
        //會(huì)創(chuàng)建要忽略的和要認(rèn)證的攔截器鏈
		for (RequestMatcher ignoredRequest : ignoredRequests) {
			securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
		}
		for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
			securityFilterChains.add(securityFilterChainBuilder.build());
		}
        //過(guò)濾器鏈實(shí)際是被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;
	}

斷點(diǎn)圖如下:

image-20210508162350146

FilterChainProxy間接繼承了Filter,可以作為真正的過(guò)濾器使用。它會(huì)攜帶若干條過(guò)濾器鏈,并在承擔(dān)過(guò)濾器職責(zé),將其派發(fā)到過(guò)濾器鏈上的每一個(gè)過(guò)濾器上。

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ā)到過(guò)濾器鏈上
				doFilterInternal(request, response, chain);
			}
			finally {
				SecurityContextHolder.clearContext();
				request.removeAttribute(FILTER_APPLIED);
			}
		}
		else {
			doFilterInternal(request, response, chain);
		}
	}
    //是真正執(zhí)行虛擬過(guò)濾器鏈邏輯的方法。
	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);
	}

image-20210508165318181

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);
			}
		}

請(qǐng)求的過(guò)程

ApplicationFilterChain是tomcat中的攔截器鏈,ApplicationFilterChain對(duì)象的特點(diǎn)與創(chuàng)建特點(diǎn):經(jīng)過(guò)一路代碼跟蹤發(fā)現(xiàn),每一個(gè)url匹配模式對(duì)應(yīng)于一個(gè)ApplicationFilterChain對(duì)象,在應(yīng)用的整個(gè)生命周期中只在第一次被訪問(wèn)時(shí)被創(chuàng)建一次,有種單例模式的感覺(jué),但是并沒(méi)有做線程安全方面的處理,后來(lái)發(fā)現(xiàn)可能做了池化處理;創(chuàng)建:ApplicationFilterChain對(duì)象是在StandardWrapperValve類中的invoke方法中調(diào)ApplicationFilterFactory.createFilterChain方法創(chuàng)建的,在實(shí)例化完成后緊接著就是為其設(shè)置Servlet,添加Filters以及設(shè)置其他屬性,添加Filters依賴于一個(gè)FilterMap[]數(shù)組,該數(shù)組的賦值與擴(kuò)容過(guò)程在StandardContext類中實(shí)現(xiàn),至于FilterMap的生成,則是在Spring的Bean初始化階段,通過(guò)掃描包下所有的類并結(jié)合注解通過(guò)反射機(jī)制進(jìn)行實(shí)例化的。

@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;
        }

其中會(huì)優(yōu)先調(diào)用springsecurity的攔截器鏈,它的本質(zhì)還是有一個(gè)fiter,注意,這里第5個(gè)過(guò)濾器顯示在攔截器鏈后執(zhí)行的,但是在攔截器鏈里面執(zhí)行過(guò)了,就不會(huì)這執(zhí)行了。

image-20210508161226433

當(dāng)請(qǐng)求到攔截鏈的時(shí)候

image-20210508161452455

依次執(zhí)行過(guò)濾器鏈

image-20210508161551528

到此這篇關(guān)于SpringSecurity攔截器鏈的使用詳解的文章就介紹到這了,更多相關(guān)SpringSecurity攔截器鏈內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot邏輯異常統(tǒng)一處理方法

    SpringBoot邏輯異常統(tǒng)一處理方法

    這篇文章主要介紹了SpringBoot邏輯異常統(tǒng)一處理方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • springboot集成redis并使用redis生成全局唯一索引ID

    springboot集成redis并使用redis生成全局唯一索引ID

    本文主要介紹了springboot集成redis并使用redis生成全局唯一索引ID,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • @RequestMapping 如何使用@PathVariable 從URI中獲取參數(shù)

    @RequestMapping 如何使用@PathVariable 從URI中獲取參數(shù)

    這篇文章主要介紹了@RequestMapping 如何使用@PathVariable 從URI中獲取參數(shù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • SpringBoot中實(shí)現(xiàn)多數(shù)據(jù)源連接和切換的方案

    SpringBoot中實(shí)現(xiàn)多數(shù)據(jù)源連接和切換的方案

    在Spring Boot中,通過(guò)AbstractRoutingDataSource實(shí)現(xiàn)多數(shù)據(jù)源連接是一種常見(jiàn)的做法,這種技術(shù)允許你在運(yùn)行時(shí)動(dòng)態(tài)地切換數(shù)據(jù)源,從而支持對(duì)多個(gè)數(shù)據(jù)庫(kù)的操作,本文給大家介紹了SpringBoot中實(shí)現(xiàn)多數(shù)據(jù)源連接和切換的方案,需要的朋友可以參考下
    2024-11-11
  • SpringBoot中利用AOP和攔截器實(shí)現(xiàn)自定義注解

    SpringBoot中利用AOP和攔截器實(shí)現(xiàn)自定義注解

    本文將通過(guò)攔截器+AOP實(shí)現(xiàn)自定義注解,在這里攔截器充當(dāng)在指定注解處要執(zhí)行的方法,aop負(fù)責(zé)將攔截器的方法和要注解生效的地方做一個(gè)織入,感興趣的可以嘗試一下
    2022-06-06
  • Java編寫緩存工具類的示例代碼

    Java編寫緩存工具類的示例代碼

    這篇文章主要為大家詳細(xì)介紹了如何基于Java編寫一個(gè)緩存工具類,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴可以了解一下
    2023-07-07
  • java中斷機(jī)制實(shí)例講解

    java中斷機(jī)制實(shí)例講解

    這篇文章主要介紹了java中斷機(jī)制實(shí)例講解,用了風(fēng)趣幽默的講法,有對(duì)這方面不太懂的同學(xué)可以研究下
    2021-01-01
  • springboot斷言異常封裝與統(tǒng)一異常處理實(shí)現(xiàn)代碼

    springboot斷言異常封裝與統(tǒng)一異常處理實(shí)現(xiàn)代碼

    異常處理其實(shí)一直都是項(xiàng)目開(kāi)發(fā)中的大頭,但關(guān)注異常處理的人一直都特別少,下面這篇文章主要給大家介紹了關(guān)于springboot斷言異常封裝與統(tǒng)一異常處理的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • Java比較兩個(gè)對(duì)象中全部屬性值是否相等的方法

    Java比較兩個(gè)對(duì)象中全部屬性值是否相等的方法

    本文主要介紹了Java比較兩個(gè)對(duì)象中全部屬性值是否相等的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Spring通過(guò)ApplicationContext主動(dòng)獲取bean的方法講解

    Spring通過(guò)ApplicationContext主動(dòng)獲取bean的方法講解

    今天小編就為大家分享一篇關(guān)于Spring通過(guò)ApplicationContext主動(dòng)獲取bean的方法講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-03-03

最新評(píng)論