Acegi 的配置看起来非常复杂
但事实上在实际项目的安全应用中我们并不需要那么多功能
清楚的了解Acegi配置中各项的功能
有助于我们灵活的运用Acegi于实践中
在Webxml中的配置
) FilterToBeanProxy
Acegi通过实现了Filter接口的FilterToBeanProxy提供一种特殊的使用Servlet Filter的方式它委托Spring中的Bean FilterChainProxy来完成过滤功能这好处是简化了webxml的配置并且充分利用了Spring IOC的优势FilterChainProxy包含了处理认证过程的filter列表每个filter都有各自的功能
<filter>
<filtername>Acegi Filter Chain Proxy</filtername>
<filterclass>orgacegisecurityutilFilterToBeanProxy</filterclass>
<initparam>
<paramname>targetClass</paramname>
<paramvalue>orgacegisecurityutilFilterChainProxy</paramvalue>
</initparam>
</filter>
) filtermapping
<filtermapping>限定了FilterToBeanProxy的URL匹配模式只有*do和*jsp和/j_acegi_security_check 的请求才会受到权限控制对javascriptcss等不限制
<filtermapping>
<filtername>Acegi Filter Chain Proxy</filtername>
<urlpattern>*do</urlpattern>
</filtermapping>
<filtermapping>
<filtername>Acegi Filter Chain Proxy</filtername>
<urlpattern>*jsp</urlpattern>
</filtermapping>
<filtermapping>
<filtername>Acegi Filter Chain Proxy</filtername>
<urlpattern>/j_acegi_security_check</urlpattern>
</filtermapping>
) HttpSessionEventPublisher
<listener>的HttpSessionEventPublisher用于发布HttpSessionApplicationEvents和HttpSessionDestroyedEvent事件给spring的applicationcontext
<listener>
<listenerclass>orgacegisecurityuisessionHttpSessionEventPublisher</listenerclass>
</listener>
在applicationContextacegisecurityxml中
FILTER CHAIN
FilterChainProxy会按顺序来调用这些filter使这些filter能享用Spring ioc的功能 CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON定义了url比较前先转为小写 PATTERN_TYPE_APACHE_ANT定义了使用Apache ant的匹配模式
<bean id=filterChainProxy class=orgacegisecurityutilFilterChainProxy>
<property name=filterInvocationDefinitionSource>
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilterauthenticationProcessingFilter
basicProcessingFilterrememberMeProcessingFilteranonymousProcessingFilter
exceptionTranslationFilterfilterInvocationInterceptor
</value>
</property>
</bean>
基础认证
) authenticationManager
起到认证管理的作用它将验证的功能委托给多个Provider并通过遍历Providers 以保证获取不同来源的身份认证若某个Provider能成功确认当前用户的身份authenticate()方法会返回一个完整的包含用户授权信息的Authentication对象否则会抛出一个AuthenticationException
Acegi提供了不同的AuthenticationProvider的实现如
DaoAuthenticationProvider 从数据库中读取用户信息验证身份
AnonymousAuthenticationProvider 匿名用户身份认证
RememberMeAuthenticationProvider 已存cookie中的用户信息身份认证
AuthByAdapterProvider 使用容器的适配器验证身份
CasAuthenticationProvider 根据Yale中心认证服务验证身份 用于实现单点登陆
JaasAuthenticationProvider 从JASS登陆配置中获取用户信息验证身份
RemoteAuthenticationProvider 根据远程服务验证用户身份
RunAsImplAuthenticationProvider 对身份已被管理器替换的用户进行验证
XAuthenticationProvider 从X认证中获取用户信息验证身份
TestingAuthenticationProvider 单元测试时使用
每个认证者会对自己指定的证明信息进行认证如DaoAuthenticationProvider仅对UsernamePasswordAuthenticationToken这个证明信息进行认证
<bean id=authenticationManager class=orgacegisecurityprovidersProviderManager>
<property name=providers>
<list>
<ref local=daoAuthenticationProvider/>
<ref local=anonymousAuthenticationProvider/>
<ref local=rememberMeAuthenticationProvider/>
</list>
</property>
</bean>
) daoAuthenticationProvider
进行简单的基于数据库的身份验证DaoAuthenticationProvider获取数据库中的账号密码并进行匹配若成功则在通过用户身份的同时返回一个包含授权信息的Authentication对象否则身份验证失败抛出一个AuthenticatiionException
<bean id=daoAuthenticationProvider
class=orgacegisecurityprovidersdaoDaoAuthenticationProvider>
<property name=userDetailsService ref=jdbcDaoImpl/>
<property name=userCache ref=userCache/>
<property name=passwordEncoder ref=passwordEncoder/>
</bean>
) passwordEncoder
使用加密器对用户输入的明文进行加密Acegi提供了三种加密器:
PlaintextPasswordEncoder—默认不加密返回明文
ShaPasswordEncoder—哈希算法(SHA)加密
MdPasswordEncoder—消息摘要(MD)加密
<bean id=passwordEncoder class=orgacegisecurityprovidersencodingMdPasswordEncoder/>
) jdbcDaoImpl
用于在数据中获取用户信息 acegi提供了用户及授权的表结构但是您也可以自己来实现通过usersByUsernameQuery这个SQL得到你的(用户ID密码状态信息);通过authoritiesByUsernameQuery这个SQL得到你的(用户ID授权信息)
<bean id=jdbcDaoImpl class=orgacegisecurityuserdetailsjdbcJdbcDaoImpl>
<property name=dataSource ref=dataSource/>
<property name=usersByUsernameQuery>
<value>select loginidpasswd from users where loginid = ?</value>
</property>
<property name=authoritiesByUsernameQuery>
<value>select uloginidpname from users uroles rpermissions puser_role urrole_permis rp where uid=uruser_id and rid=urrole_id and pid=rppermis_id and
rid=rprole_id and pstatus= and uloginid=?</value>
</property>
</bean>
) userCache& resourceCache
缓存用户和资源相对应的权限信息每当请求一个受保护资源时daoAuthenticationProvider就会被调用以获取用户授权信息如果每次都从数据库获取的话那代价很高对于不常改变的用户和资源信息来说最好是把相关授权信息缓存起来(详见 资源权限定义扩展 )
userCache提供了两种实现: NullUserCache和EhCacheBasedUserCache NullUserCache实际上就是不进行任何缓存EhCacheBasedUserCache是使用Ehcache来实现缓功能
<bean id=userCacheBackend class=orgspringframeworkcacheehcacheEhCacheFactoryBean>
<property name=cacheManager ref=cacheManager/>
<property name=cacheName value=userCache/>
</bean>
<bean id=userCache class=orgacegisecurityprovidersdaocacheEhCacheBasedUserCache autowire=byName>
<property name=cache ref=userCacheBackend/>
</bean>
<bean id=resourceCacheBackend class=orgspringframeworkcacheehcacheEhCacheFactoryBean>
<property name=cacheManager ref=cacheManager/>
<property name=cacheName value=resourceCache/>
</bean>
<bean id=resourceCache class=orgspringsidemodulessecurityserviceacegicacheResourceCache autowire=byName>
<property name=cache ref=resourceCacheBackend/>
</bean>
) basicProcessingFilter
用于处理HTTP头的认证信息如从Spring远程协议(如Hessian和Burlap)或普通的浏览器如IENavigator的HTTP头中获取用户信息将他们转交给通过authenticationManager属性装配的认证管理器如果认证成功会将一个Authentication对象放到会话中否则如果认证失败会将控制转交给认证入口点(通过authenticationEntryPoint属性装配)
<bean id=basicProcessingFilter
class=orgacegisecurityuibasicauthBasicProcessingFilter>
<property name=authenticationManager ref=authenticationManager/>
<property name=authenticationEntryPoint ref=basicProcessingFilterEntryPoint/>
</bean>
) basicProcessingFilterEntryPoint
通过向浏览器发送一个HTTP(未授权)消息提示用户登录
处理基于HTTP的授权过程 在当验证过程出现异常后的去向通常实现转向在response里加入error信息等功能
<bean id=basicProcessingFilterEntryPoint class=orgacegisecurityuibasicauthBasicProcessingFilterEntryPoint>
<property name=realmName value=SpringSide Realm/>
</bean>
) authenticationProcessingFilterEntryPoint
当抛出AccessDeniedException时将用户重定向到登录界面属性loginFormUrl配置了一个登录表单的URL当需要用户登录时authenticationProcessingFilterEntryPoint会将用户重定向到该URL
<bean id=authenticationProcessingFilterEntryPoint class=orgacegisecurityuiwebappAuthenticationProcessingFilterEntryPoint>
<property name=loginFormUrl>
<value>/security/loginjsp</value>
</property>
<property name=forceHttps value=false/>
</bean>
HTTP安全请求
) httpSessionContextIntegrationFilter
每次request前 HttpSessionContextIntegrationFilter从Session中获取Authentication对象在request完后 又把Authentication对象保存到Session中供下次request使用此filter必须其他Acegi filter前使用使之能跨越多个请求
<bean id=httpSessionContextIntegrationFilter
class=ontextHttpSessionContextIntegrationFilter></bean>
<bean id=httpRequestAccessDecisionManager
class=orgacegisecurityvoteAffirmativeBased>
<property name=allowIfAllAbstainDecisions value=false/>
<property name=decisionVoters>
<list>
<ref bean=roleVoter/>
</list>
</property>
</bean>
) httpRequestAccessDecisionManager
经过投票机制来决定是否可以访问某一资源(URL或方法)allowIfAllAbstainDecisions为false时如果有一个或以上的decisionVoters投票通过则授权通过可选的决策机制有ConsensusBased和UnanimousBased
<bean id=httpRequestAccessDecisionManager
class=orgacegisecurityvoteAffirmativeBased>
<property name=allowIfAllAbstainDecisions value=false/>
<property name=decisionVoters>
<list>
<ref bean=roleVoter/>
</list>
</property>
</bean>
) roleVoter
必须是以rolePrefix设定的value开头的权限才能进行投票如AUTH_ ROLE_
<bean id=roleVoter class=orgacegisecurityvoteRoleVoter>
<property name=rolePrefix value=AUTH_/>
</bean>
)exceptionTranslationFilter
异常转换过滤器主要是处理AccessDeniedException和AuthenticationException将给每个异常找到合适的去向
<bean id=exceptionTranslationFilter
class=orgacegisecurityuiExceptionTranslationFilter>
<property name=authenticationEntryPoint
ref=authenticationProcessingFilterEntryPoint/>
</bean>
) authenticationProcessingFilter
和servlet spec差不多处理登陆请求当身份验证成功时AuthenticationProcessingFilter会在会话中放置一个Authentication对象并且重定向到登录成功页面
authenticationFailureUrl定义登陆失败时转向的页面
defaultTargetUrl定义登陆成功时转向的页面
filterProcessesUrl定义登陆请求的页面
rememberMeServices用于在验证成功后添加cookie信息
<bean id=authenticationProcessingFilter
class=orgacegisecurityuiwebappAuthenticationProcessingFilter>
<property name=authenticationManager ref=authenticationManager/>
<property name=authenticationFailureUrl>
<value>/security/loginjsp?login_error=</value>
</property>
<property name=defaultTargetUrl>
<value>/admin/indexjsp</value>
</property>
<property name=filterProcessesUrl>
<value>/j_acegi_security_check</value>
</property>
<property name=rememberMeServices ref=rememberMeServices/>
</bean>
) filterInvocationInterceptor
在执行转向url前检查objectDefinitionSource中设定的用户权限信息首先objectDefinitionSource中定义了访问URL需要的属性信息(这里的属性信息仅仅是标志告诉accessDecisionManager要用哪些voter来投票)然后authenticationManager掉用自己的provider来对用户的认证信息进行校验最后有投票者根据用户持有认证和访问url需要的属性调用自己的voter来投票决定是否允许访问
<bean id=filterInvocationInterceptor
class=orgacegisecurityinterceptwebFilterSecurityInterceptor>
<property name=authenticationManager ref=authenticationManager/>
<property name=accessDecisionManager ref=httpRequestAccessDecisionManager/>
<property name=objectDefinitionSource ref=filterDefinitionSource/>
</bean>
) filterDefinitionSource (详见 资源权限定义扩展)
自定义DBFilterInvocationDefinitionSource从数据库和cache中读取保护资源及其需要的访问权限信息
<bean id=filterDefinitionSource
class=orgspringsidemodulessecurityserviceacegiDBFilterInvocationDefinitionSource>
<property name=convertUrlToLowercaseBeforeComparison value=true/>
<property name=useAntPath value=true/>
<property name=acegiCacheManager ref=acegiCacheManager/>
</bean>
方法调用安全控制
(详见 资源权限定义扩展)
) methodSecurityInterceptor
在执行方法前进行拦截检查用户权限信息
) methodDefinitionSource
自定义MethodDefinitionSource从cache中读取权限
<bean id=methodSecurityInterceptor
class=orgacegisethodaopallianceMethodSecurityInterceptor>
<property name=authenticationManager ref=authenticationManager/>
<property name=accessDecisionManager ref=httpRequestAccessDecisionManager/>
<property name=objectDefinitionSource ref=methodDefinitionSource/>
</bean>
<bean id=methodDefinitionSource
class=orgspringsidemodulessecurityserviceacegiDBMethodDefinitionSource>
<property name=acegiCacheManager ref=acegiCacheManager/>
</bean>
Jcaptcha验证码
采用 ;作为通用的验证码方案请参考SpringSide中的例子或网上的
_with_appfuse
差沙在此过程中又发现acegi logout filter的错误进行了修正
另外它默认提供的图片比较难认我们custom了一个美观一点的版本