- 浏览: 194210 次
- 性别:
- 来自: 南京
文章分类
最新评论
前言:
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 1602前言 本文将详细探讨spring security中的鉴权 ... -
test
2018-03-19 21:20 0什么是服务发现 ... -
spring-security(二十四)CSRF
2018-03-24 09:54 81661.什么是CSRF攻击 下面我们以一个具体的例子来说明这种常见 ... -
spring-security(二十三)Remember-Me认证
2018-03-09 21:06 1343前言: Remember-me认证方式指的是能在不同的会话 ... -
spring-security(二十二)基本认证和摘要认证
2018-03-06 16:58 1806前言: 在web应用中,非常流行以基本认证和摘要认证作为备 ... -
spring-security(二十一)核心Filter-UsernamePasswordAuthenticationFilter
2018-03-04 11:05 1242一、UsernamePasswordAuthenticatio ... -
spring-security(二十)核心Filter-SecurityContextPersistenceFilter
2018-02-28 21:40 1133一、SecurityContextPersistenceFil ... -
spring-security(十九)核心Filter-ExceptionTranslationFilter
2018-02-27 16:31 2065前言: 在spring的安全 ... -
spring-security(十八)核心Filter-FilterSecurityInterceptor
2018-02-25 10:59 2351前言: 当用spring secur ... -
spring-security(二十六)整合CAS 实现SSO
2018-04-05 18:09 12471.cas 认证流程 2.spring security ... -
spring-security(十七)Filter顺序及简介
2018-02-22 18:16 7908前言: spring security在 ... -
spring-security(十五) Password编码
2018-02-19 15:15 1229前言: 在实际应用中 ... -
spring-security(十四)UserDetailsService
2018-02-19 11:35 1477前言: 作为spring security的核心类,大多数 ... -
spring-security(十三)核心服务类
2018-02-18 16:46 1420前言: 在之前的篇章中我们已经讲述了spring secu ... -
spring-security(十二)鉴权方式概述
2018-02-18 10:42 2529前言: 本文主要讲述在spring security鉴权的 ... -
spring-security(十一)Web应用认证过程
2018-02-17 17:17 1240前言: 本文将探讨当 ... -
spring-security(十)基本认证过程
2018-02-17 13:55 2109前言: 在spring security中认证具体指的是什 ... -
spring-security(九)-核心组件
2018-02-17 10:46 871前言: 本文主要介绍在spring security中的几 ... -
spring-security(八)java config-sample之cas client
2018-02-16 11:26 2073前言: SSO英文全称Single Sign On,单点登 ... -
spring-security(七)java config-sample之concurrency
2018-02-15 10:51 677前言: 在实际应用中,我们可能会限制一个用户同时登录同一个 ...
相关推荐
Spring Security是Java领域中广泛应用的安全框架,用于保护Web应用程序免受各种安全威胁。OAuth2则是一种授权协议,常用于提供安全的第三方应用访问资源的权限。在这个源码分析中,我们将深入探讨`spring-security-...
Spring Security 是一个强大的和高度可定制的身份验证和访问控制框架,用于Java应用程序。...虽然现在已经有更新的版本,但对于学习和理解Spring Security的基本概念和工作原理,这个版本仍然是一个很好的起点。
通过学习这个版本,我们可以了解Spring Security的基本架构和工作原理,为理解和使用更高级的版本奠定基础。同时,这个版本的API文档和JAR包对于修复旧系统中的安全问题或研究历史实现也具有参考价值。
对于初学者来说,这是了解Spring Security工作原理和开始项目开发的好资料。 《spring-security3.0.rar》是一个包含Spring Security 3.0版本库的压缩文件,用户可以将其解压到项目的lib目录下,以便在项目中引用。...
在实际应用中,Spring Security还提供了过滤器链(Filter Chain)的概念,这是实现Web安全的关键。每个过滤器都有特定的职责,例如,`HttpSessionAuthenticationStrategy`处理会话相关的认证,而`...
要深入了解Spring Security 3.0.5.RELEASE,你可以查看官方文档,了解每个组件的工作原理和配置选项,同时研究压缩包中的源代码和示例,这将有助于你在实际项目中更有效地应用这一强大的安全框架。
Spring Security 是一个强大的和高度...虽然这个版本相对较旧,但它的基本原理和架构在后续版本中仍然适用,学习它有助于理解Spring Security的核心思想。在实际项目中,通常会使用最新版本以获得更好的安全性和功能。
本篇文章将深入探讨Spring Security的核心特性、工作原理以及在实际项目中的应用。 一、Spring Security概述 Spring Security是Spring生态系统的组成部分,主要负责应用程序的安全性,包括身份验证、授权和访问...
本文将深入探讨Spring Security的核心模块,并以2.0.6.RELEASE版本为基准,解析其主要组件和工作原理。 一、概述 Spring Security 2.0.6.RELEASE是该框架的一个稳定版本,它提供了强大的安全特性,包括基于角色的...
1. **过滤器链**:Spring Security通过一系列预定义的Filter来拦截HTTP请求,执行身份验证和授权检查。 2. **配置**:通常通过XML或Java配置来定制安全行为,例如设置认证提供者、定义访问规则等。 3. **认证**:...
Spring Security 是一个强大的和高度可定制的身份验证和访问控制框架,用于Java应用程序。它为Web应用程序和企业级...通过分析这些文件,你可以更好地理解Spring Security的工作原理,并学习如何在实际项目中应用它。
下面将详细阐述Spring Security的核心概念、配置步骤以及实现原理。 1. **核心概念** - **Authentication(认证)**: 认证是验证用户身份的过程,Spring Security提供了多种认证方式,如用户名/密码、API密钥等。 ...
Spring Security 是一个强大的且高度可定制的身份验证和访问控制框架,用于保护Java应用程序。...通过分析和运行这个示例,你可以更深入地理解Spring Security的架构和工作原理,为实际项目开发打下坚实的基础。
这个示例对于学习和理解Spring Security的工作原理以及如何在实际开发中应用它非常有帮助。通过分析源代码和运行示例,你可以更好地掌握Spring Security的精髓,并将其应用于自己的项目中,提升Web应用的安全性。
8. **Filter Chain**:Spring Security通过一系列过滤器处理请求,每个过滤器执行特定的安全任务,如认证、授权等。 9. **国际化的错误消息**:Spring Security支持多语言错误消息,方便不同地区的用户理解。 10. ...
通过这份文档,读者可以深入理解Spring Security的工作原理,掌握如何构建安全的Spring应用程序,并了解如何根据具体需求定制和扩展框架。这份高清且带有书签的文档,使得学习过程更加高效,无疑是对Spring Security...
在这个源码包`spring-security-3.0.5.RELEASE`中,我们可以深入理解Spring Security的核心机制及其工作原理。 首先,Spring Security的核心组件包括以下几个部分: 1. **Authentication(认证)**:负责用户身份的...
《深入剖析Spring Security Web 3源码...总结,通过对`spring-security-web-3`源码的分析,开发者不仅可以掌握Spring Security的内在工作原理,还能提升解决实际安全问题的能力,为构建更安全的Web应用提供坚实的基础。
通过分析这些源码,我们可以深入理解OAuth的工作原理,以及Spring Security OAuth如何支持这些功能。同时,Sparklr2和Tonr2示例能帮助我们直观地看到OAuth流程的每个步骤,包括授权、令牌交换和资源访问。 在实际...
通过这个示例,开发者可以理解Spring Security的基本工作原理,并能将其应用到自己的项目中,提供强大的安全保护。同时,这个例子也是一个很好的学习资源,帮助开发者掌握如何配置、定制和扩展Spring Security以满足...