在源码分析之前补充了一些知识,详见上三篇文章的转载,我感觉比较有用。

下面是servlet的加载顺序。

web.xml 中 对象的加载顺序为:先 listener >> filter >> servlet >> spring

 

在spring security demo例子中,所有的配置文件有几个:

1、web.xml 这个是web项目的标准配置文件。

2、applicationContext-security.xml 这个是和spring security相关的配置文件

3、applicationContext-business.xml 这个是和spring security相关的配置文件

4、logback.xml 这个是和日志相关的配置文件

5、bank-servlet.xml 这个是spring mvc相关的配置文件。

 

首先分析web.xml配置文件。原文就不在这里贴出来了,只对重点内容进行分析。

spring security 过滤器配置为:

<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

后研究了源码,核心代码如下:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {

// Lazily initialize the delegate if necessary.
Filter delegateToUse = null;
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener 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);
}

这里面用到了WebApplicationContext,这也是我上篇转载WebApplicationContext 介绍的原因。

 

WebApplicationContext 在web.xml的配置为:

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
 

通过WebApplicationContext加载applicationContext-security.xml、applicationContext-business.xml 两个配置文件得到spring security的所有配置。

 

从这两段代码中可以看到,spring security的权限核心就是过滤器,他将 HTTP 请求委托给 Spring 应用程序上下文中的一个 Bean . 被委托的 Bean 实现了 javax.servlet.Fitler 接口, 但它需要受 Spring IOC 容器管理, 而不是直接在 web.xml 中配置*.默认情况下, DelegatingFilterProxy 会把 HTTP 请求委托给和它的 <filter-name> 属性相同的 Bean 上(也可以在 targetBeanName 初始参数中覆盖该 Bean 的名字).

SpringSecurity 在 web 服务器加载当前 web 应用时配置一个名称为 springSecurityFilterChain 的过滤器链(SpringSecurity 即通过该过滤器链为 web 应用提供安全服务), 所以 <filter-name> 应该使用这个名字.

Spring Security的web架构是完全基于标准的servlet过滤器的。它没有在内部使用servlet或任何其他基于servlet的框架(比如spring mvc),所以它没有与任何特定的web技术强行关联。 它只管处理HttpServletRequest 和HttpServletResponse,不关心请求时来自浏览器,web服务客户端,HttpInvoker还是一个AJAX应用。

Spring Security维护了一个过滤器链,每个过滤器拥有特定的功能,过滤器需要服务也会对应添加和删除。过滤器的次序是非常重要的,它们之间都有依赖关系。 如果你已经使用了命名空间配置,过滤器会自动帮你配置, 你不需要定义任何Spring Bean,但是有时候你需要完全控制Spring过滤器链,因为你使用了命名空间没有提供的特性,或者你需要使用你自己自定义的类。

这个过滤器里没有实现过滤器的任何逻辑。 DelegatingFilterProxy做的事情是代理Filter的方法,从application context里获得bean。这让bean可以获得spring web application context的生命周期支持,使配置较为轻便。 bean必须实现javax.servlet.Filter接口,它必须和filter-name里定义的名称是一样的。

 

下表列出了这些安全过滤器的名称作用以及它们在系统中的执行顺序:

 

 

通道处理过滤器

确保请求是在安全通道(HTTP和HTTPS)之上传输的

认证处理过滤器

接受认证请求,并将它们转交给认证管理器进行身份验证

CAS处理过滤器

接受CAS服务票据,验证Yale CAS(单点登录)是否已经对用户进行了认证

HTTP基本授权过滤器

处理使用HTTP基本认证的身份验证请求

集成过滤器

处理认证信息在请求间的存储(比如在HTTP会话中)

安全强制过滤器

确保用户己经认证,并且满足访问一个受保护Web资源的权限需求

 

每个Spring Security过滤器都实现了Spring的Ordered接口, 这些过滤器在初始化的时候先被排好序了。 标准的过滤器在命名空间里都有自己的假名:

spring security 默认的过滤器是放在web命名空间http://www.springframework.org/schema/security中的。

深入到spring-security-3.1.xsd中可以看到如下配置。

<xs:simpleType name="named-security-filter">
<xs:restriction base="xs:token">
<xs:enumeration value="FIRST"/>
<xs:enumeration value="CHANNEL_FILTER"/>
<xs:enumeration value="CONCURRENT_SESSION_FILTER"/>
<xs:enumeration value="SECURITY_CONTEXT_FILTER"/>
<xs:enumeration value="LOGOUT_FILTER"/>
<xs:enumeration value="X509_FILTER"/>
<xs:enumeration value="PRE_AUTH_FILTER"/>
<xs:enumeration value="CAS_FILTER"/>
<xs:enumeration value="FORM_LOGIN_FILTER"/>
<xs:enumeration value="OPENID_FILTER"/>
<xs:enumeration value="BASIC_AUTH_FILTER"/>
<xs:enumeration value="SERVLET_API_SUPPORT_FILTER"/>
<xs:enumeration value="REMEMBER_ME_FILTER"/>
<xs:enumeration value="ANONYMOUS_FILTER"/>
<xs:enumeration value="EXCEPTION_TRANSLATION_FILTER"/>
<xs:enumeration value="SESSION_MANAGEMENT_FILTER"/>
<xs:enumeration value="FILTER_SECURITY_INTERCEPTOR"/>
<xs:enumeration value="SWITCH_USER_FILTER"/>
<xs:enumeration value="LAST"/>
</xs:restriction>
</xs:simpleType>

