
Spring Security的WebSecurityConfiguration配置及初始化流程
基于源码的流程分析H2
下面的源码基于Spring Security 4.0.3版本。
入口 WebSecurityConfigurationH3
WebSecurityConfiguration的目的是配置WebSecurity来创建[FilterChainProxy][FilterChainProxy]
bash
@Autowired(required = false)public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor,@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)throws Exception {webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));if (debugEnabled != null) {webSecurity.debug(debugEnabled);}Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);Integer previousOrder = null;for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {Integer order = AnnotationAwareOrderComparator.lookupOrder(config);if (previousOrder != null && previousOrder.equals(order)) {throw new IllegalStateException("@Order on WebSecurityConfigurers must be unique. Order of "+ order + " was already used, so it cannot be used on "+ config + " too.");}previousOrder = order;}for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {webSecurity.apply(webSecurityConfigurer);}this.webSecurityConfigurers = webSecurityConfigurers;}
这里会对WebSecurity套用所有的SecurityConfigurer的实例,包括自定义的继承了WebSecurityConfigurerAdapter的自定义配置。
这里的套用的过程只是把实例添加到configurers属性中
bash
public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {add(configurer);return configurer;}
最终在build的过程中会初始化这些配置并套用。
配置HttpSecurityH3
WebSecurityConfigurerAdapter会初始化一个默认的HttpSecurity,同时会调用protected void configure(HttpSecurity http)来进行自定义的设置。最终HttpSecurity的实例会作为SecurityFilterChainBuilder传入WebSecurity,以及调用postBuildAction来设置FilterSecurityInterceptor。
bash
public void init(final WebSecurity web) throws Exception {final HttpSecurity http = getHttp();web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {public void run() {FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);web.securityInterceptor(securityInterceptor);}});}
WebSecurity中的FilterSecurityInterceptor最终还会用来构建WebInvocationPrivilegeEvaluator用于页面标签的权限控制。从目前源码上来看,页面标签的WebInvocationPrivilegeEvaluator只会使用最后设置到WebSecurity中的FilterSecurityInterceptor。如果有多个WebSecurityConfigurerAdapter的子类实例,那么只有最后一个HttpSecurity中的FilterSecurityInterceptor才会生效,其余的会被覆盖。所以,页面标签的权限控制要注意HttpSecurity配置的顺序,WebSecurityConfigurerAdapter默认优先度是100
bash
/*** Creates the {@link HttpSecurity} or returns the current instance** ] * @return the {@link HttpSecurity}* @throws Exception*/protected final HttpSecurity getHttp() throws Exception {if (http != null) {return http;}DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);AuthenticationManager authenticationManager = authenticationManager();authenticationBuilder.parentAuthenticationManager(authenticationManager);http = new HttpSecurity(objectPostProcessor, authenticationBuilder,localConfigureAuthenticationBldr.getSharedObjects());http.setSharedObject(UserDetailsService.class, userDetailsService());http.setSharedObject(ApplicationContext.class, context);http.setSharedObject(ContentNegotiationStrategy.class, contentNegotiationStrategy);http.setSharedObject(AuthenticationTrustResolver.class, trustResolver);if (!disableDefaults) {// @formatter:offhttp.csrf().and().addFilter(new WebAsyncManagerIntegrationFilter()).exceptionHandling().and().headers().and().sessionManagement().and().securityContext().and().requestCache().and().anonymous().and().servletApi().and().apply(new DefaultLoginPageConfigurer<HttpSecurity>()).and().logout();// @formatter:on}configure(http);return http;}
构建FilterH3
WebSecurity最终会构建Servlet Filter。HttpSecurity的performBuild则最终会构建DefaultSecurityFilterChain对象,然后添加到Filter的securityFilterChains中
bash
protected final O doBuild() throws Exception {synchronized (configurers) {buildState = BuildState.INITIALIZING;beforeInit();init();buildState = BuildState.CONFIGURING;beforeConfigure();configure();buildState = BuildState.BUILDING;O result = performBuild();buildState = BuildState.BUILT;return result;}}
bash
@Overrideprotected 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<SecurityFilterChain>(chainSize);for (RequestMatcher ignoredRequest : ignoredRequests) {securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));}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;}
相关的一些问题H2
Spring Boot的H2ConsoleAutoConfiguration导致页面标签的权限控制不正常H3
如果Spring Boot启用了H2 Console的话,由于H2ConsoleAutoConfiguration并没有注解@ConditionalOnMissingBean(WebSecurityConfiguration.class),所以即便应用配置了WebSecurityConfiguration的子类,如果没有显示地把security.basic.enabled设置成false的话,最终还是会导致H2ConsoleSecurityConfiguration配置的套用,H2ConsoleSecurityConfiguration的优先度是SecurityProperties.BASIC_AUTH_ORDER - 10,比较低,反而容易导致WebSecurity的FilterSecurityInterceptor被覆盖。
评论
新的评论
上一篇
The last packet successfully received from the server was xxx ago
问题 系统长时间运行,但是没有人使用后,再次使用时会无法连接上数据库,会报类似这样的错误 org.hibernate.util.JDBCExceptionReporter: The last packet successfully received from the serve…
下一篇
Spring Security RememberMe
Remember-Me接口和实现 Remember-me是和 UsernamePasswordAuthenticationFilter 一起使用的,通过其父类 AbstractAuthenticationProcessingFilter 中的钩子来触发回调来实现用户信息的保存。…
