赞
踩
这是Spring Security官方提供的入门示例:
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/", "/home").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .permitAll() .and() .logout() .permitAll(); } @Bean @Override public UserDetailsService userDetailsService() { UserDetails user = User.withDefaultPasswordEncoder() .username("user") .password("password") .roles("USER") .build(); return new InMemoryUserDetailsManager(user); } }
从上面的示例可以看到,WebSecurityConfig
上有两个注解:
@Configuration
:,Spring Configuration,用来注册bean。详情可以查看Configuration详解@EnableWebSecurity
:用来开启Spring Security支持。如果需要自定义配置,可扩展WebSecurityConfigurerAdapter
并覆盖其一些方法来设置Web安全配置的某些细节。@EnableGlobalMethodSecurity(prePostEnabled = true)
,用来开启@PreFilter
,@PreAuthorize
,@PostAuthorize
,@PostFilter
注解的支持解释一下这样配置的作用:
该configure(HttpSecurity)
方法定义应保护哪些URL路径,不应该保护哪些URL路径。具体来说, /
和/home
路径配置为不需要任何身份验证。所有其他路径必须经过验证。
用户成功登录后,他们将被重定向到之前要求身份验证的页面。有一个自定义/login
页面(由指定loginPage()
),每个人都可以查看它。
该userDetailsService()
方法与单个用户一起建立内存用户存储。该用户的用户名为user
,密码为password
,角色为USER
。
@Import({ WebSecurityConfiguration.class,
SpringWebMvcImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
boolean debug() default false;
}
@Import
是用于Spring Boot提供的引入外部配置的注解,具体如何加载可查看bean加载
用来配置web Security。在这个类中有一个非常重要的Bean被注册了。
private WebSecurity webSecurity;
@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);
}
return webSecurity.build();
}
通过WebSecurity#build
创建了filter springSecurityFilterChain
,build方法在WebSecurity
详细说明。
而WebSecurity
的初始化是在:
@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); //... for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) { webSecurity.apply(webSecurityConfigurer); } this.webSecurityConfigurers = webSecurityConfigurers; }
创建WebSecurity
的实例,并将WebSecurityConfigurer
设置到实例中。那么WebSecurityConfigurer
来自于哪里呢? 注意看入参 @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}")List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers
。
public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
ConfigurableListableBeanFactory beanFactory) {
return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
}
//AutowiredWebSecurityConfigurersIgnoreParents#getWebSecurityConfigurers
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<SecurityConfigurer<Filter, WebSecurity>>();
Map<String, WebSecurityConfigurer> beansOfType = beanFactory
.getBeansOfType(WebSecurityConfigurer.class);
for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
获取注册在Spring容器中的所有WebSecurityConfigurer
bean。比如测试用例WebSecurityConfig
。如果没有自定义的实现类,即没有配置的webSecurityConfigurers
,则会new 一个WebSecurityConfigurerAdapter
作为默认的webSecurityConfigurers
。
如果当前的环境包含Spring MVC时,需要加载WebMvcSecurityConfiguration
,该配置文件用于用于为Spring MVC和Spring Security添加CSRF。
class SpringWebMvcImportSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
boolean webmvcPresent = ClassUtils.isPresent(
"org.springframework.web.servlet.DispatcherServlet",
getClass().getClassLoader());
return webmvcPresent
? new String[] {
"org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration" }
: new String[] {};
}
源码:
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import(AuthenticationConfiguration.class)
@Configuration
public @interface EnableGlobalAuthentication {
}
可以看出,这个注解引入了AuthenticationConfiguration
。主要用来初始化AuthenticationManager
@Configuration @Import(ObjectPostProcessorConfiguration.class) public class AuthenticationConfiguration { private AuthenticationManager authenticationManager; @Bean //创建默认的DefaultPasswordEncoderAuthenticationManagerBuilder 并注册EventPublisher public AuthenticationManagerBuilder authenticationManagerBuilder( ObjectPostProcessor<Object> objectPostProcessor, ApplicationContext context) { LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context); AuthenticationEventPublisher authenticationEventPublisher = getBeanOrNull(context, AuthenticationEventPublisher.class); DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder); if (authenticationEventPublisher != null) { result.authenticationEventPublisher(authenticationEventPublisher); } return result; } public AuthenticationManager getAuthenticationManager() throws Exception { if (this.authenticationManagerInitialized) { return this.authenticationManager; } AuthenticationManagerBuilder authBuilder = authenticationManagerBuilder( this.objectPostProcessor, this.applicationContext); if (this.buildingAuthenticationManager.getAndSet(true)) { return new AuthenticationManagerDelegator(authBuilder); } for (GlobalAuthenticationConfigurerAdapter config : globalAuthConfigurers) { authBuilder.apply(config); } authenticationManager = authBuilder.build(); if (authenticationManager == null) { //从beanFactory中getBean(AuthenticationManager.class) authenticationManager = getAuthenticationManagerBean(); } this.authenticationManagerInitialized = true; return authenticationManager; } }
和WebSecurity
一样,首先创建AuthenticationManagerBuilder
实例,然后通过AuthenticationManagerBuilder#build``创建
AuthenticationManager。而``globalAuthConfigurers``同样和
WebSecurity一样,可以通过像测试用例一样实现
GlobalAuthenticationConfigurerAdapter` 来配置全局的AuthenticationManagerBuilder
。
@Import({ GlobalMethodSecuritySelector.class }) @EnableGlobalAuthentication @Configuration public @interface EnableGlobalMethodSecurity { boolean prePostEnabled() default false; boolean securedEnabled() default false; boolean jsr250Enabled() default false; boolean proxyTargetClass() default false; AdviceMode mode() default AdviceMode.PROXY; int order() default Ordered.LOWEST_PRECEDENCE; }
prePostEnabled=true
,用来开启@PreFilter
,@PreAuthorize
,@PostAuthorize
,@PostFilter
注解的支持.securedEnabled=true
,开启@Secured
注解的支持jsr250Enabled=true
,开启JSR-250 注解的支持,如@RolesAllowed
,@PermitAll
,@DenyAll
@EnableGlobalAuthentication
上面已经说过了,这里主要看下GlobalMethodSecuritySelector
final class GlobalMethodSecuritySelector implements ImportSelector { public final String[] selectImports(AnnotationMetadata importingClassMetadata) { Class<EnableGlobalMethodSecurity> annoType = EnableGlobalMethodSecurity.class; Map<String, Object> annotationAttributes = importingClassMetadata .getAnnotationAttributes(annoType.getName(), false); AnnotationAttributes attributes = AnnotationAttributes .fromMap(annotationAttributes); Assert.notNull(attributes, String.format( "@%s is not present on importing class '%s' as expected", annoType.getSimpleName(), importingClassMetadata.getClassName())); // TODO would be nice if could use BeanClassLoaderAware (does not work) Class<?> importingClass = ClassUtils .resolveClassName(importingClassMetadata.getClassName(), ClassUtils.getDefaultClassLoader()); boolean skipMethodSecurityConfiguration = GlobalMethodSecurityConfiguration.class .isAssignableFrom(importingClass); //@EnableGlobalMethodSecurity的mode属性 AdviceMode mode = attributes.getEnum("mode"); boolean isProxy = AdviceMode.PROXY == mode; String autoProxyClassName = isProxy ? AutoProxyRegistrar.class .getName() : GlobalMethodSecurityAspectJAutoProxyRegistrar.class .getName(); //@EnableGlobalMethodSecurity的jsr250Enabled属性 boolean jsr250Enabled = attributes.getBoolean("jsr250Enabled"); List<String> classNames = new ArrayList<>(4); if(isProxy) { classNames.add(MethodSecurityMetadataSourceAdvisorRegistrar.class.getName()); } classNames.add(autoProxyClassName); if (!skipMethodSecurityConfiguration) { classNames.add(GlobalMethodSecurityConfiguration.class.getName()); } if (jsr250Enabled) { classNames.add(Jsr250MetadataSourceConfiguration.class.getName()); } return classNames.toArray(new String[0]); } }
根据注解配置的不同加载不同的Configuration。
@EnableGlobalMethodSecurity
上的mode
和jsr250Enabled
属性。将 GlobalMethodSecurityConfiguration
添加到扫描的list中。mode=PROXY
,加载 MethodSecurityMetadataSourceAdvisorRegistrar
,否则加载GlobalMethodSecurityAspectJAutoProxyRegistrar`,默认为前者。jsr250Enabled =true
,加载 Jsr250MetadataSourceConfiguration
该类主要配置了鉴权时的一些基础信息。
@Bean public MethodInterceptor methodSecurityInterceptor() throws Exception { this.methodSecurityInterceptor = isAspectJ() ? new AspectJMethodSecurityInterceptor() : new MethodSecurityInterceptor(); methodSecurityInterceptor.setAccessDecisionManager(accessDecisionManager()); methodSecurityInterceptor.setAfterInvocationManager(afterInvocationManager()); methodSecurityInterceptor .setSecurityMetadataSource(methodSecurityMetadataSource()); RunAsManager runAsManager = runAsManager(); if (runAsManager != null) { methodSecurityInterceptor.setRunAsManager(runAsManager); } return this.methodSecurityInterceptor; }
MethodInterceptor
MethodSecurityInterceptor
,当在方法上注释了@PreAuthorize
,会在这里验证权限,这一部分会在鉴权里详细展开。setAccessDecisionManager
,默认为AffirmativeBased
,鉴权时统计投票结果的,默认进行投票的是RoleVoter
和AuthenticatedVoter
,如果prePostEnabled=true
,会使用 PreInvocationAuthorizationAdviceVoter
来进行投票。afterInvocationManager
,处理鉴权后的返回结果,默认返回null,如果prePostEnabled=true
,则会返回AfterInvocationProviderManager
。setSecurityMetadataSource
,用来保存自定义权限的元数据,鉴权时使用,如prePostEnabled=true
时,会解析@PreAuthorize
。afterSingletonsInstantiated
,设置AuthenticationManager
到MethodInterceptor
,这里的AuthenticationManager
来自于AuthenticationConfiguration
。MethodSecurityMetadataSourceAdvisorRegistrar
class MethodSecurityMetadataSourceAdvisorRegistrar implements ImportBeanDefinitionRegistrar { public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { BeanDefinitionBuilder advisor = BeanDefinitionBuilder .rootBeanDefinition(MethodSecurityMetadataSourceAdvisor.class); advisor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); advisor.addConstructorArgValue("methodSecurityInterceptor"); advisor.addConstructorArgReference("methodSecurityMetadataSource"); advisor.addConstructorArgValue("methodSecurityMetadataSource"); MultiValueMap<String, Object> attributes = importingClassMetadata.getAllAnnotationAttributes(EnableGlobalMethodSecurity.class.getName()); Integer order = (Integer) attributes.getFirst("order"); if(order != null) { advisor.addPropertyValue("order", order); } registry.registerBeanDefinition("metaDataSourceAdvisor", advisor.getBeanDefinition()); } }
注册bean MethodSecurityMetadataSourceAdvisor
,将 methodSecurityInterceptor
和 methodSecurityMetadataSource
注入到构造方法。当调用方法时,该类会返回对应的MethodInterceptor
,MethodInterceptor
用于AOP。
Jsr250MetadataSourceConfiguration
@Configuration
class Jsr250MetadataSourceConfiguration {
@Bean
public Jsr250MethodSecurityMetadataSource jsr250MethodSecurityMetadataSource() {
return new Jsr250MethodSecurityMetadataSource();
}
}
SecurityMetadataSource
用来解析@EnableGlobalMethodSecurity
配置所支持的注解。Jsr250MethodSecurityMetadataSource
用来解析 @RolesAllowed
,@PermitAll
,@DenyAll
接下来详细分析一下 build
方法。
HttpSecurity
、WebSecurity
、AnthenticationManagerBuilder
都实现了SecurityBuilder
,首先来看下SecurityBuilder
的源码:
public interface SecurityBuilder<O> {
/**
* Builds the object and returns it or null.
*/
O build() throws Exception;
}
只有一个build方法,用来构建一个对象
AbstractSecurityBuilder
实现了SecurityBuilder
,并使用CAS来调用build。
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> { private AtomicBoolean building = new AtomicBoolean(); private O object; public final O build() throws Exception { //CAS 构建对象。 if (this.building.compareAndSet(false, true)) { this.object = doBuild(); return this.object; } throw new AlreadyBuiltException("This object has already been built"); } public final O getObject() { if (!this.building.get()) { throw new IllegalStateException("This object has not been built"); } return this.object; } //抽象方法build protected abstract O doBuild() throws Exception; }
抽象类AbstractConfiguredSecurityBuilder
继承了AbstractSecurityBuilder
。
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>> extends AbstractSecurityBuilder<O> { //属性 private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>>(); //apply public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception { add(configurer); return configurer; } private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception { Assert.notNull(configurer, "configurer cannot be null"); Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer .getClass(); synchronized (configurers) { if (buildState.isConfigured()) { throw new IllegalStateException("Cannot apply " + configurer + " to already built object"); } List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers .get(clazz) : null; if (configs == null) { configs = new ArrayList<SecurityConfigurer<O, B>>(1); } configs.add(configurer); this.configurers.put(clazz, configs); if (buildState.isInitializing()) { this.configurersAddedInInitializing.add(configurer); } } } //build protected final O doBuild() throws Exception { synchronized (configurers) { buildState = BuildState.INITIALIZING; beforeInit(); init();//SecurityConfigurer#init方法 buildState = BuildState.CONFIGURING; beforeConfigure(); configure(); buildState = BuildState.BUILDING; O result = performBuild(); buildState = BuildState.BUILT; return result; } } }
configurers
,调用apply方法时会保存SecurityConfigurer
beforeInit()
,默认什么都不做init()
,调用SecurityConfigurer
的init方法beforeConfigure()
,默认什么都不做configure()
,调用SecurityConfigurer
的configure方法performBuild()
,抽象方法 ,提供给子类实现。接下来具体解释一下每个子类的构建方法
从上文得知,WebSecurity#build
是用来构建filter的,源码如下:
protected Filter performBuild() throws Exception { 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 = new FilterChainProxy(securityFilterChains); if (httpFirewall != null) { filterChainProxy.setFirewall(httpFirewall); } filterChainProxy.afterPropertiesSet(); Filter result = filterChainProxy; postBuildAction.run(); return result; }
创建FilterChainProxy
,并为其添加Security过滤器链。如果配置了ignoredRequest
,如:
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resource/**");
}
}
会將DefaultSecurityFilterChain
添加到链。将其余配置的也添加到链。
WebSecurity
还有很多其它方法,这里只列个大概:
HttpSecurity#build
调用目前没有在源码中看到,不过还是解读一下它的源码:
protected DefaultSecurityFilterChain performBuild() throws Exception {
Collections.sort(filters, comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}
返回一个默认的过滤器链,而filters
的来源自 #addFilter
方法,比如:
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilter(usernamePasswordAuthenticationFilter());
http.addFilterBefore(authenticationTokenFilterBean(),UsernamePasswordAuthenticationFilter.class);
}
}
常用配置有:
@Configuration @EnableWebSecurity public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/resources/**").permitAll() .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/user/**").access("hasRole('ADMIN') and hasRole('USER')") .anyRequest().authenticated() .and() .formLogin() .usernameParameter("username") .passwordParameter("password") .failureForwardUrl("/login?error") .loginPage("/login") .permitAll() .and() .logout() .logoutUrl("/logout") .logoutSuccessUrl("/bye") .permitAll() .and() .httpBasic() .disable(); } }
当配置HttpSecurity
或者WebSecurity
,比如http.authorizeRequests()
,authorizeRequests
会将对应的SecurityConfigurer
添加到构建器,构建时调用init
方法和configure
方法。
public ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests() throws Exception { ApplicationContext context = getContext(); return getOrApply(new ExpressionUrlAuthorizationConfigurer<>(context)) .getRegistry(); } private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply( C configurer) throws Exception { C existingConfig = (C) getConfigurer(configurer.getClass()); if (existingConfig != null) { return existingConfig; } //还是这个方法 return apply(configurer); }
这里的构建比较简单,就是创建一个实例,然后将authenticationProviders
和parentAuthenticationManager
注入。
protected ProviderManager performBuild() throws Exception {
ProviderManager providerManager = new ProviderManager(authenticationProviders,
parentAuthenticationManager);
//...
providerManager = postProcess(providerManager);
return providerManager;
}
其它的主要方法有:
public class AuthenticationManagerBuilder { //配置父AuthenticationManager public AuthenticationManagerBuilder parentAuthenticationManager( AuthenticationManager authenticationManager) { ... } //配置事件发布器 public AuthenticationManagerBuilder authenticationEventPublisher( AuthenticationEventPublisher eventPublisher) { ... } //认证结束后是否删除密码凭证 public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) { ... } //配置内存用户 public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication() throws Exception { return apply(new InMemoryUserDetailsManagerConfigurer<>()); } //配置数据库用户 public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication() throws Exception { return apply(new JdbcUserDetailsManagerConfigurer<>()); } //配置userDetailService public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> userDetailsService( T userDetailsService) throws Exception { ... } //配置authenticationProvider public AuthenticationManagerBuilder authenticationProvider( AuthenticationProvider authenticationProvider) { this.authenticationProviders.add(authenticationProvider); return this; } }
具体根据实际情况进行配置,注意:UserDetailService会默认加载DaoAuthenticationProvider。
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(jwtUserDetailsServiceImpl()).passwordEncoder(passwordEncoder());
}
这里还要做一个说明AuthenticationManagerBuilder
有两种配置方式,一种是如上,还有一种是文章开头的配置,如果你的应用只有唯一一个WebSecurityConfigurerAdapter
,那么他们之间的差距可以被忽略,从方法名可以看出两者的区别:使用@Autowired
注入的AuthenticationManagerBuilder
是全局的身份认证器,作用域可以跨越多个WebSecurityConfigurerAdapter,以及影响到基于Method的安全控制;而 protected configure()
的方式则类似于一个匿名内部类,它的作用域局限于一个WebSecurityConfigurerAdapter
内部。
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
void init(B builder) throws Exception;
void configure(B builder) throws Exception;
}
SecurityBuilder
SecurityBuilder
。SecurityConfigurer
有两个比较重要的实现类,一个是WebSecurityConfigurerAdapter
还有一个是ExpressionUrlAuthorizationConfigurer
。
WebSecurityConfigurerAdapter
默认配置了一部分Security信息,
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();//初始化http
web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
public void run() {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
//添加FilterSecurityInterceptor
web.securityInterceptor(securityInterceptor);
}
});
}
初始化http时会默认添加filter
protected final HttpSecurity getHttp(){ //... http = new HttpSecurity(objectPostProcessor, authenticationBuilder, sharedObjects); //默认为false if (!disableDefaults) { // @formatter:off http .csrf().and() .addFilter(new WebAsyncManagerIntegrationFilter()) .exceptionHandling().and() .headers().and() .sessionManagement().and() .securityContext().and() .requestCache().and() .anonymous().and() .servletApi().and() .apply(new DefaultLoginPageConfigurer<>()).and() .logout(); // @formatter:on ClassLoader classLoader = this.context.getClassLoader(); List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader); for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) { http.apply(configurer); } } }
这里通过配置将对应的SecurityConfigurer
添加到SecurityBuilder
,然后再执行configure方法时添加filter到httpSecurity,比如SecurityContextPersistenceFilter
,当配置了securityContext()
时,将SecurityContextConfigurer
添加,而SecurityContextConfigurer#configure
执行时,会将SecurityContextPersistenceFilter
添加到httpSecurity中。
SecurityContextConfigurer#configure
public void configure(H http) throws Exception { SecurityContextRepository securityContextRepository = http .getSharedObject(SecurityContextRepository.class); if(securityContextRepository == null) { securityContextRepository = new HttpSessionSecurityContextRepository(); } SecurityContextPersistenceFilter securityContextFilter = new SecurityContextPersistenceFilter( securityContextRepository); SessionManagementConfigurer<?> sessionManagement = http .getConfigurer(SessionManagementConfigurer.class); SessionCreationPolicy sessionCreationPolicy = sessionManagement == null ? null : sessionManagement.getSessionCreationPolicy(); if (SessionCreationPolicy.ALWAYS == sessionCreationPolicy) { securityContextFilter.setForceEagerSessionCreation(true); } securityContextFilter = postProcess(securityContextFilter); http.addFilter(securityContextFilter); }
初始化HttpSecurity
,这里会将UserDetailService
添加到HttpSecurity
中。可以通过继承来实现扩展,比如上面的测试用例,常用的方法有3个:
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/", "/home").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .permitAll() .and() .logout() .permitAll(); } @Override public void configure(WebSecurity web) throws Exception { web .ignoring() .antMatchers("/resource/**"); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(jwtUserDetailsServiceImpl()).passwordEncoder(passwordEncoder()); } }
当你配置了http.authorizeRequests()...
时,会加载ExpressionUrlAuthorizationConfigurer
,它的configure
方法在父类AbstractInterceptUrlConfigurer
中,init方法默认什么都不做。
public void configure(H http) throws Exception { //解析配在HttpSecurity的url路径 FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http); if (metadataSource == null) { return; } //创建过滤器 FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor( http, metadataSource, http.getSharedObject(AuthenticationManager.class)); if (filterSecurityInterceptorOncePerRequest != null) { securityInterceptor .setObserveOncePerRequest(filterSecurityInterceptorOncePerRequest); } securityInterceptor = postProcess(securityInterceptor); http.addFilter(securityInterceptor); http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor); }
.antMatchers("/", "/home").permitAll()
等,封装在SecurityMetadataSource
中,以备后面鉴权时使用FilterSecurityInterceptor
,用来拦截请求并鉴权,这里只验证配在HttpSecurity
中的。要和上文的MethodInterceptor
区分开。这里配置的AccessDecisionManager
默认依然是AffirmativeBased
,默认进行投票的是WebExpressionVoter
。Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。