java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > @EnableWebSecurity注解

SpringSecurity的@EnableWebSecurity注解详解

作者:dalianpai

这篇文章主要介绍了SpringSecurity的@EnableWebSecurity注解详解,@EnableWebSecurity是开启SpringSecurity的默认行为,它的上面有一个Import注解导入了WebSecurityConfiguration类,就是往IOC容器中注入了WebSecurityConfiguration这个类,需要的朋友可以参考下

@EnableWebSecurity

@EnableWebSecurity是开启SpringSecurity的默认行为,它的上面有一个Import注解导入了WebSecurityConfiguration类,也就是说我们加上了@EnableWebSecurity这个注解,就是往IOC容器中注入了WebSecurityConfiguration这个类。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class})
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
    boolean debug() default false;
}

它还有一个debug的功能,如果设置为true,则开启debug功能,每个经过那些过滤器都会被展示出来。

image-20210808122218037

WebSecurityConfiguration

WebSecurityConfiguration用来配置初始化webSecurity的,在setFilterChainProxySecurityConfigurer方法中,它以配置SpringSecurity时继承自WebSecurityConfigurerAdapter的配置类来初始化SecurityConfigurer列表,来启用所需的安全策略

@Autowired(
        required = false
    )
    public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor, @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception {
        this.webSecurity = (WebSecurity)objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
        if (this.debugEnabled != null) {
            this.webSecurity.debug(this.debugEnabled);
        }
 
        webSecurityConfigurers.sort(WebSecurityConfiguration.AnnotationAwareOrderComparator.INSTANCE);
        Integer previousOrder = null;
        Object previousConfig = null;
 
        Iterator var5;
        SecurityConfigurer config;
        for(var5 = webSecurityConfigurers.iterator(); var5.hasNext(); previousConfig = config) {
            config = (SecurityConfigurer)var5.next();
            Integer order = WebSecurityConfiguration.AnnotationAwareOrderComparator.lookupOrder(config);
            if (previousOrder != null && previousOrder.equals(order)) {
                throw new IllegalStateException("@Order on WebSecurityConfigurers must be unique. Order of " + order + " was already used on " + previousConfig + ", so it cannot be used on " + config + " too.");
            }
 
            previousOrder = order;
        }
 
        var5 = webSecurityConfigurers.iterator();
 
        while(var5.hasNext()) {
            config = (SecurityConfigurer)var5.next();
            //将配置的每一个SecurityConfigurer列表传递给 webSecurity
            this.webSecurity.apply(config);
        }
 
        this.webSecurityConfigurers = webSecurityConfigurers;
    }

WebSecurityConfiguration这个类的创建流程也是经过spring容器初始化的那一整套。

image-20210808131543640

因为我们配置的SpringSecurityConfig这个类,继承了WebSecurityConfigurerAdapter,它又实现了SecurityConfigurer这个接口,所以在配置的时候,能拿到我们这个配置类里面的信息,具体如图:

image-20210808133759715

创建过滤器链

//提供一个名叫springSecurityFilterChain的bean,返回一个Filter对象
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public Filter springSecurityFilterChain() throws Exception {
		boolean hasConfigurers = webSecurityConfigurers != null
				&& !webSecurityConfigurers.isEmpty();
		if (!hasConfigurers) {
             //如果没有配置过Spring Security,则会议WebSecurityConfigurerAdapter中的配置作为默认,上面能拿到我们的配置,因此就不走这段逻辑
			WebSecurityConfigurerAdapter
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
					.postProcess(new WebSecurityConfigurerAdapter() {
					});
			webSecurity.apply(adapter);
		}
		return webSecurity.build();
	}

以前在Spring的配置中,会有一个web.xml,在里面配置过滤器,但是现在SpringBoot已经自动配置了web.xml. DelegatingFilterProxy是Spring提供的一个标准Servlet Filter代理,并代理改bean提供的过滤器,也就是说,在这个配置中,最终起作用的过滤器是什么完全取决于springSecurityFilterChain。

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(SecurityProperties.class)
@ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class })
@AutoConfigureAfter(SecurityAutoConfiguration.class)
public class SecurityFilterAutoConfiguration {
 
	private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
 
	@Bean
	@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
	public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
			SecurityProperties securityProperties) {
		DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
				DEFAULT_FILTER_NAME);
		registration.setOrder(securityProperties.getFilter().getOrder());
		registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
		return registration;
	}
 
	private EnumSet<DispatcherType> getDispatcherTypes(SecurityProperties securityProperties) {
		if (securityProperties.getFilter().getDispatcherTypes() == null) {
			return null;
		}
		return securityProperties.getFilter().getDispatcherTypes().stream()
				.map((type) -> DispatcherType.valueOf(type.name()))
				.collect(Collectors.toCollection(() -> EnumSet.noneOf(DispatcherType.class)));
	}
 
}

前面说的springSecurityFilterChain是由 webSecurity.build()这个创建的,最终调用的是doBuild方法,是由AbstractConfiguredSecurityBuilder提供的

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

AbstractConfiguredSecurityBuilder的doBuild调用的是WebSecurity的performBuild()方法

@Override
	protected final O doBuild() throws Exception {
		synchronized (configurers) {
            //安装状态依次执行相应的方法
			buildState = BuildState.INITIALIZING;
 
			beforeInit();
            //初始化状态,通过调用WebSecurityConfigurerAdapter的init,将所有HttpSecurity添加到WebSecurity里
			init();
 
			buildState = BuildState.CONFIGURING;
 
			beforeConfigure();
			configure();
 
			buildState = BuildState.BUILDING;
      //在BUILDING阶段调用WebSecurity的performBuild方法
			O result = performBuild();
 
			buildState = BuildState.BUILT;
 
			return result;
		}
	}

在init方法中,初始化状态,通过调用WebSecurityConfigurerAdapter的init,将所有HttpSecurity添加到WebSecurity里

image-20210808135049557

在performBuild方法中,SpringSecurity完成了所有过滤器的创建,最终返回一个过滤器链代理类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));
		}
         //简单来说,就是每一个HttpSecurity生成一个过滤器链,HttpSecurity则来自我们配置的WebSecurityConfigure
		for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
			securityFilterChains.add(securityFilterChainBuilder.build());
		}
		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;
	}

image-20210808134236782

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

doFilterInternal是真正执行虚拟过滤器链逻辑的方法

private void doFilterInternal(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
    //附上Spring Security提供的HTTP防火墙
		FirewalledRequest fwRequest = firewall
				.getFirewalledRequest((HttpServletRequest) request);
		HttpServletResponse fwResponse = firewall
				.getFirewalledResponse((HttpServletResponse) response);
     //按照配置的RequestMatcher,决定每一个请求会经过那些过滤器
		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 static class VirtualFilterChain implements FilterChain {
		private final FilterChain originalChain;
		private final List<Filter> additionalFilters;
		private final FirewalledRequest firewalledRequest;
		private final int size;
		private int currentPosition = 0;
 
		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();
                               执行过滤器链后,调用真实的FilterChain,完成原生过滤器的剩余逻辑
				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);
			}
		}
	}

image-20210808132205008

image-20210808132636043

到此这篇关于SpringSecurity的@EnableWebSecurity注解详解的文章就介绍到这了,更多相关@EnableWebSecurity注解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文