Acegi 设计概述
关键词:
acegi
作者: cac,转自 springside
1. 简单叙述认证授权流程
第一步, 认证(authentication)。Acegi调用AuthenticationManager(认证管理器)来对当前请求登陆系统用户进行验证,AuthenticationManager负责委托一个或多个Provider(认证提供者),并逐一遍历每个Provider,直到某一个Provider能够成功的验证用户的身份。Provider成功验证用户身份后,会返回一个Authentication(证明), Authentication中包括Principal(用户名),Credentials(通常是密码),Authorities(该用户所拥有的权限)。这样就完成了身份验证的步骤。
第二步,授权(authorization)。当Acegi获得Authentication后,也就确定了用户的身份。每当用户请求访问某受保护资源时,Acegi会调用AccessDecisionManager(访问决策管理器)来决定用户的Authentication是否有恰当的权限来访问当前访问的受保护资源,有则授权用户通过,无则抛出错误信息,以达到访问控制的目的。
2. 安全保护方式
- 保护Web应用程序
当去保护Web应用程序时,Acegi使用Servlet Filter来拦截Servlet请求,并将这些请求转给AuthenticationManager,AccessDecisionManager或其他管理器来处理。
Acegi提供了多个各种功能的Filter, 这些Filter按照先后顺序组成了一个 Filter Chain。当一个请求被提交道一个由Acegi所保护的Web应用程序时,该请求会按照如下顺序逐一通过Filter Chain:
第一步, 请求通过ChannelProcessingFilter(通道处理过滤器,可选)。该过滤器负责检查当前请求的Channel,并判断是否已满足安全需要。如果不满足,则由非安全的通道传输(Http)重定向于安全的通道传输(Https),以确保服务器与浏览器之间传输的数据加密。
第二步,请求通过 AuthenticationProcessingFilter(认证处理过滤器)。该过滤器会判断该请求是否是一个认证请求(通常是"/j_acegi_security_check")。如果是,则它会从请求中获取用户名和密码,并转交给AuthenticationManager来认证用户身份。如果不是,则会直接转到下一个Filter。
第三步,请求通过HttpSessionContextIntegrationFilter(HttpSession安全上下文信息集成过滤器)。该过滤器负责将Authentication对象保存在HttpSession中,使其在下一个请求到来时仍可被获取。故Authentication能跨越多个请求。
第四步,请求通过FilterSecurityInterceptor(过滤器安全拦截器)。该过滤器会首先调用AuthenticationManager判断用户是否已登陆认证,如还没认证成功,则重定向到登陆界面。认证成功,则并从Authentication中获取用户的权限。然后从objectDefinitionSource属性获取各种URL资源所对应的权限。最后调用AccessDecisionManager来判断用户所拥有的权限与当前受保华的URL资源所对应的权限是否相匹配。如果匹配失败,则返回403错误(禁止访问)给用户。匹配成功则用户可以访问受保护的URL资源。
- 保护方法调用
当去保护方法调用时,Acegi使用Spring AOP,将"切面"应用于所代理的对象,以确保用户只有在拥有恰当授权时才可以调用受保护的方法。
MethodSecurityInterceptor(方法调用安全拦截器)同样也是在调用方法之前,先调用AuthenticationManager判断用户身份是否已验证,然后从objectDefinitionSource中获取方法所对应的权限,再调用AccessDecisionManager来匹配用户权限和方法对应的权限。如果用户没有足够权限调用当前方法,则抛出AccessDeniedException使方法不能被调用。
3.过滤器链代理
Acegi的FilterChainProxy提供了一种很好的方式,使你不需要在web.xml配置Filters,而在Spring配置文件中加入, 从而能充分利用Spring IOC的优势。
FilterChainProxy其实也只是一个扩展后的Filter,它负责委托Spring中的各个实现了javax.servlet.Filter接口的Bean来执行过滤功能。 如httpSessionContextIntegrationFilter, authenticationProcessingFilter, filterInvocationInterceptor等。
在web.xml中的配置FilterToBeanProxy:
<filter>
<filter-name>Acegi Filter Chain Proxy</filter-name>|
<filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>org.acegisecurity.util.FilterChainProxy</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在Spring配置文件中相应的FilterChainProxy Bean:
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter, rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
</value>
</property>
</bean>
4. Acegi Exceptions 管理机制
Acegi的异常管理机制做得不错,基本上可以满足权限管理需求。在其结构基础上继承实现自己的异常类也是很方便的。
所有Acegi的异常都是在 AcegiSecurityException 上继承而来的,其中最主要的是 AuthenticationException 和 AccessDeniedException。
这里需要注意的是 AcegiSecurityException 继承了Spring的 NestedRuntimeException,而NestedRuntimeException又是继承RuntimeException的。
所以抛出的时候不需要throws, 而捕捉的时候需要专门catch RuntimeException。
AuthenticationException 是所有授权相关的异常的父类,当用户身份验证失败时就会抛出这个异常。
AccessDeniedException 是所有认证相关的异常的父类,当资源被拒绝访问时就会抛出这个异常。
在Acegi 中,通过ExceptionTranslationFilter(异常转换过滤器)来对各种异常进行捕获和处理,并重定向到不同的错误信息显示页面。
如遇到AuthenticationException 就会重定向到登陆页面,遇到AccessDeniedException 就会无权访问资源页面。你还可以继承ExceptionTranslationFilter基础上覆盖handleException方法来实现你自己的系统中异常的处理。
<bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint">
<bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl" value="/login.jsp" />
<property name="forceHttps" value="false" />
</bean>
</property>
<property name="accessDeniedHandler">
<bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
<property name="errorPage" value="/accessDenied.jsp" />
</bean>
</property>
</bean>