转自:http://zhuoyaopingzi.iteye.com/blog/1219364
以前在PA关注过用户登录授权的过程,看过JAAS的规范,看过WEBLOGIC的实现代码,看过Spring Security的源代码,这么久都忘了。。现在开始,把以前丢掉的技术日记能记得记下来,以后也开始写日记了~~:)
----
验证
使用opends作ldap数据库,(运行的脚本在该目录的ldap.ldif下)
上下文参数:
http://localhost:389
o=company.com.cn
cn=orcladmin
pwd: hhxxttxs
创建ou用的opends的图形工具创建,groups下面的条目用ldif导入创建的,ldif如下:
dn:cn=staffRole,ou=groups,o= company.com.cn objectClass:groupOfUniqueNames cn:staffRole uniquemember:uid=zhuoyueping790,ou=staff,ou=people,o= company.com.cn
dn:cn=clientRole,ou=groups,o= company.com.cn objectClass:groupOfUniqueNames cn:clientRole uniquemember:uid=chenshengli532,ou=client,ou=people,o= company.com.cn |
有staffRole角色的用户可以访问/staff.*资源;
有clientRole角色的用户可以访问/client.*资源
例子中所有密码均为:hhxxttxs
所有标有页数的地方为《敏捷Acegi、CAS——构建安全的java系统》的页码。
红色表示待解决的问题,蓝色表示有特别说明或者代码解释的语句。
该项目的全部文件(包括jar包,在该目录下的ss目录中)
斜体bena的名字表示即将要解释的调用的bean.
web.xml
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> org.springframework.web.context.ContextLoaderListener《1》 org.springframework.security.util.FilterToBeanProxy《2》 |
《1》:org.springframework.web.context.ContextLoaderListener
(该类在spring-web.jar包中,)
《2》:org.springframework.util.FilterToBeanProxy:如果配置了targetBean和targetClass,优先调用targetHean.推荐使用targetHean的方式。
通过调用FilterToBeanProxy的init()方法启动过滤器链工作。
(org.springframework.security.*的类都在spring-security-core.jar包中,)
public class FilterToBeanProxy implements Filter{ public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; String strategy = filterConfig.getInitParameter("init"); if ((strategy != null) && (strategy.toLowerCase().equals("lazy"))) { return; } doInit(); } } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (!(this.initialized)) { doInit(); } }
private synchronized void doInit() throws ServletException { //… String targetBean = this.filterConfig.getInitParameter("targetBean"); //关于lifecircle部分,先放在这里以后补充 String lifecycle = this.filterConfig.getInitParameter("lifecycle"); if ("servlet-container-managed".equals(lifecycle)) { this.servletContainerManaged = true; } ApplicationContext ctx = getContext(this.filterConfig); String beanName = null;
if ((targetBean != null) && (ctx.containsBean(targetBean))) { beanName = targetBean; } else { Class targetClass; if (targetBean != null) { throw new ServletException("targetBean '" + targetBean + "' not found in context"); } String targetClassString = this.filterConfig.getInitParameter("targetClass"); //… targetClass= Thread.currentThread().getContextClassLoader().loadClass(targetClassString); //.. this.initialized = true; //得到代理类,代理类必须是filter类,调用代理类的init() Object object = ctx.getBean(beanName); if (!(object instanceof Filter)) { throw new ServletException("Bean '" + beanName + "' does not implement javax.servlet.Filter"); } this.delegate = ((Filter)object); if (this.servletContainerManaged) {
//启动代理filter的init方法 this.delegate.init(this.filterConfig); } }
|
/WEB-INF/applicationContextsecurity.xml:
配置bean:filterChainProxy
class="org.springframework.security.util.FilterChainProxy">《3》 PATTERN_TYPE_APACHE_ANT /**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor |
《4》给该参数配置过滤器链。
《3》org.springframework.security.util.FilterChainProxy
/**表示要过该链的url为all
public class FilterChainProxy implements Filter, InitializingBean, ApplicationContextAware{ public void init(FilterConfig filterConfig) throws ServletException { Filter[] filters = obtainAllDefinedFilters(); for (int i = 0; i < filters.length; ++i) filters[i].init(filterConfig);//依次调用每个filter的过滤器链 } } } //destroy也相同,调用每个filter的destroy
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { FilterInvocation fi = new FilterInvocation(request, response, chain); List filters = getFilters(fi.getRequestUrl()); if ((filters == null) || (filters.size() == 0)) { chain.doFilter(request, response); return; } virtualFilterChain virtualFilterChain = new VirtualFilterChain(fi, filters, null); virtualFilterChain.doFilter(fi.getRequest(), fi.getResponse()); } } |
下面依次是过滤器链中的过滤器。
配置bean:httpSessionContextIntegrationFilter
集成过滤器,P49,如名字所示,将httpSession与securityContext中的认证信息同步。
class="org.springframework.security.context.HttpSessionContextIntegrationFilter">《5》 |
《5》HttpSessionContextIntegrationFilter继承SpringSecurityFilter,SpringSecurityFilter实现了接口filter,在SpringSecurityFilter【附1】的doFilter方法中调用了doFilterHttp,子类在doFilterHttp中实现自己的逻辑,该过滤器应该放在所有过滤器之前。
《6》allowSessionCreation 是否创建HTTP session。
public class HttpSessionContextIntegrationFilter extends SpringSecurityFilter implements InitializingBean{ public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain){ public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { //进入该过滤器器,从session中取认证相关信息,放在SecurityContext中。 SecurityContext contextBeforeChainExecution = readSecurityContextFromSession(httpSession); SecurityContextHolder.setContext(contextBeforeChainExecution); //继续执行过滤器链,因此,该过滤器应该放在所有过滤器之前! chain.doFilter(request, responseWrapper);
//… //在finally中执行,退出出这个过滤器(也是该过滤器链执行完后)前执行。 //在contextAfterChainExecution存最新的SecurityContext SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
SecurityContextHolder.clearContext();//清除当前线程中的SecurityContext相关信息。 request.removeAttribute("__spring_security_session_integration_filter_applied"); //将contextAfterChainExecution 修改过的securitycontext放在HTTP session对象中 if (!(responseWrapper.isSessionUpdateDone())) { storeSecurityContextInSession(contextAfterChainExecution, request, httpSessionExistedAtStartOfRequest, contextHashBeforeChainExecution); } } }
|
配置bean:authenticationProcessingFilter
*认证处理过滤器。
class="org.springframework.security.ui.webapp.AuthenticationProcessingFilter"> —org.springframework.security.ui.webapp.AuthenticationProcessingFilter: 表示用表单认证的类; authenticationFailureUrl:认证失败跳转的url。 defaultTargetUrl:认证完成后,跳转回之前请求的页面,如果没有,到该url filterProcessesUrl:提交验证请求的form标签中action的地址 authenticationManager:处理得到的用户名和密码交给谁验证的问题 -->
class="org.springframework.security.providers.ProviderManager">p50
— providers:可以指定多个provider 的bean。 -->
class="org.springframework.security.providers.ldap.LdapAuthenticationProvider"> — org.springframework.security.providers.ldap.LdapAuthenticationProvider: 指定该bean是去ldap认证。 --> class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator"> ref="ldapUserDetailsMapper"> —initialDirContextFactory有错,主要原因是包的依赖关系有问题,待解决。 -->
class="org.springframework.security.ldap.DefaultInitialDirContextFactory"> value="com.sun.jndi.ldap.LdapCtxFactory" /> class="org.springframework.security.userdetails.ldap.LdapUserDetailsMapper">
class="org.springframework.security.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">
|
配置bean:exceptionTranslationFilter
异常处理器:处理认证和授权过程中的异常
class="org.springframework.security.ui.ExceptionTranslationFilter"> p173-->
class="org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint"> 继承自AuthenticationEntryPoint p17 AuthenticationEntryPoint 的commence(ServletRequest request, ServletResponse response, AuthenticationException authException) 构造验证入口。 -->
|
配置bean:filterInvocationInterceptor
过滤安全拦截器:处理认证和授权过程中的异常
class="org.springframework.security.intercept.web.FilterSecurityInterceptor"> PATTERN_TYPE_APACHE_ANT /client/*=ROLE_CLIENTROLE /staff/*=ROLE_staffRole
class="org.springframework.security.vote.AffirmativeBased">
class="org.springframework.security.vote.RoleVoter" /> |
附:一些类。
附1:
public abstract class SpringSecurityFilter implements Filter, Ordered { protected final Log logger; public SpringSecurityFilter() { this.logger = LogFactory.getLog(super.getClass()); } public final void init(FilterConfig filterConfig) throws ServletException { } public final void destroy() { } public final void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (!(request instanceof HttpServletRequest)) { throw new ServletException("Can only process HttpServletRequest"); } if (!(response instanceof HttpServletResponse)) { throw new ServletException("Can only process HttpServletResponse"); } doFilterHttp((HttpServletRequest)request, (HttpServletResponse)response, chain); } protected abstract void doFilterHttp(HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse, FilterChain paramFilterChain) throws IOException, ServletException; public String toString() { return super.getClass() + "[ order=" + super.getOrder() + "; ]"; } public abstract int getOrder(); } |
/WEB-INF/applicationContextsecurity.xml
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:jms="http://www.springframework.org/schema/jms" xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> class="org.springframework.security.context.HttpSessionContextIntegrationFilter"> —下一个bean(initialDirContextFactory)有错,主要原因是包的依赖关系有问题,待解决。 --> class="org.springframework.security.ldap.DefaultInitialDirContextFactory"> value="com.sun.jndi.ldap.LdapCtxFactory" /> ref="ldapUserDetailsMapper"> class="org.springframework.security.providers.ProviderManager"> class="org.springframework.security.ui.ExceptionTranslationFilter"> class="org.springframework.security.vote.RoleVoter" /> class="org.springframework.security.vote.AffirmativeBased"> class="org.springframework.security.intercept.web.FilterSecurityInterceptor"> PATTERN_TYPE_APACHE_ANT /client/*=ROLE_CLIENTROLE /staff/*=ROLE_staffRole class="org.springframework.security.util.FilterChainProxy"> PATTERN_TYPE_APACHE_ANT /**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor |