ECM(Enterprise content management) ,
ECM 的一个普遍的解决方案是通过URL来实现访问控制的。Acegi提供了根据URL,实现访问控制的特性。Acegi也提供了方法访问控制的特性。
Acegi过滤器通过配置,可以做到:
1、当用户需要访问一个安全资源时,引导用户登录
2、通过检查用户的口令令牌,来认证用户信息
3、检查获得认证的用户是否有权限访问安全资源
Acegi 包含四个主要的组件:filter、manager、 provider、handler
filter:处于顶层组件,提供认证流程、session处理、登出等操作
manager:filter仅仅提供一个高层次的安全相关功能的抽象,而manager和provider提供了底层的真实实现。manager管理由不同的provider提供的底层安全服务。
provider:处理实际的安全资源, 如:目录、关系数据库、内存数据等。acegi针对不同的安全资源,提供了不同的provider。 也就是说,你可以将user base、权限策略等信息放在任意的数据存储服务中,manager在运行时自动选择需要的provider
handler : 在acegi其他组件处理某个流程时,可以分为多个步骤,每个步骤对应的操作,实际上交给handler处理
Acegi Filter:
Session Integration filter
Acegi 的Session Integration filter (SIF) 一般来说是你第一个要配置的filter, SIF创建一个Security Context对象, 这个对象里面保存了与安全相关的信息, 其他Acegi filter 保存安全相关的信息到Security Context对象中, 并且可以访问Security Context 对象中的安全相关的信息。
SIF 创建Security Context对象, 并且调用FilterChain中的其他Acegi filter , 其他Acegi Filter对象获取Security Context对象,并对Security Context对象进行操作。例如:Authentication Process Filter 存储用户信息(username,password) 到Security Context对象中。
当所有的Filter都操作完成后,SIF 将会检查Security Context对象,并更新Security Context对象。如果Acegi Filter对Security Context对象做了更改,SIF将会把所作的修改保存到服务器端的Session对象中去,如果没有检测到修改,SIF将不做操作。
SIF 的xml 配置文件如下:
<bean id = "httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"/>
Authentication Processing Filter
Acegi 使用Authentication Processing Filter (APF) 做认证操作。Acegi使用一个认证表单来认证用户, 在认证表单中,用户可以填写用户名,密码等信息,然后触发认证过程。
APF 中实际需要许多其他的操作来提供支持: 1、从客户端请求中抽取用户名,密码信息。2、 从用户数据表中取出用户的信息 。 3 、使用获取到的信息来完成认证操作
在配置APF时,你必须指定如下的几个参数:
Authentication Manager : 用于指定管理Authentication Provider 的Authentication Manager对象。
Filter Processing URL : 用于指定登录表单中提交数据的action地址,当服务器收到来自客户端对该URL的请求时,Acegi将会调用Authentication Processing Filter过滤器
Default Target URL : 用于指定当认证或授权成功后,呈现给用户的页面
Authentication Failure URL: 用于指定当认证或授权失败后,呈现给用户的页面
APF 从用户的请求中抽取username 、 password ,以及其他信息,然后把这些信息传给Authentication Manager对象。Authentication Manager对象使用合适的Provider对象从后台数据库中取出详细的用户信息。然后对这个user进行认证,把认证信息存储到一个Authentication对象中。
最后,APF将这个Authentication 对象保存到前面有SIF 创建的Security Context对象中。保存在Security Context对象中的Authentication 对象将会在后面的认证、授权过程中使用。
APF的xml配置文件如下:
<bean id = "authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="filterProcessingUrl" value="j_acegi_security_check"/>
<property name="defaultTargetUrl" value="/protected/index.jsp"/>
<property name="authenticationFailureUrl" value="/login.jsp?login_error=1"/>
</bean>
Logout Processing Filter
Acegi 使用Logout Processing Filter (LPF) 来管理登出操作。 当服务器从客户端收到一个登出请求时,将会调用LPF来处理相关的操作。LPF根据客户端请求的URL来判断是否是登出请求;当访问的URL为:j_acegi_logout时,LPF认为是请求登出
LPF的xml配置文件如下:
<bean id="logoutProcessingFilter" class="org.acegisecurity.ui.logout.LogoutFilter" >
<constructor-arg value="/logoutSuccess.jsp"/>
<constructor-arg>
<list>
<bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/>
</list>
</constructor-arg>
</bean>
在上面的LPF配置文件中,我们可以看到,LPF在其构造器中需要传入两个参数: 1、登出成功后显示的页面的url(logoutSuccess.jsp) 2、 实际上处理登出操作的Handler 列表
当登出操作结束后,将会客户端重定向到/logoutSuccess.jsp页面中。Handler是实际处理登出操作的对象。
Exception Translation Filter
Exception Translation Filter (ETF) 用于处理在认证和授权过程中出现的异常。例如,当认证失败后,ETF 决定怎么处理接下来的操作。
例如,当一个非授权的用户试图访问受保护的资源时,ETF 将会给这个用户提供一个登陆页面,用于认证用户信息。同样的,当出现授权失败时,ETF可以提供一个访问受限页面
ETF的xml配置文件如下:
<bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint" >
<bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl" value="/login.jsp" />
</bean>
</property>
<property name="accessDeniedHandler">
<bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
<property name="errorPage" value="/accessDenied.jsp" />
</bean>
</property>
</bean>
正如上面配置文件上看到的,ETF需要两个参数: authenticationEntryPoint 、accessDeniedHandler 。authenticationEntryPoint 用于指定用户认证时的登陆页面, accessDeniedHandler 用于指定当授权失败时显示的页面
Interceptor Filters
Acegi 的Interceptor Filters 用于对用户请求授权,当APF对用户请求认证成功后,你需要配置Interceptor Filters 来对用户进行授权操作。Interceptor Filter使用实际应用程序的访问控制策略来对用户授权
一个简单的访问控制配置操作可以分为两步:
1、 写访问控制策略
2、 根据访问控制策略,配置相应的Interceptor Filters
1、 写访问控制策略:
alice=123 ; ROLE_HEAD_OF_ENGINEERING
上面的配置信息,配置了一个用户名为alice , 密码为123 , 角色为:ROLE_HEAD_OF_ENGINEERING的用户。 (随后会谈到在如何配置用户、角色)
2、 配置Acegi的Interceptor Filter
Interceptor Filter的xml配置文件如下:
<bean id = "filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="objectDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/protected/**=ROLE_HEAD_OF_ENGINEERING
/**=IS_AUTHENTICATED_ANONYMOUSLY
</value>
</property>
</bean>
如上面的配置文件所述,Interceptor Filter 需要使用3个组件,其中authenticationManager和前面讨论的authenticationManager对象一样,Interceptor Filter 在授权的过程中,可以使用authenticationManager对象来对用户进行重新认证。
accessDecisionManager,用于管理认证的过程, accessDecisionManager 使用objectDefinitionSource 所提供的访问控制策略来决定一个用户是否会被允许访问指定的资源
objectDefinitionSource 定义了与访问控制有关的权限配置信息,通过这些配置信息,能够对指定的用户访问授权。例如上面指定的对于/protected/* 目录的访问,需要ROLE_HEAD_OF_ENGINEERING 权限
Filter工作的机制:
Acegi本身持有一个Filter Chain对象,这个对象中包装了所有配置的与应用程序安全相关的Filter。Acegi Filter Chain的生命周期开始于一个对服务器的用户请求。
Acegi Filter Chain 的生命周期:
1、 客户端浏览器向应用程序发送一个HTTP 请求。
2、容器收到这个HTTP请求,创建一个Request对象用于包装HTTP 请求中的信息。容器同时也会创建一个Response对象,这样不同的Filter就可以为客户端准备HTTP 响应了。随后容器调用Acegi 的 Filter Chain Proxy。Filter Chain Filter 是一个代理过滤器
这个代理知道实际中应用的过滤器的序列。当容器调用这个代理时,容器会将Request、Response、Filter Chain对象传给这个代理。
3、这个代理过滤器调用Filter Chain中的第一个过滤器,把Request、 Response 、 Filter Chain对象传给第一个过滤器
4、Filter Chain中的过滤器一个接一个的调用,在每个过滤器中,可以调用下一个过滤器,来终止本过滤器的操作。一个过滤器也可以不做任何操作,例如:当APF发现客户端的请求不需要认证时,APF可以终止认证过程
5、当于认证有关的过滤器都执行完成后,Request,Response ,Filter Chain 对象将会被传给系统配置的Interceptor Filter ,进行授权操作。
6、Interceptor Filter 根据访问的客户端访问的资源来判断客户端请求是否有权限访问。
7、Interceptor Filter 将控制权转交给你的应用程序。例如一个客户端请求的jsp页面(认证、授权成功的时候)
8、应用程序向Response 对象中写入内容(Model)
9、现在Response对象已经准备好了,容器将Response对象转换为HTTP 响应对象,然后通过HTTP协议将响应发送给请求的客户端
SIF创建Security Context 的过程
1、Acegi 的 Filter Chain 代理调用SIF过滤器,将Request , Response ,Filter Chain 对象传递给SIF。一般来说,你需要把SIF配置成Filter Chain中的第一个过滤器
2、SIF检测是否已经处理过这个web 请求,如果他发现已经处理过了, 将不做任何操作,只是调用Filter Chain中的下一个过滤器。如果他发现这是第一次处理这个web 请求,他将设置一个标记。下一次访问是,通过这个标记,SIF来判断是否已经处理过对应的web 请求
3、SIF对象检测是否存在Session对象,Session对象中是否包含了Security Context对象。如果存在Session对象,SIF将从Session中获取Security Context对象,然后把这个对象放到临时的Security Context Holder对象。如果SIF发现不存在Session对象,SIF将会创建一个Security Context
对象,然后把这个新创建的Security Context对象放到Security Context Horder对象中。 Security Context Horder对象是Application 范围的,因此我们可以在任何与安全相关的过滤器中使用它
4、SIF调用Filter Chain中的下一个过滤器
5、Filter Chain中的过滤器有可能编辑Security Context对象。
6、Filter Chain中的过滤器都处理完成后,SIF重新获得控制权。
7、SIF检查是否有其他过滤器改变了Security Context对象(例如:APF 可能会在Security Context中存储用户的信息)。如果发现有更改,SIF将会更新Session对象中的Security Context对象。这就意味着,Session中的Security Context对象保存了Filter Chain中过滤器的所有操作。
APF认证用户的过程
1、Filter Chain 中的前一个Filter将Request, Response , Filter Chain 对象传递给APF
2、APF 根据从Request中得到的username 、password,以及其他信息,创建一个认证令牌
3、APF 将认证令牌传递给authentication Manager对象
4、authentication Manager中可能包含了多个authentication Provider。每个provider 都支持特定类型的认证令牌。 authentication Manager 找到能够支持认证令牌的provider对象,然后将从APF中获得的认证令牌传递给对应的provider对象。
5、authentication Provider 从认证令牌中抽取username, 然后将username传递给一个叫做user cache service 的业务bean中,Acegi 保存了每一个已经认证过的用户的信息,当同一个用户下一次登陆时,Acegi将可以从cache中获取用户的信息。而不用从后台数据库中读取。
6、user cache service 检查是否存在对应用户的信息。
7、user cache service 返回对应用户的详细信息给authentication Provider 。如果user cache service中没有对应的用户信息,将返回null 给authentication provider。
8、如果user cache service 返回null , authentication Provider 将把username 传递给另一个叫做user details service的service bean中
9、user details service 从后台数据库中查询用户的详细信息。然后将用户的详细信息传递给authentication Provider,如果找不到对应的用户的详细信息。user details service将会抛出一个认证异常
10、如果能够从user details service 或者user cache service中获得有效的用户详细信息。authentication Provider 将会用认证令牌和从user details service 或user cache service获取的用户详细信息比对,如果发现有相同的,authentication Provider 将会把对应的用户详细信息返回给authentication manager对象。如果发现不匹配,authentication Provider 将会抛出一个认证异常。
11、authentication manager 将用户的详细信息返回给APF , 用户成功通过认证
12、APF 将返回的用户详细信息保存到security Context 中
13、APF 将控制权转交给Filter Chain中的下一个Filter 。
配置Authentication manager
org.acegisecurity.providers.ProviderManager 是一个用来管理Acegi认证过程的一个管理类,可以把它作为Authentication Manager对象。一个Authentication Manager对象需要一个或多个Authentication Provider 对象,我们可以使用ProviderManager对象的providers 属性来指定对应的provider
Authentication Manager的xml配置文件如下:
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref local="ldapAuthenticationProvider"/>
</list>
</property>
</bean>
上面的Authentication Manager配置中, 只为该manager配置了一个authentication Provider。
配置LDAP authentication Provider
LDAP authentication Provider 对应的类为:org.acegisecurity.providers.ldap.LdapAuthenticationProvider, 在构造LdapAuthenticationProvider 对象时,需要提供两个参数:
authenticator:用于认证LDAP用户的username,password。一旦用户通过认证,第二个参数populator ,将从LDAP 目录中重新获取用户的权限
LDAP authentication Provider 的xml配置文件如下:
<bean id="ldapAuthenticationProvider" class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">
<constructor-arg><ref local="authenticator"/></constructor-arg>
<constructor-arg><ref local="populator" /></constructor-arg>
</bean>
配置authenticator
authenticator 用于判断给定的用户是否存在于LDAP Directory 中,并且从LDAP Directory中获取username,password 。Acegi提供了org.acegisecurity.providers.ldap.authenticator.BindAuthenticator, 用于完成authenticator 的认证功能
authenticator 的xml配置文件如下:
<bean id="authenticator" class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">
<constructor-arg><ref local="initialDirContextFactory" /></constructor-arg>
<property name="userDnPatterns">
<list>
<value>uid={0},ou=employees,ou=partners</value>
<value>uid={0},ou=customers,ou=partners</value>
<value>uid={0},ou=suppliers,ou=partners</value>
</list>
</property>
</bean>
。。。。。。。LDAP 暂时用不到,关于populator , initialDirContextFactory的配置信息,参见http://www.ibm.com/developerworks/java/library/j-acegi2/。。。。。。。。。
配置accessDecisionManager
accessDecisionManager用于决定一个用户是否有权限访问指定的资源,Acegi提供了大量的accessDecisionManager 的实现,他们在如果判断用户是否有权限访问指定资源时的判断方式是不同的。
accessDecisionManager 的xml配置实例如下:
<bean id="accessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
<property name="decisionVoters">
<list>
<bean class="org.acegisecurity.vote.RoleVoter" />
<bean class="org.acegisecurity.vote.AuthenticatedVoter" />
</list>
</property>
</bean>
上面配置的accessDecisionManager使用了AffirmativeBased 的实现,他需要voter数组作为其参数。
在Acegi中,使用voters 来决定一个用户是否被允许访问指定的资源,当accessDecisionManager 需要判断一个用户是否有权限访问指定资源时,accessDecisionManager会询问voters,voters有3个可选项:允许访问,拒绝访问,弃权
不同的accessDecisionManager对于voters的投票有不同的解释,对于AffirmativeBased 实现,只要有voters投赞成票,AffirmativeBased 将会允许用户访问指定的资源。
Voter 的逻辑实现
Acegi提供了多个voter实现,accessDecisionManager 把一个认证用户的信息(包括用户的角色信息)和objectDefinitionSource对象传递给voter。voter根据收到的信息来判断是否允许访问指定资源
RoleVoter:RoleVoter对象只有当他在objectDefinitionSource中找到以ROLE_开头的角色时,才会发起投票。如果他找不到对应的项时,将会弃权。如果在objectDefinitionSource中找到了与用户信息匹配的角色时,将会投赞成票,如果找不到匹配的角色时,将投反对票
AuthenticatedVoter: AuthenticatedVoter对象只有当他在objectdefinitionSource中找到预定义的角色信息时,才回发起投票。IS_AUTHENTICATED_ANONYMOUSLY是一个预定义的角色信息,他代表一个没有认证的用户信息。AuthenticatedVoter一旦发现这个预定义角色时,
他将会检查是否一些未保护的资源可以被未认证的用户访问。如果AuthenticatedVoter发现被访问的资源是一个未保护的资源,而且在objectDefinitionSource对象中,允许未认证的用户访问未保护的资源。AuthenticatedVoter将会投赞成票,否则投反对票
Spring 创建java对象流程:
。。。。。。
为java对象配置代理
如果你需要创建一个java代理对象。Spring 的IOC 框架需要你配置一个proxy creator bean的实例。Spring框架使用这个proxy creator 来创建java的代理对象。
spring中的proxy creater 的xml配置文件:
<bean id="proxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<!--Name of other beans to be proxy-->
<value>privateCatalog</value>
</list>
</property>
<property name="interceptorNames">
<list>
<!--Name of interceptor for those proxy beans-->
<value>privateCatalogSecurityInterceptor</value>
</list>
</property>
</bean>
如上面的配置文件所示, proxy creator 是org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator 类的一个实例, BeanNameAutoProxyCreator 类是Spring IoC 框架的一部分,这个类用于自动创建bean代理。
Spring框架提供了一个叫做BeanPostProcessor的接口, 这个接口提供了允许用户自己设计创建代理bean的逻辑的扩展功能,BeanNameAutoProxyCreator类实现了这个接口,并且提供了创建代理bean的逻辑。因此,我们没有必要自己实现proxy Creator类。
在创建bean 代理时, proxy creator 将会为beanNames中的所有bean创建一个代理对象。
当Spring 中的XMLWebApplicationContext对象从Spring的xml配置文件中读取bean的配置信息时,XMLWebApplicationContext 会检查xml配置文件中是否存在proxy creator对象(通过检查是否有Bean实现了BeanPostProcessor接口的),如果发现有proxy creator对象,
XMLWebApplicationContext对象将会让proxy creator对象,为指定的bean创建代理。如果没有找到proxy creator对象,XMLWebApplicationContext对象将会把spring配置文件中的bean实例化,然后将其保存在自己的对象中。
Proxy Creator 内部创建代理的过程:
1、proxy creator对象加载beanNames中指定的beanname, 然后通过指定的beanName,实例化对应的bean对象(通过class想找到对应的类)
2、proxy creator 对象创建interceptorNames中指定的拦截器的实例
3、proxy creator 将会创建一个Cglib2AopProxy对象,并且将实例化的bean对象,和实例化的拦截器对象传到Cglib2AopProxy中,Cglib2AopProxy 用来动态创建代理对象。
Cglib2AopProxy类实现了AOPProxy和MethodInterceptor 接口,AOPProxy是Spring框架的一部分,用来表示你实际代理的bean对象,AOPProxy对象提供了实际代理的bean提供的所有方法。
MethodInterceptor 接口也是AOP规范中的一部分,当有请求访问代理的bean时,MethodIntercetpor 接口可以获取对对应代理bean的控制权。因而可以在Methodinterceptor中处理对代理bean的访问控制。
当proxy creator 创建完代理后,XMLWebApplicationContext 对象将会保存创建后的代理对象。
配置Acegi的method security Interceptor
当试图访问一个代理对象时, 请求自动被转发到Acegi 的 Method Security Interceptor 中,使用Method Security Interceptor 的目的就是用来控制对代理bean的访问。Method Security Interceptor 使用Acegi的认证和授权框架来判断一个用户是否有权限访问指定的代理对象的方法,然后根据配置文件做出相应的响应
Method Security Interceptor 的xml配置文件:
<bean id="privateCatalogSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDescisionManager" ref="accessDescisionManager" />
<property name="objectDefineSource" >
<value>
sample.PrivateCatalog.getData=ROLE_HEAD_OF_ENGINEERING
</value>
</property>
</bean>
当有用户访问sample.PrivateCatalog的getData方法时,bean的控制权自动被转移给了Method Security Interceptor, Method Security Interceptor使用Acegi框架去检查用户是否有ROLE_HEAD_OF_ENGINEERING的权限,如果发现没有,他将拒绝访问。
也可以为某个bean临时授权,具体参见:http://www.ibm.com/developerworks/java/library/j-acegi3/index.html