本文我们主要梳理下SpringSecurity
的原理,SpringSecurity
主要是用于认证、授权的,其的主要原理就是通过Filter
来处理。
一、SpringSecurity的基本原理
1、基本流程
SpringSecurity
的基本原理就是应用了Tomcat
容器的Filter
,其的实现原理也就是类似于Tomcat
本身的ApplicationFilterChain
,也就是Filter
执行链。
publicfinalclassApplicationFilterChainimplementsFilterChain{........privateApplicationFilterConfig[]filters=newApplicationFilterConfig[0];...........publicApplicationFilterChain(){}publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse)throwsIOException,ServletException{..........}else{this.internalDoFilter(request,response);}}privatevoidinternalDoFilter(ServletRequestrequest,ServletResponseresponse)throwsIOException,ServletException{if(this.pos<this.n){ApplicationFilterConfigfilterConfig=this.filters[this.pos++];try{Filterfilter=filterConfig.getFilter();.........}else{filter.doFilter(request,response,this);}.........}else{try{.........}else{this.servlet.service(request,response);}}........}}}
ApplicationFilterChain
本身就是先执行所有的filters
,执行完成后,其就会执行当前请求的Servlet
,对于SpringMVC
来说,就是DispatchSevelt
:
这里也就是说,先执行Tomcat
自身已有的Filter
,然后再交给SpringSecurity
定义的FilterChainProxy
,然后其再去执行SpringSecurity
用于认证、授权管理的各种Filter
。这个就是SpringSecurity
的核心原理。
2、默认注入的Filter列表
我们看下默认引入spring-boot-starter-security
后其会默认加入的Filter
:
3、FilterChainProxy
publicclassFilterChainProxyextendsGenericFilterBean{..........privatevoiddoFilterInternal(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{......List<Filter>filters=getFilters(firewallRequest);........VirtualFilterChainvirtualFilterChain=newVirtualFilterChain(firewallRequest,chain,filters);virtualFilterChain.doFilter(firewallRequest,firewallResponse);}........privatestaticfinalclassVirtualFilterChainimplementsFilterChain{privatefinalFilterChainoriginalChain;privatefinalList<Filter>additionalFilters;..........@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse)throwsIOException,ServletException{if(this.currentPosition==this.size){if(logger.isDebugEnabled()){logger.debug(LogMessage.of(()->"Secured"+requestLine(this.firewalledRequest)));}//Deactivatepathstrippingasweexitthesecurityfilterchainthis.firewalledRequest.reset();this.originalChain.doFilter(request,response);return;}this.currentPosition++;FilternextFilter=this.additionalFilters.get(this.currentPosition-1);if(logger.isTraceEnabled()){logger.trace(LogMessage.format("Invoking%s(%d/%d)",nextFilter.getClass().getSimpleName(),this.currentPosition,this.size));}nextFilter.doFilter(request,response,this);}}
二、SpringSecurity对FilterChain的组装初始化
其的组装是借助于两个类来添加关于认证、授权相关的信息,由这些信息来添加FilterChain
中需要的Filter
,这两个类就是WebSecurity
、HttpSecurity
。
1、WebSecurity、HttpSecurity
这两个类都是继承的AbstractConfiguredSecurityBuilder
,以及实现SecurityBuilder
接口(用于构建对象)。
publicfinalclassWebSecurityextendsAbstractConfiguredSecurityBuilder<Filter,WebSecurity>implementsSecurityBuilder<Filter>,ApplicationContextAware{
publicfinalclassHttpSecurityextendsAbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain,HttpSecurity>implementsSecurityBuilder<DefaultSecurityFilterChain>,HttpSecurityBuilder<HttpSecurity>{
publicinterfaceSecurityBuilder<O>{Obuild()throwsException;}
2、AbstractConfiguredSecurityBuilder
publicabstractclassAbstractConfiguredSecurityBuilder<O,BextendsSecurityBuilder<O>>extendsAbstractSecurityBuilder<O>{privatefinalLoglogger=LogFactory.getLog(getClass());privatefinalLinkedHashMap<Class<?extendsSecurityConfigurer<O,B>>,List<SecurityConfigurer<O,B>>>configurers=newLinkedHashMap<>();privatefinalList<SecurityConfigurer<O,B>>configurersAddedInInitializing=newArrayList<>();privatefinalMap<Class<?>,Object>sharedObjects=newHashMap<>();
这个基类主要有两个成员变量需要关注,也就是configurers
,与sharedObjects
。configurers(SecurityConfigurer)
这个主要是添加的各种配置信息。然后sharedObjects
中是添加一些共享的类对象,然后需要就能取出来。例如:
privateMap<Class<?>,Object>createSharedObjects(){Map<Class<?>,Object>sharedObjects=newHashMap<>();sharedObjects.putAll(this.localConfigureAuthenticationBldr.getSharedObjects());sharedObjects.put(UserDetailsService.class,userDetailsService());sharedObjects.put(ApplicationContext.class,this.context);sharedObjects.put(ContentNegotiationStrategy.class,this.contentNegotiationStrategy);sharedObjects.put(AuthenticationTrustResolver.class,this.trustResolver);returnsharedObjects;}
而对于SecurityConfigurer
的添加主要是使用apply()
方法:
public<CextendsSecurityConfigurer<O,B>>Capply(Cconfigurer)throwsException{add(configurer);returnconfigurer;}
3、SecurityConfigurer
publicinterfaceSecurityConfigurer<O,BextendsSecurityBuilder<O>>{voidinit(Bbuilder)throwsException;voidconfigure(Bbuilder)throwsException;}
其主要是两个方法,一个就是先init
产生化,另一个就是configure
,也就是通过configure
方法来设置SecurityBuilder
中的内容。
下面我们来看下WebSecurity
的构建,以及通过其来产生FilterChainProxy
:
@Autowired(required=false)publicvoidsetFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object>objectPostProcessor,@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}")List<SecurityConfigurer<Filter,WebSecurity>>webSecurityConfigurers)throwsException{this.webSecurity=objectPostProcessor.postProcess(newWebSecurity(objectPostProcessor));if(this.debugEnabled!=null){this.webSecurity.debug(this.debugEnabled);}..........for(SecurityConfigurer<Filter,WebSecurity>webSecurityConfigurer:webSecurityConfigurers){this.webSecurity.apply(webSecurityConfigurer);}this.webSecurityConfigurers=webSecurityConfigurers;}
这里我们看到入参有webSecurityConfigurers(List<SecurityConfigurer)
,而我们当前有添加一个WebSecurityConfigurerAdapter
, 其是继承与WebSecurityConfigurerAdapter
,可以看到其是属于SecurityConfigurer
,我们能通过其来进行对应的属性拓展
然后就是通过this.webSecurity.apply(webSecurityConfigurer)
来添加到configurers
中
4、构建Filter(FilterChainProxy)
publicclassFilterChainProxyextendsGenericFilterBean{..........privatevoiddoFilterInternal(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{......List<Filter>filters=getFilters(firewallRequest);........VirtualFilterChainvirtualFilterChain=newVirtualFilterChain(firewallRequest,chain,filters);virtualFilterChain.doFilter(firewallRequest,firewallResponse);}........privatestaticfinalclassVirtualFilterChainimplementsFilterChain{privatefinalFilterChainoriginalChain;privatefinalList<Filter>additionalFilters;..........@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse)throwsIOException,ServletException{if(this.currentPosition==this.size){if(logger.isDebugEnabled()){logger.debug(LogMessage.of(()->"Secured"+requestLine(this.firewalledRequest)));}//Deactivatepathstrippingasweexitthesecurityfilterchainthis.firewalledRequest.reset();this.originalChain.doFilter(request,response);return;}this.currentPosition++;FilternextFilter=this.additionalFilters.get(this.currentPosition-1);if(logger.isTraceEnabled()){logger.trace(LogMessage.format("Invoking%s(%d/%d)",nextFilter.getClass().getSimpleName(),this.currentPosition,this.size));}nextFilter.doFilter(request,response,this);}}0
publicclassFilterChainProxyextendsGenericFilterBean{..........privatevoiddoFilterInternal(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{......List<Filter>filters=getFilters(firewallRequest);........VirtualFilterChainvirtualFilterChain=newVirtualFilterChain(firewallRequest,chain,filters);virtualFilterChain.doFilter(firewallRequest,firewallResponse);}........privatestaticfinalclassVirtualFilterChainimplementsFilterChain{privatefinalFilterChainoriginalChain;privatefinalList<Filter>additionalFilters;..........@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse)throwsIOException,ServletException{if(this.currentPosition==this.size){if(logger.isDebugEnabled()){logger.debug(LogMessage.of(()->"Secured"+requestLine(this.firewalledRequest)));}//Deactivatepathstrippingasweexitthesecurityfilterchainthis.firewalledRequest.reset();this.originalChain.doFilter(request,response);return;}this.currentPosition++;FilternextFilter=this.additionalFilters.get(this.currentPosition-1);if(logger.isTraceEnabled()){logger.trace(LogMessage.format("Invoking%s(%d/%d)",nextFilter.getClass().getSimpleName(),this.currentPosition,this.size));}nextFilter.doFilter(request,response,this);}}1
publicclassFilterChainProxyextendsGenericFilterBean{..........privatevoiddoFilterInternal(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{......List<Filter>filters=getFilters(firewallRequest);........VirtualFilterChainvirtualFilterChain=newVirtualFilterChain(firewallRequest,chain,filters);virtualFilterChain.doFilter(firewallRequest,firewallResponse);}........privatestaticfinalclassVirtualFilterChainimplementsFilterChain{privatefinalFilterChainoriginalChain;privatefinalList<Filter>additionalFilters;..........@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse)throwsIOException,ServletException{if(this.currentPosition==this.size){if(logger.isDebugEnabled()){logger.debug(LogMessage.of(()->"Secured"+requestLine(this.firewalledRequest)));}//Deactivatepathstrippingasweexitthesecurityfilterchainthis.firewalledRequest.reset();this.originalChain.doFilter(request,response);return;}this.currentPosition++;FilternextFilter=this.additionalFilters.get(this.currentPosition-1);if(logger.isTraceEnabled()){logger.trace(LogMessage.format("Invoking%s(%d/%d)",nextFilter.getClass().getSimpleName(),this.currentPosition,this.size));}nextFilter.doFilter(request,response,this);}}2
这里的init()
,就是遍历configurers
进行初始化:
publicclassFilterChainProxyextendsGenericFilterBean{..........privatevoiddoFilterInternal(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{......List<Filter>filters=getFilters(firewallRequest);........VirtualFilterChainvirtualFilterChain=newVirtualFilterChain(firewallRequest,chain,filters);virtualFilterChain.doFilter(firewallRequest,firewallResponse);}........privatestaticfinalclassVirtualFilterChainimplementsFilterChain{privatefinalFilterChainoriginalChain;privatefinalList<Filter>additionalFilters;..........@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse)throwsIOException,ServletException{if(this.currentPosition==this.size){if(logger.isDebugEnabled()){logger.debug(LogMessage.of(()->"Secured"+requestLine(this.firewalledRequest)));}//Deactivatepathstrippingasweexitthesecurityfilterchainthis.firewalledRequest.reset();this.originalChain.doFilter(request,response);return;}this.currentPosition++;FilternextFilter=this.additionalFilters.get(this.currentPosition-1);if(logger.isTraceEnabled()){logger.trace(LogMessage.format("Invoking%s(%d/%d)",nextFilter.getClass().getSimpleName(),this.currentPosition,this.size));}nextFilter.doFilter(request,response,this);}}3
再通过configure()
遍历执行configurers
的configurer
方法:
publicclassFilterChainProxyextendsGenericFilterBean{..........privatevoiddoFilterInternal(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{......List<Filter>filters=getFilters(firewallRequest);........VirtualFilterChainvirtualFilterChain=newVirtualFilterChain(firewallRequest,chain,filters);virtualFilterChain.doFilter(firewallRequest,firewallResponse);}........privatestaticfinalclassVirtualFilterChainimplementsFilterChain{privatefinalFilterChainoriginalChain;privatefinalList<Filter>additionalFilters;..........@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse)throwsIOException,ServletException{if(this.currentPosition==this.size){if(logger.isDebugEnabled()){logger.debug(LogMessage.of(()->"Secured"+requestLine(this.firewalledRequest)));}//Deactivatepathstrippingasweexitthesecurityfilterchainthis.firewalledRequest.reset();this.originalChain.doFilter(request,response);return;}this.currentPosition++;FilternextFilter=this.additionalFilters.get(this.currentPosition-1);if(logger.isTraceEnabled()){logger.trace(LogMessage.format("Invoking%s(%d/%d)",nextFilter.getClass().getSimpleName(),this.currentPosition,this.size));}nextFilter.doFilter(request,response,this);}}4
例如我们当前添加了自定义的SecurityConfiguration
:
这里还有beforeConfigure()
方法,例如HttpSecurity
对其的实现:
publicclassFilterChainProxyextendsGenericFilterBean{..........privatevoiddoFilterInternal(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{......List<Filter>filters=getFilters(firewallRequest);........VirtualFilterChainvirtualFilterChain=newVirtualFilterChain(firewallRequest,chain,filters);virtualFilterChain.doFilter(firewallRequest,firewallResponse);}........privatestaticfinalclassVirtualFilterChainimplementsFilterChain{privatefinalFilterChainoriginalChain;privatefinalList<Filter>additionalFilters;..........@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse)throwsIOException,ServletException{if(this.currentPosition==this.size){if(logger.isDebugEnabled()){logger.debug(LogMessage.of(()->"Secured"+requestLine(this.firewalledRequest)));}//Deactivatepathstrippingasweexitthesecurityfilterchainthis.firewalledRequest.reset();this.originalChain.doFilter(request,response);return;}this.currentPosition++;FilternextFilter=this.additionalFilters.get(this.currentPosition-1);if(logger.isTraceEnabled()){logger.trace(LogMessage.format("Invoking%s(%d/%d)",nextFilter.getClass().getSimpleName(),this.currentPosition,this.size));}nextFilter.doFilter(request,response,this);}}5
添加认证管理器。对于SecurityConfigurer
的实现类来说,其主要是设置填充各种类对象例如我们当前填充了`UserDetailsService
,以及属于SpringSecurity
的Filter
。
5、HttpSecurity构建
对于HttpSecurity
的构建,其是在WebSecurity
下构建的:
publicclassFilterChainProxyextendsGenericFilterBean{..........privatevoiddoFilterInternal(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{......List<Filter>filters=getFilters(firewallRequest);........VirtualFilterChainvirtualFilterChain=newVirtualFilterChain(firewallRequest,chain,filters);virtualFilterChain.doFilter(firewallRequest,firewallResponse);}........privatestaticfinalclassVirtualFilterChainimplementsFilterChain{privatefinalFilterChainoriginalChain;privatefinalList<Filter>additionalFilters;..........@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse)throwsIOException,ServletException{if(this.currentPosition==this.size){if(logger.isDebugEnabled()){logger.debug(LogMessage.of(()->"Secured"+requestLine(this.firewalledRequest)));}//Deactivatepathstrippingasweexitthesecurityfilterchainthis.firewalledRequest.reset();this.originalChain.doFilter(request,response);return;}this.currentPosition++;FilternextFilter=this.additionalFilters.get(this.currentPosition-1);if(logger.isTraceEnabled()){logger.trace(LogMessage.format("Invoking%s(%d/%d)",nextFilter.getClass().getSimpleName(),this.currentPosition,this.size));}nextFilter.doFilter(request,response,this);}}6
我们看到其是在前面doBuild()
遍历调用init
调用的。创建后,通过web.addSecurityFilterChainBuilder(http)
添加到WebSecurity
中。而定义对于HttpSecurity
,其主要会通过SecurityConfigurer
来添加各种Filter
publicfinalclassHttpSecurityextendsAbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain,HttpSecurity>implementsSecurityBuilder<DefaultSecurityFilterChain>,HttpSecurityBuilder<HttpSecurity>{privatefinalRequestMatcherConfigurerrequestMatcherConfigurer;privateList<OrderedFilter>filters=newArrayList<>();privateRequestMatcherrequestMatcher=AnyRequestMatcher.INSTANCE;privateFilterOrderRegistrationfilterOrders=newFilterOrderRegistration();
publicclassFilterChainProxyextendsGenericFilterBean{..........privatevoiddoFilterInternal(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{......List<Filter>filters=getFilters(firewallRequest);........VirtualFilterChainvirtualFilterChain=newVirtualFilterChain(firewallRequest,chain,filters);virtualFilterChain.doFilter(firewallRequest,firewallResponse);}........privatestaticfinalclassVirtualFilterChainimplementsFilterChain{privatefinalFilterChainoriginalChain;privatefinalList<Filter>additionalFilters;..........@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse)throwsIOException,ServletException{if(this.currentPosition==this.size){if(logger.isDebugEnabled()){logger.debug(LogMessage.of(()->"Secured"+requestLine(this.firewalledRequest)));}//Deactivatepathstrippingasweexitthesecurityfilterchainthis.firewalledRequest.reset();this.originalChain.doFilter(request,response);return;}this.currentPosition++;FilternextFilter=this.additionalFilters.get(this.currentPosition-1);if(logger.isTraceEnabled()){logger.trace(LogMessage.format("Invoking%s(%d/%d)",nextFilter.getClass().getSimpleName(),this.currentPosition,this.size));}nextFilter.doFilter(request,response,this);}}8
这里主要有三部:
1)、创建AuthenticationManager
。
publicclassFilterChainProxyextendsGenericFilterBean{..........privatevoiddoFilterInternal(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{......List<Filter>filters=getFilters(firewallRequest);........VirtualFilterChainvirtualFilterChain=newVirtualFilterChain(firewallRequest,chain,filters);virtualFilterChain.doFilter(firewallRequest,firewallResponse);}........privatestaticfinalclassVirtualFilterChainimplementsFilterChain{privatefinalFilterChainoriginalChain;privatefinalList<Filter>additionalFilters;..........@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse)throwsIOException,ServletException{if(this.currentPosition==this.size){if(logger.isDebugEnabled()){logger.debug(LogMessage.of(()->"Secured"+requestLine(this.firewalledRequest)));}//Deactivatepathstrippingasweexitthesecurityfilterchainthis.firewalledRequest.reset();this.originalChain.doFilter(request,response);return;}this.currentPosition++;FilternextFilter=this.additionalFilters.get(this.currentPosition-1);if(logger.isTraceEnabled()){logger.trace(LogMessage.format("Invoking%s(%d/%d)",nextFilter.getClass().getSimpleName(),this.currentPosition,this.size));}nextFilter.doFilter(request,response,this);}}9
2)、再构建HttpSecurity
publicfinalclassWebSecurityextendsAbstractConfiguredSecurityBuilder<Filter,WebSecurity>implementsSecurityBuilder<Filter>,ApplicationContextAware{0
3)、填充HttpSecurity
的配置内容。
publicfinalclassWebSecurityextendsAbstractConfiguredSecurityBuilder<Filter,WebSecurity>implementsSecurityBuilder<Filter>,ApplicationContextAware{1
我们下面再具体梳理这个过程。
6、AuthenticationManager
1)、AuthenticationManager认证管理器
publicfinalclassWebSecurityextendsAbstractConfiguredSecurityBuilder<Filter,WebSecurity>implementsSecurityBuilder<Filter>,ApplicationContextAware{2
其首先会调用configure(AuthenticationManagerBuilder)
2)、AuthenticationManagerBuilder设置授权管理器的内容
publicfinalclassWebSecurityextendsAbstractConfiguredSecurityBuilder<Filter,WebSecurity>implementsSecurityBuilder<Filter>,ApplicationContextAware{3
这里你可以添加自己的AuthenticationProvider
、UserDetailsService
,或使用自带的内存UserDetailsServiceinMemoryAuthentication()
,其是通过SecurityConfigurer
来添加HttpSecurity
的属性。
例如我们当前的WebSecurityConfigurerAdapter
publicfinalclassWebSecurityextendsAbstractConfiguredSecurityBuilder<Filter,WebSecurity>implementsSecurityBuilder<Filter>,ApplicationContextAware{4
其就设置UserDetailsService
为我们自定义的。然后这里通过configure(AuthenticationManagerBuilder)
拓展好AuthenticationManagerBuilder
的一些属性后,下面就能通过来创建了。
3)、构建AuthenticationManager
publicfinalclassWebSecurityextendsAbstractConfiguredSecurityBuilder<Filter,WebSecurity>implementsSecurityBuilder<Filter>,ApplicationContextAware{5
这个build()
同样是父类AbstractSecurityBuilder
的方法:
publicfinalclassWebSecurityextendsAbstractConfiguredSecurityBuilder<Filter,WebSecurity>implementsSecurityBuilder<Filter>,ApplicationContextAware{6
其也是这一串逻辑:
这个也主要是SecurityConfigurer
的方法调用,然后其的performBuild()
方法
publicfinalclassWebSecurityextendsAbstractConfiguredSecurityBuilder<Filter,WebSecurity>implementsSecurityBuilder<Filter>,ApplicationContextAware{7
这里就讲其的authenticationProviders
设置到ProviderManager
, 也就是ProviderManager
认证管理器,通过其的AuthenticationProvider
也就是认证提供者来处理认证内容:
4)、ProviderManager认证处理管理器
publicfinalclassWebSecurityextendsAbstractConfiguredSecurityBuilder<Filter,WebSecurity>implementsSecurityBuilder<Filter>,ApplicationContextAware{8
我们看到这里是通过AuthenticationProvider
来处理产生Authentication
,也就是认证信息。
而对于这里的AuthenticationProvider
,一般是怎样添加的呢?对于AuthenticationManagerBuilder
,当我们设置UserDetailsService
的时候,其就会添加DaoAuthenticationConfigurer
publicfinalclassWebSecurityextendsAbstractConfiguredSecurityBuilder<Filter,WebSecurity>implementsSecurityBuilder<Filter>,ApplicationContextAware{9
publicfinalclassHttpSecurityextendsAbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain,HttpSecurity>implementsSecurityBuilder<DefaultSecurityFilterChain>,HttpSecurityBuilder<HttpSecurity>{0
其就会产生一个DaoAuthenticationProvider
,同时在configure(B builder)
方法的时候,就会将DaoAuthenticationProvider
设置到ProviderManagerBuilder
中。
publicfinalclassHttpSecurityextendsAbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain,HttpSecurity>implementsSecurityBuilder<DefaultSecurityFilterChain>,HttpSecurityBuilder<HttpSecurity>{1
publicfinalclassHttpSecurityextendsAbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain,HttpSecurity>implementsSecurityBuilder<DefaultSecurityFilterChain>,HttpSecurityBuilder<HttpSecurity>{2
这里首先是通过retrieveUser
方法使用UserDetailsService
来查询到对应的用户,如果查询不到就抛出UsernameNotFoundException
,如果能查询到,就再来解析认证判断,例如用户是否被锁定,密码是否匹配等,如果失败,就抛出AuthenticationException
认证失败。
publicfinalclassHttpSecurityextendsAbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain,HttpSecurity>implementsSecurityBuilder<DefaultSecurityFilterChain>,HttpSecurityBuilder<HttpSecurity>{3
publicfinalclassHttpSecurityextendsAbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain,HttpSecurity>implementsSecurityBuilder<DefaultSecurityFilterChain>,HttpSecurityBuilder<HttpSecurity>{4
而对于AuthenticationException
,也就是认证失败,我们看到这个异常是在接口控制的,所以一定要处理。
publicfinalclassHttpSecurityextendsAbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain,HttpSecurity>implementsSecurityBuilder<DefaultSecurityFilterChain>,HttpSecurityBuilder<HttpSecurity>{5
拿我们常见的UsernamePasswordAuthenticationFilter
,也就是账密登录的Filter
:
publicfinalclassHttpSecurityextendsAbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain,HttpSecurity>implementsSecurityBuilder<DefaultSecurityFilterChain>,HttpSecurityBuilder<HttpSecurity>{6
publicfinalclassHttpSecurityextendsAbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain,HttpSecurity>implementsSecurityBuilder<DefaultSecurityFilterChain>,HttpSecurityBuilder<HttpSecurity>{7
其认证失败后就会通过AuthenticationException
异常,来调用unsuccessfulAuthentication
处理认证失败的情况。
其就是通过failureHandler
来跳到/login?error
页面
然后对于UsernamePasswordAuthenticationFilter
来说,其后面就是DefaultLoginPageGeneratingFilter
其通过url
,就会构建登录页面,然后直接就return
了,就不会通过chain.doFilter(request, response)
到下面的其他Filter
以及Servlet
了。
publicfinalclassHttpSecurityextendsAbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain,HttpSecurity>implementsSecurityBuilder<DefaultSecurityFilterChain>,HttpSecurityBuilder<HttpSecurity>{8
5)、对于认证的流程
7、applyDefaultConfiguration(this.http)默认设置
我们接走上面的创建HttpSecurity
的步骤,来看下HttpSecurity
中的Filter
是怎样添加的:
protectedfinalHttpSecuritygetHttp()throwsException{if(this.http!=null){returnthis.http;}AuthenticationEventPublishereventPublisher=getAuthenticationEventPublisher();this.localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);publicclassFilterChainProxyextendsGenericFilterBean{..........privatevoiddoFilterInternal(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{......List<Filter>filters=getFilters(firewallRequest);........VirtualFilterChainvirtualFilterChain=newVirtualFilterChain(firewallRequest,chain,filters);virtualFilterChain.doFilter(firewallRequest,firewallResponse);}........privatestaticfinalclassVirtualFilterChainimplementsFilterChain{privatefinalFilterChainoriginalChain;privatefinalList<Filter>additionalFilters;..........@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse)throwsIOException,ServletException{if(this.currentPosition==this.size){if(logger.isDebugEnabled()){logger.debug(LogMessage.of(()->"Secured"+requestLine(this.firewalledRequest)));}//Deactivatepathstrippingasweexitthesecurityfilterchainthis.firewalledRequest.reset();this.originalChain.doFilter(request,response);return;}this.currentPosition++;FilternextFilter=this.additionalFilters.get(this.currentPosition-1);if(logger.isTraceEnabled()){logger.trace(LogMessage.format("Invoking%s(%d/%d)",nextFilter.getClass().getSimpleName(),this.currentPosition,this.size));}nextFilter.doFilter(request,response,this);}}9this.authenticationBuilder.parentAuthenticationManager(authenticationManager);Map<Class<?>,Object>sharedObjects=createSharedObjects();publicfinalclassWebSecurityextendsAbstractConfiguredSecurityBuilder<Filter,WebSecurity>implementsSecurityBuilder<Filter>,ApplicationContextAware{0if(!this.disableDefaults){applyDefaultConfiguration(this.http);ClassLoaderclassLoader=this.context.getClassLoader();List<AbstractHttpConfigurer>defaultHttpConfigurers=SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class,classLoader);for(AbstractHttpConfigurerconfigurer:defaultHttpConfigurers){publicfinalclassWebSecurityextendsAbstractConfiguredSecurityBuilder<Filter,WebSecurity>implementsSecurityBuilder<Filter>,ApplicationContextAware{1}}configure(this.http);returnthis.http;}
其首先会通过applyDefaultConfiguration(this.http)
方法添加一些默认的Filter
:
publicinterfaceSecurityBuilder<O>{Obuild()throwsException;}0
这里的例如http.csrf()
、http.headers()
这些方法,其内部一般是这种方式,也就是需要什么就对应的调用方法例如http.anonymous()
,其就会添加SecurityConfigurer
,再等待configure()
方法的调用,来注入对应SecurityConfigurer.configure
的内容
publicinterfaceSecurityBuilder<O>{Obuild()throwsException;}1
1)、AnonymousConfigurer
例如当前的AnonymousConfigurer
publicinterfaceSecurityBuilder<O>{Obuild()throwsException;}2
其init
方法就会创建一个AnonymousAuthenticationFilter
匿名Filter
,以及一个AnonymousAuthenticationProvider
,匿名认证提供者。然后在configure
方法就会将authenticationFilter
添加到HttpSecurity
中:
publicinterfaceSecurityBuilder<O>{Obuild()throwsException;}3
2)、LogoutConfigurer
然后对于applyDefaultConfiguration
的http.logout()
,其添加的就是LogoutConfigurer
publicinterfaceSecurityBuilder<O>{Obuild()throwsException;}4
这个就构建添加了LogoutFilter
publicinterfaceSecurityBuilder<O>{Obuild()throwsException;}5
当然我们也可以自动拓展来设置我们自己的信息:
publicinterfaceSecurityBuilder<O>{Obuild()throwsException;}6
3)、FilterSecurityInterceptor
当然我们上面是认证相关的内容,但还有授权信息,例如我们自己的拓展授权信息:
publicinterfaceSecurityBuilder<O>{Obuild()throwsException;}7
例如http.authorizeRequests()
,这个是授权具体的哪个请求资源,其添加的就是ExpressionUrlAuthorizationConfigurer
,也就是表达式授权配置:
publicinterfaceSecurityBuilder<O>{Obuild()throwsException;}8
publicinterfaceSecurityBuilder<O>{Obuild()throwsException;}9
其就是通过这些信息来构建FilterSecurityInterceptor
publicabstractclassAbstractConfiguredSecurityBuilder<O,BextendsSecurityBuilder<O>>extendsAbstractSecurityBuilder<O>{privatefinalLoglogger=LogFactory.getLog(getClass());privatefinalLinkedHashMap<Class<?extendsSecurityConfigurer<O,B>>,List<SecurityConfigurer<O,B>>>configurers=newLinkedHashMap<>();privatefinalList<SecurityConfigurer<O,B>>configurersAddedInInitializing=newArrayList<>();privatefinalMap<Class<?>,Object>sharedObjects=newHashMap<>();0
FilterSecurityInterceptor
其是放在整条Filter链的最后一个:
其的处理主要是beforeInvocation(filterInvocation)
方法:
publicabstractclassAbstractConfiguredSecurityBuilder<O,BextendsSecurityBuilder<O>>extendsAbstractSecurityBuilder<O>{privatefinalLoglogger=LogFactory.getLog(getClass());privatefinalLinkedHashMap<Class<?extendsSecurityConfigurer<O,B>>,List<SecurityConfigurer<O,B>>>configurers=newLinkedHashMap<>();privatefinalList<SecurityConfigurer<O,B>>configurersAddedInInitializing=newArrayList<>();privatefinalMap<Class<?>,Object>sharedObjects=newHashMap<>();1
通过authenticateIfRequired
来解析认证,如果有需要的话
publicabstractclassAbstractConfiguredSecurityBuilder<O,BextendsSecurityBuilder<O>>extendsAbstractSecurityBuilder<O>{privatefinalLoglogger=LogFactory.getLog(getClass());privatefinalLinkedHashMap<Class<?extendsSecurityConfigurer<O,B>>,List<SecurityConfigurer<O,B>>>configurers=newLinkedHashMap<>();privatefinalList<SecurityConfigurer<O,B>>configurersAddedInInitializing=newArrayList<>();privatefinalMap<Class<?>,Object>sharedObjects=newHashMap<>();2
可以看到其本身其也是调用的认证管理器:this.authenticationManager.authenticate(authentication)
。
认证成功后就是授权attemptAuthorization(object, attributes, authenticated)
,这个就是你自己配置的一些规则:
publicabstractclassAbstractConfiguredSecurityBuilder<O,BextendsSecurityBuilder<O>>extendsAbstractSecurityBuilder<O>{privatefinalLoglogger=LogFactory.getLog(getClass());privatefinalLinkedHashMap<Class<?extendsSecurityConfigurer<O,B>>,List<SecurityConfigurer<O,B>>>configurers=newLinkedHashMap<>();privatefinalList<SecurityConfigurer<O,B>>configurersAddedInInitializing=newArrayList<>();privatefinalMap<Class<?>,Object>sharedObjects=newHashMap<>();3
其实通过访问决策管理器处理this.accessDecisionManager.decide(authenticated, object, attributes)
,如果没有权限,就抛出AccessDeniedException
。与这个FilterSecurityInterceptor
相关的就是其前面的ExceptionTranslationFilter
这个Filter
是放在FilterSecurityInterceptor
前面的,其是通过默认的applyDefaultConfiguration(HttpSecurity http)
添加的http.exceptionHandling()
:
4)、ExceptionTranslationFilter
publicabstractclassAbstractConfiguredSecurityBuilder<O,BextendsSecurityBuilder<O>>extendsAbstractSecurityBuilder<O>{privatefinalLoglogger=LogFactory.getLog(getClass());privatefinalLinkedHashMap<Class<?extendsSecurityConfigurer<O,B>>,List<SecurityConfigurer<O,B>>>configurers=newLinkedHashMap<>();privatefinalList<SecurityConfigurer<O,B>>configurersAddedInInitializing=newArrayList<>();privatefinalMap<Class<?>,Object>sharedObjects=newHashMap<>();4
publicabstractclassAbstractConfiguredSecurityBuilder<O,BextendsSecurityBuilder<O>>extendsAbstractSecurityBuilder<O>{privatefinalLoglogger=LogFactory.getLog(getClass());privatefinalLinkedHashMap<Class<?extendsSecurityConfigurer<O,B>>,List<SecurityConfigurer<O,B>>>configurers=newLinkedHashMap<>();privatefinalList<SecurityConfigurer<O,B>>configurersAddedInInitializing=newArrayList<>();privatefinalMap<Class<?>,Object>sharedObjects=newHashMap<>();5
它的处理主要是两个,一个是处理AuthenticationException
认证失败,另一个是AccessDeniedException
访问授权失败
publicabstractclassAbstractConfiguredSecurityBuilder<O,BextendsSecurityBuilder<O>>extendsAbstractSecurityBuilder<O>{privatefinalLoglogger=LogFactory.getLog(getClass());privatefinalLinkedHashMap<Class<?extendsSecurityConfigurer<O,B>>,List<SecurityConfigurer<O,B>>>configurers=newLinkedHashMap<>();privatefinalList<SecurityConfigurer<O,B>>configurersAddedInInitializing=newArrayList<>();privatefinalMap<Class<?>,Object>sharedObjects=newHashMap<>();6
publicabstractclassAbstractConfiguredSecurityBuilder<O,BextendsSecurityBuilder<O>>extendsAbstractSecurityBuilder<O>{privatefinalLoglogger=LogFactory.getLog(getClass());privatefinalLinkedHashMap<Class<?extendsSecurityConfigurer<O,B>>,List<SecurityConfigurer<O,B>>>configurers=newLinkedHashMap<>();privatefinalList<SecurityConfigurer<O,B>>configurersAddedInInitializing=newArrayList<>();privatefinalMap<Class<?>,Object>sharedObjects=newHashMap<>();7
通过这两个来处理response
返回内容,如果是认证失败:
publicabstractclassAbstractConfiguredSecurityBuilder<O,BextendsSecurityBuilder<O>>extendsAbstractSecurityBuilder<O>{privatefinalLoglogger=LogFactory.getLog(getClass());privatefinalLinkedHashMap<Class<?extendsSecurityConfigurer<O,B>>,List<SecurityConfigurer<O,B>>>configurers=newLinkedHashMap<>();privatefinalList<SecurityConfigurer<O,B>>configurersAddedInInitializing=newArrayList<>();privatefinalMap<Class<?>,Object>sharedObjects=newHashMap<>();8
publicabstractclassAbstractConfiguredSecurityBuilder<O,BextendsSecurityBuilder<O>>extendsAbstractSecurityBuilder<O>{privatefinalLoglogger=LogFactory.getLog(getClass());privatefinalLinkedHashMap<Class<?extendsSecurityConfigurer<O,B>>,List<SecurityConfigurer<O,B>>>configurers=newLinkedHashMap<>();privatefinalList<SecurityConfigurer<O,B>>configurersAddedInInitializing=newArrayList<>();privatefinalMap<Class<?>,Object>sharedObjects=newHashMap<>();9
通过authenticationEntryPoint
来处理,我们以其实现Http403ForbiddenEntryPoint
为例:
privateMap<Class<?>,Object>createSharedObjects(){Map<Class<?>,Object>sharedObjects=newHashMap<>();sharedObjects.putAll(this.localConfigureAuthenticationBldr.getSharedObjects());sharedObjects.put(UserDetailsService.class,userDetailsService());sharedObjects.put(ApplicationContext.class,this.context);sharedObjects.put(ContentNegotiationStrategy.class,this.contentNegotiationStrategy);sharedObjects.put(AuthenticationTrustResolver.class,this.trustResolver);returnsharedObjects;}0
其就是对response
输出403
,访问拒绝。
如果是授权失败,其有两种如果当前Authentication
是匿名认证,就调用sendStartAuthentication
,也就是与前面的handleAuthenticationException
一样:
privateMap<Class<?>,Object>createSharedObjects(){Map<Class<?>,Object>sharedObjects=newHashMap<>();sharedObjects.putAll(this.localConfigureAuthenticationBldr.getSharedObjects());sharedObjects.put(UserDetailsService.class,userDetailsService());sharedObjects.put(ApplicationContext.class,this.context);sharedObjects.put(ContentNegotiationStrategy.class,this.contentNegotiationStrategy);sharedObjects.put(AuthenticationTrustResolver.class,this.trustResolver);returnsharedObjects;}1
如果不是匿名,则是通过AccessDeniedHandler
访问拒绝处理其来处理,例如AccessDeniedHandlerImpl
:
privateMap<Class<?>,Object>createSharedObjects(){Map<Class<?>,Object>sharedObjects=newHashMap<>();sharedObjects.putAll(this.localConfigureAuthenticationBldr.getSharedObjects());sharedObjects.put(UserDetailsService.class,userDetailsService());sharedObjects.put(ApplicationContext.class,this.context);sharedObjects.put(ContentNegotiationStrategy.class,this.contentNegotiationStrategy);sharedObjects.put(AuthenticationTrustResolver.class,this.trustResolver);returnsharedObjects;}2
也就是如果没有设置指定的errorPage
,就直接返回text
:HttpStatus.FORBIDDEN
。
privateMap<Class<?>,Object>createSharedObjects(){Map<Class<?>,Object>sharedObjects=newHashMap<>();sharedObjects.putAll(this.localConfigureAuthenticationBldr.getSharedObjects());sharedObjects.put(UserDetailsService.class,userDetailsService());sharedObjects.put(ApplicationContext.class,this.context);sharedObjects.put(ContentNegotiationStrategy.class,this.contentNegotiationStrategy);sharedObjects.put(AuthenticationTrustResolver.class,this.trustResolver);returnsharedObjects;}3
如果有页面,就forword到errorPage
。
8、filter的顺序
同时我们前面只介绍了Filter
的添加http.addFilter
,当这里还有添加顺序:
publicinterfaceSecurityBuilder<O>{Obuild()throwsException;}3
privateMap<Class<?>,Object>createSharedObjects(){Map<Class<?>,Object>sharedObjects=newHashMap<>();sharedObjects.putAll(this.localConfigureAuthenticationBldr.getSharedObjects());sharedObjects.put(UserDetailsService.class,userDetailsService());sharedObjects.put(ApplicationContext.class,this.context);sharedObjects.put(ContentNegotiationStrategy.class,this.contentNegotiationStrategy);sharedObjects.put(AuthenticationTrustResolver.class,this.trustResolver);returnsharedObjects;}5
我们在添加一个Filter
的时候,还需要指定整个Filter
的位置
privateMap<Class<?>,Object>createSharedObjects(){Map<Class<?>,Object>sharedObjects=newHashMap<>();sharedObjects.putAll(this.localConfigureAuthenticationBldr.getSharedObjects());sharedObjects.put(UserDetailsService.class,userDetailsService());sharedObjects.put(ApplicationContext.class,this.context);sharedObjects.put(ContentNegotiationStrategy.class,this.contentNegotiationStrategy);sharedObjects.put(AuthenticationTrustResolver.class,this.trustResolver);returnsharedObjects;}6
当前Security
自带添加是使用addFilter(Filter filter)
方法,在添加时实现会在filterOrders
获取其的顺序来添加,其的初始化是:
privateMap<Class<?>,Object>createSharedObjects(){Map<Class<?>,Object>sharedObjects=newHashMap<>();sharedObjects.putAll(this.localConfigureAuthenticationBldr.getSharedObjects());sharedObjects.put(UserDetailsService.class,userDetailsService());sharedObjects.put(ApplicationContext.class,this.context);sharedObjects.put(ContentNegotiationStrategy.class,this.contentNegotiationStrategy);sharedObjects.put(AuthenticationTrustResolver.class,this.trustResolver);returnsharedObjects;}7
这里初始化的时候就已经定义好顺序了
privateMap<Class<?>,Object>createSharedObjects(){Map<Class<?>,Object>sharedObjects=newHashMap<>();sharedObjects.putAll(this.localConfigureAuthenticationBldr.getSharedObjects());sharedObjects.put(UserDetailsService.class,userDetailsService());sharedObjects.put(ApplicationContext.class,this.context);sharedObjects.put(ContentNegotiationStrategy.class,this.contentNegotiationStrategy);sharedObjects.put(AuthenticationTrustResolver.class,this.trustResolver);returnsharedObjects;}8
三、SpringSecurity对FilterChain的组装构建
1、添加HttpSecurity到WebSecurity
privateMap<Class<?>,Object>createSharedObjects(){Map<Class<?>,Object>sharedObjects=newHashMap<>();sharedObjects.putAll(this.localConfigureAuthenticationBldr.getSharedObjects());sharedObjects.put(UserDetailsService.class,userDetailsService());sharedObjects.put(ApplicationContext.class,this.context);sharedObjects.put(ContentNegotiationStrategy.class,this.contentNegotiationStrategy);sharedObjects.put(AuthenticationTrustResolver.class,this.trustResolver);returnsharedObjects;}9
public<CextendsSecurityConfigurer<O,B>>Capply(Cconfigurer)throwsException{add(configurer);returnconfigurer;}0
HttpSecurity
构建好后,就讲其添加到WebSecurity
中,继续构建WebSecurity
:
publicfinalclassWebSecurityextendsAbstractConfiguredSecurityBuilder<Filter,WebSecurity>implementsSecurityBuilder<Filter>,ApplicationContextAware{6
2、performBuild完成FilterChainProxy创建
public<CextendsSecurityConfigurer<O,B>>Capply(Cconfigurer)throwsException{add(configurer);returnconfigurer;}2
这里的处理我们可以看到FilterChainProxy
创建会通过(securityFilterChains)
,我们前面添加的HttpSecurity
public<CextendsSecurityConfigurer<O,B>>Capply(Cconfigurer)throwsException{add(configurer);returnconfigurer;}3
3、FilterChainProxy的拦截逻辑
这个逻辑前面贴过了
public<CextendsSecurityConfigurer<O,B>>Capply(Cconfigurer)throwsException{add(configurer);returnconfigurer;}4
我们当前的是SecurityFilterChain
是通过HttpSecurity
构建的,通过doBuilder
:
public<CextendsSecurityConfigurer<O,B>>Capply(Cconfigurer)throwsException{add(configurer);returnconfigurer;}5
也就是将HttpSecurity
以及其的匹配器RequestMatcher
来构建。
public<CextendsSecurityConfigurer<O,B>>Capply(Cconfigurer)throwsException{add(configurer);returnconfigurer;}6
这个匹配器都拦截。
由此就完成了FilterChain
的组装。