- 浏览: 189379 次
- 性别:
- 来自: 南京
文章分类
最新评论
前言:
spring security最常见的应用场景还是基于http请求和servlet API的web应用,接下来的几个章节我们将重点探讨下spring security是如何在web应用中实现认证和访问控制的。通过前面章节的讲述,我们已经了解到,在web应用中spring是通过各种各样的filter来做认证和鉴权,本小节就主要讨论下spring中过滤器的几个主要组件。
1.DelegatingFilterProxy
为了使Filter起作用,我们必须明确的在web.xml中声明,或者通过servlet3.0+中追加的方法ServletContext.addFilter方法追加到servlet中,否则这个filter是会被servlet容器忽略的。在spring security中,我们的filter也是一个spring bean,也需要用到依赖注入功能来装配,而我们知道,正常情况下servlet context的创建是在application context之前的,如果我们直接把我们的filter注册到servlet中,filter中依赖注入的对象将获取不到,为了解决这个问题,spring引入了DelegatingFilterProxy类,他提供了一servlet context和application context之间的一种链接。
在web.xml中使用DelegatingFilterProxy,通常采用下面的配置代码段
相应的java config代码段如下:
可以看到,我们实际上配置的Filter是DelegatingFilterProxy,并不是我们实际实现了Filter接口的类,当有请求满足DelegatingFilterProxy的UriPatterns时,DelegatingFilterProxy类会首先判断实际的Filter类是否已经配置过,如果没有将根据配置的bean名称从spring context中获取对应的bean,然后将处理委托给我们真实的Filter类。这样我们真实的Filter类就可以利用spring bean生命周期方法以及灵活的依赖注入配置,需要注意的是我们的Filter类注入到spring context中的bean名称必须和传入到DelegatingFilterProxy中的名字一致。DelegatingFilterProxy中处理请求的代码段如下:
2.FilterChainProxy
在spring security我们通过DelegatingFilterProxy注册到servlet context中并不是一个个单独的filter,而是FilterChainProxy,一个FilterChainProxy就是一系列过滤器链的代理,里面包含很多个的过滤器链(过滤器链在spring中的统一接口是SecurityFilterChain,每一个SecurityFilterChain中都包含一个排好序的过滤器列表),当FilterChainProxy接受到DelegatingFilterProxy委托过来的请求时,就从这些过滤器链中查找到第一个满足当前请求的过滤器链,并获取这个过滤器链中的过滤器列表,然后依次执行获取到的过滤器列表。虽然理论上我们是能够通过DelegatingFilterProxy将我们需要的filter一个个都配置到web.xml或者通过servletContext.addFilter方法追加到servlet中,但是随着我们filter的追加这种方式会变的难以维护,并且这样一来filter的执行顺序将严格依赖我们追加的顺序,缺少灵活性。下面是一个例子
在FilterChainProxy中的多个过滤器链中,我们是用第一个匹配当前请求的过滤器链对请求进行filter操作,所以越具体的URI应配置的越靠前
3.下面我们来看看在spring security中是如何把我们需要的filter注册到servlet中的
3.1首先是WebSecurity类的performBuild方法,
这个方法会把我们追加到ignoredRequests(通过ignoring()方法配置)和配置好的securityFilterChainBuilders(实际就是HttpSecurity)分别配置成SecurityFilterChain,代码片段如下:
3.2配置好的FilterChainProxy返回后会在WebSecurityConfiguration中被注册到spring context,bean名称为springSecurityFilterChain
如果我们有继承WebSecurityConfigurerAdapter的配置类,就会用我们自己的。
AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME的具体值就是springSecurityFilterChain
3.3 通过DelegatingFilterProxy把名称为springSecurityFilterChain的bean注册到servlet中,在spring boot工程中这个是通过SecurityFilterAutoConfiguration这个自动注册类来实现的
可以看到,当我们的context中存在名称为springSecurityFilterChain的bean后,spring boot将自动为我们注册一个DelegatingFilterProxyRegistrationBean,并将springSecurityFilterChain传递进去,DelegatingFilterProxyRegistrationBean这个bean的继承关系为
DelegatingFilterProxyRegistrationBean ->AbstractFilterRegistrationBean->RegistrationBean->ServletContextInitializer,所以在servlet启动时会出发onStartup方法,在这个方法内部,具体实现在AbstractFilterRegistrationBean类中
可以看到,这里会获取一个filter并注册到servletContext中,具体的getFilter方法在DelegatingFilterProxyRegistrationBean中
很明显,是用了一个DelegatingFilterProxy将我们传入的bean名称进行了包装。至此spring security中用到的filter就被以DelegatingFilterProxy的形式注册到了servlet。
具体我们想用的filter如 UsernamePasswordAuthenticationFilter、SecurityContextPersistenceFilter等是如何追加到HttpSecurity中,从而在3.1步骤中被加入到SecurityFilterChain中的,我们在具体讨论每一个filter时再继续讲解
4.绕过filter
在实际应用开发中,对于某些资源的访问,我们可能不想使用spring security进行保护,例如一些静态的js、html等,下面分别给出用xml和java config两种配置形式实现这个功能的例子
xml配置中,我们可以将对应URI的filter设置成none,如下
对应的java config
通过3.1小节,我们知道通过web.ignoring()配置的过滤规则,会被配置成DefaultSecurityFilterChain追加到过滤器链的列表的最前面,并且在配置DefaultSecurityFilterChain时只传入了RequestMatcher,没有传入任何filter,DefaultSecurityFilterChain构造函数如下,
表示对于满足条件的uri不适用任何过滤器来处理,和xml中security="none"效果一样。
另外需要注意的一点是,采用这种配置后,任何满足条件的URI将不被spring security的过滤器处理,此时SecurityContext中将不包含任何用户信息。
spring security最常见的应用场景还是基于http请求和servlet API的web应用,接下来的几个章节我们将重点探讨下spring security是如何在web应用中实现认证和访问控制的。通过前面章节的讲述,我们已经了解到,在web应用中spring是通过各种各样的filter来做认证和鉴权,本小节就主要讨论下spring中过滤器的几个主要组件。
1.DelegatingFilterProxy
为了使Filter起作用,我们必须明确的在web.xml中声明,或者通过servlet3.0+中追加的方法ServletContext.addFilter方法追加到servlet中,否则这个filter是会被servlet容器忽略的。在spring security中,我们的filter也是一个spring bean,也需要用到依赖注入功能来装配,而我们知道,正常情况下servlet context的创建是在application context之前的,如果我们直接把我们的filter注册到servlet中,filter中依赖注入的对象将获取不到,为了解决这个问题,spring引入了DelegatingFilterProxy类,他提供了一servlet context和application context之间的一种链接。
在web.xml中使用DelegatingFilterProxy,通常采用下面的配置代码段
<filter> <filter-name>myFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>myFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
相应的java config代码段如下:
Filter filter = new DelegatingFilterProxy("myFilter"); servletContext.addFilter("myFilter", filter) .addMappingForUrlPatterns( EnumSet.of(DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.REQUEST), false, "/*");
可以看到,我们实际上配置的Filter是DelegatingFilterProxy,并不是我们实际实现了Filter接口的类,当有请求满足DelegatingFilterProxy的UriPatterns时,DelegatingFilterProxy类会首先判断实际的Filter类是否已经配置过,如果没有将根据配置的bean名称从spring context中获取对应的bean,然后将处理委托给我们真实的Filter类。这样我们真实的Filter类就可以利用spring bean生命周期方法以及灵活的依赖注入配置,需要注意的是我们的Filter类注入到spring context中的bean名称必须和传入到DelegatingFilterProxy中的名字一致。DelegatingFilterProxy中处理请求的代码段如下:
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { // Lazily initialize the delegate if necessary. Filter delegateToUse = this.delegate; if (delegateToUse == null) { synchronized (this.delegateMonitor) { if (this.delegate == null) { WebApplicationContext wac = findWebApplicationContext(); if (wac == null) { throw new IllegalStateException("No WebApplicationContext found: " + "no ContextLoaderListener or DispatcherServlet registered?"); } this.delegate = initDelegate(wac); } delegateToUse = this.delegate; } } // Let the delegate perform the actual doFilter operation. invokeDelegate(delegateToUse, request, response, filterChain); } ...... protected void invokeDelegate( Filter delegate, ServletRequest request, ServletResponse response,FilterChain filterChain) throws ServletException, IOException { delegate.doFilter(request, response, filterChain); }
2.FilterChainProxy
在spring security我们通过DelegatingFilterProxy注册到servlet context中并不是一个个单独的filter,而是FilterChainProxy,一个FilterChainProxy就是一系列过滤器链的代理,里面包含很多个的过滤器链(过滤器链在spring中的统一接口是SecurityFilterChain,每一个SecurityFilterChain中都包含一个排好序的过滤器列表),当FilterChainProxy接受到DelegatingFilterProxy委托过来的请求时,就从这些过滤器链中查找到第一个满足当前请求的过滤器链,并获取这个过滤器链中的过滤器列表,然后依次执行获取到的过滤器列表。虽然理论上我们是能够通过DelegatingFilterProxy将我们需要的filter一个个都配置到web.xml或者通过servletContext.addFilter方法追加到servlet中,但是随着我们filter的追加这种方式会变的难以维护,并且这样一来filter的执行顺序将严格依赖我们追加的顺序,缺少灵活性。下面是一个例子
<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy"> <constructor-arg> <list> <sec:filter-chain pattern="/restful/**" filters=" securityContextPersistenceFilterWithASCFalse, basicAuthenticationFilter, exceptionTranslationFilter, filterSecurityInterceptor" /> <sec:filter-chain pattern="/**" filters=" securityContextPersistenceFilterWithASCTrue, formLoginFilter, exceptionTranslationFilter, filterSecurityInterceptor" /> </list> </constructor-arg> </bean>
在FilterChainProxy中的多个过滤器链中,我们是用第一个匹配当前请求的过滤器链对请求进行filter操作,所以越具体的URI应配置的越靠前
3.下面我们来看看在spring security中是如何把我们需要的filter注册到servlet中的
3.1首先是WebSecurity类的performBuild方法,
这个方法会把我们追加到ignoredRequests(通过ignoring()方法配置)和配置好的securityFilterChainBuilders(实际就是HttpSecurity)分别配置成SecurityFilterChain,代码片段如下:
@Override protected Filter performBuild() throws Exception { ... 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); ...
3.2配置好的FilterChainProxy返回后会在WebSecurityConfiguration中被注册到spring context,bean名称为springSecurityFilterChain
/** * Creates the Spring Security Filter Chain * @return * @throws Exception */ @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(); }
如果我们有继承WebSecurityConfigurerAdapter的配置类,就会用我们自己的。
AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME的具体值就是springSecurityFilterChain
public abstract class AbstractSecurityWebApplicationInitializer implements WebApplicationInitializer { ... public static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain"; ...
3.3 通过DelegatingFilterProxy把名称为springSecurityFilterChain的bean注册到servlet中,在spring boot工程中这个是通过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.getFilterOrder()); registration.setDispatcherTypes(getDispatcherTypes(securityProperties)); return registration; }
可以看到,当我们的context中存在名称为springSecurityFilterChain的bean后,spring boot将自动为我们注册一个DelegatingFilterProxyRegistrationBean,并将springSecurityFilterChain传递进去,DelegatingFilterProxyRegistrationBean这个bean的继承关系为
DelegatingFilterProxyRegistrationBean ->AbstractFilterRegistrationBean->RegistrationBean->ServletContextInitializer,所以在servlet启动时会出发onStartup方法,在这个方法内部,具体实现在AbstractFilterRegistrationBean类中
@Override public void onStartup(ServletContext servletContext) throws ServletException { Filter filter = getFilter(); Assert.notNull(filter, "Filter must not be null"); String name = getOrDeduceName(filter); if (!isEnabled()) { this.logger.info("Filter " + name + " was not registered (disabled)"); return; } FilterRegistration.Dynamic added = servletContext.addFilter(name, filter); if (added == null) { this.logger.info("Filter " + name + " was not registered " + "(possibly already registered?)"); return; } configure(added); }
可以看到,这里会获取一个filter并注册到servletContext中,具体的getFilter方法在DelegatingFilterProxyRegistrationBean中
@Override public Filter getFilter() { return new DelegatingFilterProxy(this.targetBeanName, getWebApplicationContext()) { @Override protected void initFilterBean() throws ServletException { // Don't initialize filter bean on init() } }; }
很明显,是用了一个DelegatingFilterProxy将我们传入的bean名称进行了包装。至此spring security中用到的filter就被以DelegatingFilterProxy的形式注册到了servlet。
具体我们想用的filter如 UsernamePasswordAuthenticationFilter、SecurityContextPersistenceFilter等是如何追加到HttpSecurity中,从而在3.1步骤中被加入到SecurityFilterChain中的,我们在具体讨论每一个filter时再继续讲解
4.绕过filter
在实际应用开发中,对于某些资源的访问,我们可能不想使用spring security进行保护,例如一些静态的js、html等,下面分别给出用xml和java config两种配置形式实现这个功能的例子
xml配置中,我们可以将对应URI的filter设置成none,如下
<http pattern="/static/**" security="none"/> <http pattern="/resources/**" security="none"/>
对应的java config
@Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/resources/**", "/static/**"); }
通过3.1小节,我们知道通过web.ignoring()配置的过滤规则,会被配置成DefaultSecurityFilterChain追加到过滤器链的列表的最前面,并且在配置DefaultSecurityFilterChain时只传入了RequestMatcher,没有传入任何filter,DefaultSecurityFilterChain构造函数如下,
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) { this(requestMatcher, Arrays.asList(filters)); }
表示对于满足条件的uri不适用任何过滤器来处理,和xml中security="none"效果一样。
另外需要注意的一点是,采用这种配置后,任何满足条件的URI将不被spring security的过滤器处理,此时SecurityContext中将不包含任何用户信息。
发表评论
-
spring-security(二十五)鉴权
2018-03-27 11:21 1550前言 本文将详细探讨spring security中的鉴权 ... -
test
2018-03-19 21:20 0什么是服务发现 ... -
spring-security(二十四)CSRF
2018-03-24 09:54 81281.什么是CSRF攻击 下面我们以一个具体的例子来说明这种常见 ... -
spring-security(二十三)Remember-Me认证
2018-03-09 21:06 1287前言: Remember-me认证方式指的是能在不同的会话 ... -
spring-security(二十二)基本认证和摘要认证
2018-03-06 16:58 1763前言: 在web应用中,非常流行以基本认证和摘要认证作为备 ... -
spring-security(二十一)核心Filter-UsernamePasswordAuthenticationFilter
2018-03-04 11:05 1199一、UsernamePasswordAuthenticatio ... -
spring-security(二十)核心Filter-SecurityContextPersistenceFilter
2018-02-28 21:40 1065一、SecurityContextPersistenceFil ... -
spring-security(十九)核心Filter-ExceptionTranslationFilter
2018-02-27 16:31 1993前言: 在spring的安全 ... -
spring-security(十八)核心Filter-FilterSecurityInterceptor
2018-02-25 10:59 2266前言: 当用spring secur ... -
spring-security(二十六)整合CAS 实现SSO
2018-04-05 18:09 12071.cas 认证流程 2.spring security ... -
spring-security(十七)Filter顺序及简介
2018-02-22 18:16 7801前言: spring security在 ... -
spring-security(十五) Password编码
2018-02-19 15:15 1181前言: 在实际应用中 ... -
spring-security(十四)UserDetailsService
2018-02-19 11:35 1436前言: 作为spring security的核心类,大多数 ... -
spring-security(十三)核心服务类
2018-02-18 16:46 1369前言: 在之前的篇章中我们已经讲述了spring secu ... -
spring-security(十二)鉴权方式概述
2018-02-18 10:42 2493前言: 本文主要讲述在spring security鉴权的 ... -
spring-security(十一)Web应用认证过程
2018-02-17 17:17 1210前言: 本文将探讨当 ... -
spring-security(十)基本认证过程
2018-02-17 13:55 2058前言: 在spring security中认证具体指的是什 ... -
spring-security(九)-核心组件
2018-02-17 10:46 827前言: 本文主要介绍在spring security中的几 ... -
spring-security(八)java config-sample之cas client
2018-02-16 11:26 2031前言: SSO英文全称Single Sign On,单点登 ... -
spring-security(七)java config-sample之concurrency
2018-02-15 10:51 633前言: 在实际应用中,我们可能会限制一个用户同时登录同一个 ...
相关推荐
赠送jar包:spring-security-crypto-5.5.2.jar; 赠送原API文档:spring-security-crypto-5.5.2-javadoc.jar; 赠送源代码:spring-security-crypto-5.5.2-sources.jar; 赠送Maven依赖信息文件:spring-security-...
赠送jar包:spring-security-oauth2-2.3.5.RELEASE.jar; 赠送原API文档:spring-security-oauth2-2.3.5.RELEASE-javadoc.jar; 赠送源代码:spring-security-oauth2-2.3.5.RELEASE-sources.jar; 赠送Maven依赖信息...
赠送jar包:spring-security-core-5.3.9.RELEASE.jar; 赠送原API文档:spring-security-core-5.3.9.RELEASE-javadoc.jar; 赠送源代码:spring-security-core-5.3.9.RELEASE-sources.jar; 赠送Maven依赖信息文件:...
赠送jar包:spring-security-jwt-1.0.10.RELEASE.jar; 赠送原API文档:spring-security-jwt-1.0.10.RELEASE-javadoc.jar; 赠送源代码:spring-security-jwt-1.0.10.RELEASE-sources.jar; 赠送Maven依赖信息文件:...
赠送jar包:spring-security-crypto-5.6.1.jar; 赠送原API文档:spring-security-crypto-5.6.1-javadoc.jar; 赠送源代码:spring-security-crypto-5.6.1-sources.jar; 赠送Maven依赖信息文件:spring-security-...
赠送jar包:spring-security-core-5.2.0.RELEASE.jar; 赠送原API文档:spring-security-core-5.2.0.RELEASE-javadoc.jar; 赠送源代码:spring-security-core-5.2.0.RELEASE-sources.jar; 赠送Maven依赖信息文件:...
赠送jar包:spring-security-rsa-1.0.10.RELEASE.jar; 赠送原API文档:spring-security-rsa-1.0.10.RELEASE-javadoc.jar; 赠送源代码:spring-security-rsa-1.0.10.RELEASE-sources.jar; 赠送Maven依赖信息文件:...
赠送jar包:spring-security-web-5.2.0.RELEASE.jar; 赠送原API文档:spring-security-web-5.2.0.RELEASE-javadoc.jar; 赠送源代码:spring-security-web-5.2.0.RELEASE-sources.jar; 赠送Maven依赖信息文件:...
赠送jar包:spring-security-core-5.5.2.jar; 赠送原API文档:spring-security-core-5.5.2-javadoc.jar; 赠送源代码:spring-security-core-5.5.2-sources.jar; 赠送Maven依赖信息文件:spring-security-core-...
赠送jar包:spring-security-web-5.6.1.jar; 赠送原API文档:spring-security-web-5.6.1-javadoc.jar; 赠送源代码:spring-security-web-5.6.1-sources.jar; 赠送Maven依赖信息文件:spring-security-web-5.6.1....
springSecurity spring-security jar 开发所需jar包
赠送jar包:spring-security-oauth2-autoconfigure-2.1.8.RELEASE.jar; 赠送原API文档:spring-security-oauth2-autoconfigure-2.1.8.RELEASE-javadoc.jar; 赠送源代码:spring-security-oauth2-autoconfigure-...
赠送jar包:spring-security-config-5.0.7.RELEASE.jar; 赠送原API文档:spring-security-config-5.0.7.RELEASE-javadoc.jar; 赠送源代码:spring-security-config-5.0.7.RELEASE-sources.jar; 赠送Maven依赖信息...
赠送jar包:spring-security-core-5.5.2.jar; 赠送原API文档:spring-security-core-5.5.2-javadoc.jar; 赠送源代码:spring-security-core-5.5.2-sources.jar; 赠送Maven依赖信息文件:spring-security-core-...
赠送jar包:spring-security-rsa-1.0.10.RELEASE.jar; 赠送原API文档:spring-security-rsa-1.0.10.RELEASE-javadoc.jar; 赠送源代码:spring-security-rsa-1.0.10.RELEASE-sources.jar; 赠送Maven依赖信息文件:...
赠送jar包:spring-security-core-5.6.1.jar; 赠送原API文档:spring-security-core-5.6.1-javadoc.jar; 赠送源代码:spring-security-core-5.6.1-sources.jar; 赠送Maven依赖信息文件:spring-security-core-...
赠送jar包:spring-security-jwt-1.0.10.RELEASE.jar; 赠送原API文档:spring-security-jwt-1.0.10.RELEASE-javadoc.jar; 赠送源代码:spring-security-jwt-1.0.10.RELEASE-sources.jar; 赠送Maven依赖信息文件:...
Spring-security4.2官方整合jar包,从官网下载整合出来的,其中还有demo示例与文档,包括了spring-security-core-4.2.2.RELEASE.jar,spring-security-web-4.2.2.RELEASE.jar等jar包。不想到官网找就可以在这里直接...
赠送jar包:spring-security-core-5.6.1.jar; 赠送原API文档:spring-security-core-5.6.1-javadoc.jar; 赠送源代码:spring-security-core-5.6.1-sources.jar; 赠送Maven依赖信息文件:spring-security-core-...
赠送jar包:spring-security-oauth2-autoconfigure-2.1.8.RELEASE.jar; 赠送原API文档:spring-security-oauth2-autoconfigure-2.1.8.RELEASE-javadoc.jar; 赠送源代码:spring-security-oauth2-autoconfigure-...