标准过滤器别名和顺序 Java代码

Alias Filter Class
CHANNEL_FILTER ChannelProcessingFilter
CONCURRENT_SESSION_FILTER ConcurrentSessionFilter
SESSION_CONTEXT_INTEGRATION_FILTER HttpSessionContextIntegrationFilter
LOGOUT_FILTER LogoutFilter
X509_FILTER X509PreAuthenticatedProcessigFilter
PRE_AUTH_FILTER Subclass of AstractPreAuthenticatedProcessingFilter
CAS_PROCESSING_FILTER CasProcessingFilter
AUTHENTICATION_PROCESSING_FILTER AuthenticationProcessingFilter
BASIC_PROCESSING_FILTER BasicProcessingFilter
SERVLET_API_SUPPORT_FILTER classname
REMEMBER_ME_FILTER RememberMeProcessingFilter
ANONYMOUS_FILTER AnonymousProcessingFilter
EXCEPTION_TRANSLATION_FILTER ExceptionTranslationFilter
NTLM_FILTER NtlmProcessingFilter
FILTER_SECURITY_INTERCEPTOR FilterSecurityInterceptor
SWITCH_USER_FILTER SwitchUserProcessingFilter

 

控制同一个用户是否可以同时进行多次登录:

<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>

对应到applicationContext-security.xml的配置为:

<sec:session-management invalid-session-url="/timeout.jsp">
<sec:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</sec:session-management>

HttpSessionEventPublisher类实现javax.servlet.http.HttpSessionListener接口,在Session被创建的时候通过调用ApplicationContext的publishEvent(ApplicationEvent event)发布HttpSessionCreatedEvent类型的事件,HttpSessionCreatedEvent类继承自org.springframework.context.ApplicationEvent类的子类 HttpSessionApplicationEvent抽象类。

 

下面是spring mvc的标准配置:

<servlet>
<servlet-name>bank</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>bank</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>

 

在工程的applicationContext-security.xml的关键配置为:

<!--这个元素用来在你的应用程序中启用基于安全的注解,可以使用新的基于表达式的预付-->

<sec:global-method-security pre-post-annotations="enabled" />

<!--绕过过滤器链的配置-->

<sec:http pattern="/static/**" security="none"/>

<sec:http use-expressions="true">

<sec:intercept-url pattern="/secure/extreme/**" access="hasRole('supervisor')"/>
<sec:intercept-url pattern="/secure/**" access="isAuthenticated()" />
<sec:intercept-url pattern="/**" access="permitAll" />

<!--默认登录页,如果要自定义配置为<s:form-login login-page="/login.action" default-target-url="/main.action" authentication-failure-url="/login.action?error=true" />-->
<sec:form-login />
<sec:logout logout-success-url="/loggedout.jsp" delete-cookies="JSESSIONID"/>
<sec:remember-me />

<!--上面解释过了是为了控制同一个用户是否可以同时进行多次登录:-->
<sec:session-management invalid-session-url="/timeout.jsp">
<sec:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</sec:session-management>

<!--这里可以配置自定义的过滤器,放在LAST之后<sec:custom-filter after="LAST" ref="resourceSecurityInterceptor" />-->

</sec:http>

 

下面配置用户认证的地方:

<sec:authentication-manager>

<!--可以用自定义userDetailsService来进行认证

<sec:authentication-provider user-service-ref="userDetailsService">
<sec:password-encoder hash="plaintext" />
</sec:authentication-provider>

-->

<!--可以用数据库进行配置,这样要求你的数据库表要符合一定的要求-->

<sec:authentication-provider>
<sec:password-encoder hash="md5"/>
<sec:jdbc-user-service data-source-ref="dataSource"/>
</sec:authentication-provider>

-->

<sec:authentication-provider>

<!--也可以用标准算法做加密例子如:<sec:password-encoder hash="md5"/>-->
<sec:password-encoder ref="encoder"/>
<sec:user-service>
<sec:user name="rod" password="4efe081594ce25ee4efd9f7067f7f678a347bccf2de201f3adf2a3eb544850b465b4e51cdc3fcdde" authorities="supervisor, user, teller" />
<sec:user name="dianne" password="957ea522524a41cbfb649a3e293d56268f840fd5b661b499b07858bc020d6d223f912e3ab303b00f" authorities="user,teller" />
<sec:user name="scott" password="fb1f9e48058d30dc21c35ab4cf895e2a80f2f03fac549b51be637196dfb6b2b7276a89c65e38b7a1" authorities="user" />
<sec:user name="peter" password="e175750688deee19d7179d444bfaf92129f4eea8b4503d83eb8f92a7dd9cda5fbae73638c913e420" authorities="user" />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>

加密方式配置

<beans:bean id="encoder" class="org.springframework.security.crypto.password.StandardPasswordEncoder"/>

以上就是与spring security 相关的配置。