zoukankan      html  css  js  c++  java
  • 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 版本

    拦截器链创建的过程

    @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);
            //会创建要忽略的和要认证的拦截器链
    		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("
    
    "
    					+ "********************************************************************
    "
    					+ "**********        Security debugging is enabled.       *************
    "
    					+ "**********    This may include sensitive information.  *************
    "
    					+ "**********      Do not use in a production system!     *************
    "
    					+ "********************************************************************
    
    ");
    			result = new DebugFilter(filterChainProxy);
    		}
    		postBuildAction.run();
    		return result;
    	}
    

    断点图如下:

    image-20210508162350146

    FilterChainProxy间接继承了Filter,可以作为真正的过滤器使用。它会携带若干条过滤器链,并在承担过滤器职责,将其派发到过滤器链上的每一个过滤器上。

    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);
    		}
    	}
        //是真正执行虚拟过滤器链逻辑的方法。
    	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);
    			}
    		}
    

    请求的过程

    ApplicationFilterChain是tomcat中的拦截器链,ApplicationFilterChain对象的特点与创建
    特点:经过一路代码跟踪发现,每一个url匹配模式对应于一个ApplicationFilterChain对象,在应用的整个生命周期中只在第一次被访问时被创建一次,有种单例模式的感觉,但是并没有做线程安全方面的处理,后来发现可能做了池化处理;
    创建:ApplicationFilterChain对象是在StandardWrapperValve类中的invoke方法中调ApplicationFilterFactory.createFilterChain方法创建的,在实例化完成后紧接着就是为其设置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;
            }
    

    其中会优先调用springsecurity的拦截器链,它的本质还是有一个fiter,注意,这里第5个过滤器显示在拦截器链后执行的,但是在拦截器链里面执行过了,就不会这执行了。

    image-20210508161226433

    当请求到拦截链的时候

    image-20210508161452455

    依次执行过滤器链

    image-20210508161551528

  • 相关阅读:
    905. Sort Array By Parity
    arts-week9
    521. Longest Uncommon Subsequence I
    arts-week8
    学习linux/unix编程方法的建议,学习Linux的四个步骤(转)
    对Linux内核tty设备的一点理解(转)
    ARM微处理器中支持字节、半字、字三种数据类型,地址的低两位为0是啥意思?
    c语言中 char* 和 unsigned char* 的区别浅析(转)
    命名空间的定义与使用(转)
    每日一句古文(转)
  • 原文地址:https://www.cnblogs.com/dalianpai/p/14745758.html
Copyright © 2011-2022 走看看