`
lichdb
  • 浏览: 38795 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

acegi流程

阅读更多

很早就听说过acegi了,但是当初只是下载了,并且网上找了点资料稍微看了一下。因为没有用到,所以也没有实践。最近忽然要用到了,才发现acegi的配置可不像想像中的那么简单。在网上找10篇相关文章,其配置也会有10样,各个不同,让人无可奈何。

至于acegi到底是干嘛的,它的一些最基础的介绍我这就不废话了。最近因为要配置acegi,所以没办法,只好按照一个参考并且分析了一下acegi的部分源代码,总算对acegi稍微有所理解了。下面把acegi内部流程分析如下:

acegi原理:
1 web.xml中配置filter类 org.acegisecurity.util.FilterToBeanProxy,
  并设置targetClass 参数为 org.acegisecurity.util.FilterChainProxy
  或者设置targetBean参数值为spring中配置的beanname
2 FilterToBeanProxy初始化中,获取targetBean的值,通过spring获取是否存在
  如不存在,则通过targetClass参数构造FilterChainProxy.class并通过spring
  获取配置为该class的所有beanNames,并将第一个bean设置为delegate.
3 当符合pattern配置的web请求来临时,则直接将直接filter操作转给delegate即
  FilterChainProxy类.
4 FilterChainProxy类也是Filter的实现类,它是通过spring注入方式生成的,
  属性filterInvocationDefinitionSource的值在spring xml中配置.该值一般配置为字符串格式:
  CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
  PATTERN_TYPE_APACHE_ANT
  /**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,
   securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,
   anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
  但是filterInvocationDefinitionSource属性是一个类,以上字符串如何转化为类的实例的呢?
  filterInvocationDefinitionSource extends ObjectDefinitionSource 类可以有多个
  ConfigAttributeDefinition,每个ConfigAttributeDefinition又可以包含多个Filter.
 
5 FilterChainProxy的doFilter方法中,先根据请求生成一个FilterInvocation类,然后根据该
  FilterInvocation类和4中配置的各种filter来得到处理当前请求的ConfigAttributeDefinition,
  ConfigAttributeDefinition中存储了所有与当前请求有关的filters.接着根据所有的filters生成
  一个VirtualFilterChain类,VirtualFilterChain中顺序调用每个filter的doFilter方法,最后再
  执行Chain.doFilter方法即执行实际请求的servlet

6 以下说明各个filter的执行过程,如果配置了多个filter则filter会按照配置顺序执行.

 6.1 HttpSessionContextIntegrationFilter 主要是在session中设置一个SecurityContext,而
   SecurityContextHolder中也保存这个SecurityContext,而SecurityContext具备Authentication属性
   而第一次new SecurityContext的时候,Authentication是没有内容的。当chain.doFilter执行完毕
          后执行SecurityContextHolder.clearContext().
  
 6.2 LogoutFilter 构造函数有两个参数:  1.退出后转向的url;  2.LogoutHandler
   如果请求不以"/j_acegi_logout"结束,则chain.doFilter,否则从6.1中的SecurityContextHolder
   中得到Authentication,并传入Authentication调用每一个LogoutHandler.logout方法。可以配置的
   LogoutHandler有:
   6.2.1 TokenBasedRememberMeServices.logout就是把cookie删掉
   6.2.2 SecurityContextLogoutHandler.logout就是执行SecurityContextHolder.clearContext()

 6.3 AuthenticationProcessingFilter 需要配置authenticationManager属性:
   authenticationManager->ProviderManager.它需要配置providers属性,可配置如下三个:
       6311 AnonymousAuthenticationProvider.authenticate(Authentication) 比较HashKey是否相同
       6312 RememberMeAuthenticationProvider.authenticate(Authentication) 比较HashKey是否相同
     以上两个如果HashKey不相同,则异常,相同则返回Authentication
       6313 daoAuthenticationProvider-->DaoAuthenticationProvider:三个属性:
     userDetailsService:必须设置项-->JdbcDaoImpl.通过username查询用户权限
     userCache-->EhCacheBasedUserCache(cache:resourceCacheBackend,
     cacheManager-->EhCacheManagerFactoryBean,resourceCache)
     passwordEncoder-->Md5PasswordEncoder
       AuthenticationProcessingFilter执行时先获得客户端传入的用户和密码,构造Authentication实例
       UsernamePasswordAuthenticationToken,并通过ProviderManager验证是否正确。正确则转入成功页
       面,否则错误页面。当配置了多个providers的时候,顺序验证,成功则不再调用下一个provider.

       DaoAuthenticationProvider.authenticate方法流程是:
  1 从参数authentication中获取userName
  2 从userCache中根据userName获取userDetails
  3 如果userDetails为空,则调用retrieveUser方法通过userDetailsService.loadUserByUsername
    获取userDetails. loadUserByUsername方法里面做了什么呢?
    a:根据usersByUsernameQuery查询语句查询出用户信息List,并获得第一个记录.是User类
    b:根据authoritiesByUsernameQuery查询语句查询出用户的角色信息,保存在GrantedAuthorityImpl
      类List中,GrantedAuthorityImpl是GrantedAuthority的实现类,getAuthority()方法将返回
      role_name字段的值.然后将角色信息List转化成GrantedAuthority[]数组
    c:构造new User[是UserDetails的实现类]返回。包含用户名、密码、角色GrantedAuthority[]数组。
  4 额外的additionalAuthenticationChecks。校验密码等
  5 用user置入userCache中
  6 返回UsernamePasswordAuthenticationToken[Authentication的实现类],包括角色信息
  7 这期间如果有异常,则抛出异常。因此最后返回了Authentication表明验明正身了。

 6.4 SecurityContextHolderAwareRequestFilter 将servletRequest包装成SavedRequestAwareWrapper类,然后
     再正常调用servlet

 6.5 RememberMeProcessingFilter 通过rememberMeServices.autoLogin方法获得Authentication(cookie而来),
     再调用authenticationManager.authenticate方法,SecurityContext中再保存该Authentication
 
 6.6 anonymousProcessingFilter SecurityContext中保存一个AnonymousAuthenticationToken

 6.7 ExceptionTranslationFilter 属性有:
     AuthenticationProcessingFilterEntryPoint[具备属性:loginFormUrl,forceHttps],
     AccessDeniedHandlerImpl[具备属性:errorPage]
    
     该filter先调用chain.doFilter,并捕获该方法出现的异常。出现异常后,根据异常的
     类型做如下处理:
     如果是AuthenticationException或AccessDeniedException,则根据定义的loginFormUrl,将页面转向到该url
     否则执行accessDeniedHandler.handle方法,转到errorPage页面
 
 6.8 FilterSecurityInterceptor 该filter也是个重量级的filter,主要是个拦截器的作用。
  该filter需要配置以下属性:
   6.8.1 authenticationManager 同6.3.1 校验密码并获取角色信息
   6.8.2 objectDefinitionSource
   6.8.3 accessDecisionManager-->AffirmativeBased[具备两个属性:allowIfAllAbstainDecisions,decisionVoters]
  而decisionVoters可以配置为
       6821 RoleVoter 根据ROLE_来确定是否通过
       6822 AuthenticatedVoter
  
   FilterSecurityInterceptor.doFilter中做了什么呢?首先将requet,response,chian封装成FilterInvocation fi,
   然后执行以下 a:beforeInvocation,  b:fi.getChain().doFilter(...),  c:afterInvocation  三个方法
     A.beforeInvocation(FilterInvocation)方法返回值是InterceptorStatusToken,这个方法都做了什么呢?以下
       1-6点说明了它的运行逻辑.
  1 从6.8.2中配置的objectDefinitionSource,调用其getAttributes(fi)方法.getAttributes(fi)方法中
    调用fi.getRequestUrl()得到url,然后调用objectDefinitionSource.lookupAttributes(url).
    lookup方法就可以从数据库resource表中获取url对应的resource.
  2 将1中查询出来的resource与url逐一比较,如果匹配,则获得相应GrantedAuthority[]即roles
  3 将2中GrantedAuthority[]逐一getAuthority后根据','分隔拼凑成字符串authStr,并且new
    ConfigAttributeEditor(),再调用ConfigAttributeEditor.setAsText(authStr)和
    ConfigAttributeEditor.getValue(),将value强制转化成ConfigAttributeDefinition返回.
    setAsText方法里面:new ConfigAttributeDefinition,再将authStr拆分成数组,逐一调用
    addConfigAttribute方法将SecurityConfig(auth) add到ConfigAttributeDefinition中,再
    setValue将ConfigAttributeDefinition设置为value.因此调用getValue的返回值可以强制转化为
    ConfigAttributeDefinition类
  4 通过SecurityContextHolder.getContext().getAuthentication()获取Authentication.此处根据配置
    及Authentication.isAuthenticated()判断可能会再次调用 authenticationManager[6.8.1中配置]的
    authenticate方法
  5 调用accessDecisionManager.decide(authenticated, FilterInvocation, ConfigAttributeDefinition)。
    下面分析一下AffirmativeBased这个decisionManager是如何decide的?
    a: 对AffirmativeBased配置的每一个decisionVoter执行:调用voter.vote(Authentication,obj,
       ConfigAttributeDefinition)获取是通过还是denny还是弃权
    b: 如果denny的数量>0,则异常不通过,如果有一个通过的则decide方法完成返回
    c: 根据AffirmativeBased的allowIfAllAbstainDecisions("如果全部弃权则通过")属性,
       如果false,则抛出异常
       RoleVoter.vote方法:从ConfigAttributeDefinition获取每一个ConfigAttribute.getAttribute(),
       判断是否以"ROLE_"开头:
        如是,检查Authentication中有没有与之匹配的,有则返回通过,无则返回denny
        如不是,则返回弃权.
      AuthenticatedVoter.vote方法:从ConfigAttributeDefinition获取每一个ConfigAttribute,调用
      getAttribute(),判断是否是:
      IS_AUTHENTICATED_FULLY:是则满足以下情况返回通过:
         **.既不是RememberMeAuthentication也不是AnonymousAuthenticationToken的实例
      IS_AUTHENTICATED_REMEMBERED:是则满足以下任一情况返回通过:
         a*.Authentication是RememberMeAuthenticationToken的实例
         b*.既不是RememberMeAuthentication也不是AnonymousAuthenticationToken的实例
      IS_AUTHENTICATED_ANONYMOUSLY:是则满足以下任一情况返回通过:
         a*.Authentication是AnonymousAuthenticationToken的实例
         b*.既不是RememberMeAuthentication也不是AnonymousAuthenticationToken的实例
         c*.Authentication是RememberMeAuthenticationToken的实例      
      以上3种情况的判断中,如果依然没有return通过,则只要有一个Attribute()与以上3个相同,则返回
      denny,否则弃权.
    
  6 根据FilterSecurityInterceptor是否配置了runAsManager,
    如配置了,则设置SecurityContextHolder中的Authentication为runAsManager.buildRunAs方法返回值
    并返回InterceptorStatusToken.contextHolderRefreshRequired=true
    如没有配置则返回InterceptorStatusToken.contextHolderRefreshRequired=false

     B.fi.getChain().doFilter servlet-api的doFilter方法

     C.afterInvocation(InterceptorStatusToken,returnObject)方法都做了什么呢?以下1-6点说明了它的运行逻辑
  1 如果InterceptorStatusToken==null,则返回returnObject
  2 token.isContextHolderRefreshRequired(),是则SecurityContextHolder中的Authentication设置为
    token.getAuthentication()
  3 如果afterInvocationManager配置了,则调用afterInvocationManager.decide方法并返回decide的返回值.

注意:
 TokenBasedRememberMeServices.key  与  RememberMeAuthenticationProvider.key 需要一致
 AnonymousProcessingFilter.key  与   AnonymousAuthenticationProvider.key  需要一致

 
springside春天的的springside-2.0提供的bookstore例子中采用自定义的DBFilterInvocationDefinitionSource作为
 FilterSecurityInterceptor.objectDefinitionSource属性.它继承自AbstractFilterInvocationDefinitionSource类
 lookupAttributes(String url)方法被getAttributes(fi)调用.并且自定义了AcegiCacheManager和AcegiCacheManagerImpl
 类实现缓存。AcegiCacheManagerImpl有几个方法需要说明一下:
 1 initUserCache():获取所有用户以及用户角色,将角色转化为GrantedAuthority数组再构造UserDetails实例User置入
   userCache
 2 initResourceCache():获取所有resource以及对应角色,将角色转化为GrantedAuthority数组再构造ResourceDetails
   实例Resource置入resourceCache.
   以上的cache采用了ehcache.cache元素Element类都采用了res_string作为key.

 lookupAttributes(url)中执行了如下过程:
 1 acegiCacheManager.getUrlResStrings()获取所有resStr.即查询resource表中res_type="URL"的所有记录的res_string字段
 2 比较所有res_string与url是否匹配,如果找到匹配项,立刻根据res_string获取对应的ResourceDetails接口即Resource实现类
 3 调用Resource.getAuthorities()将数组以,分隔转化为字符串并通过ConfigAttributeEditor类转化为ConfigAttributeDefinition
   返回给调用方.它又会被accessDecisionManager.decide方法作为参与调用,decide方法已经在前面介绍过了.

 
几个词汇:                    对应数据库内容
Authentication:证明     用户表
Authority:权限              可能是role.每个角色的权限在acegi中描述为资源resource.而资源是通过url来区分的.
Credential:信任状      密码
Principal:负责人        用户名


因此重点就在于 role,resource等表数据内容的编制了!!

分享到:
评论
1 楼 huzhiyong56 2011-11-04  
看着头都晕了。。。

相关推荐

    acegi+cas整合工程及说明1

    将acegi和cas的war包修改并整合测试,里面包含所需依赖包。经过分卷压缩一共3个文件.

    Acegi_使用.doc

    认证授权流程 9 授权机制 12 SkyonFramework 对 Acegi 的扩展 13 FilterInvocationDefinitionSourceDynamicExtentionEditor 14 FilterInvocationDefinitionSourceHolder 15 ResourceMappingProvider 17 ...

    Acegi1.0.7文档

    Acegi1.0.7文档,详细介绍了Acegi的原理、流程及各组件的功能等。

    acegi-security-1.0.6.tar.bz2

    Acegi的特点就是有很多的过滤器:不过我们也用不到这么多的过滤器,只是可以把它们看作为一个个的模块,在用的时候加上自己用的着的即可,由于认证的流程的方面比较复杂导致它的配置很复杂,如果能摸清它的工作原理...

    spring web flow demo

    • 与 Spring Security (原 Acegi Security )整合 只需将某个 flow 声明为“ secured ”,即可利用 Spring Security 来确定当前用户是否有权限运 行 flow 、激发事件等等。 • 更简洁的配置 官方的数据说同一个 ...

    Spring Security introduction

    本文介绍spring Security的框架和CAS认证的流程

    Grails 中文参考手册

    6.5.6 子流程和会话 6.6 过滤器 6.6.1 应用过滤器 6.6.2 过滤器的类型 6.6.3 过滤器的功能 6.7 Ajax 6.7.1 用Prototype实现Ajax 6.7.1.1 异步链接 6.7.1.2 更新内容 6.7.1.3 异步表单提交 6.7.1.4 Ajax事件 6.7.2 用...

    Grails权威指南

    第1章 寻找grails之旅  1.1 java的困惑  1.2 webc2.0时代  1.3 java的力量  1.4 什么是grails ... 11.5 在grails中使用acegi  11.6 使用xfire创建soap服务  11.7 本章小结

    Spring Security 中文教程.pdf

    16. acegi到spring security的转换方式 16.1. Spring Security是什么 16.2. 目标 16.3. 步骤 16.4. 总结 V. 高级话题 17. 领域对象安全(ACLs) 17.1. 概述 17.2. 关键概念 17.3. 开始 18. 预认证...

    Spring Security-3.0.1中文官方文档(翻译版)

    16. acegi 到spring security 的转换方式 16.1. Spring Security 是什么 16.2. 目标 16.3. 步骤 16.4. 总结 V. 高级话题 17. 领域对象安全(ACLs) 17.1. 概述 17.2. 关键概念 17.3. 开始 18. 预...

    java开源包1

    Spring4GWT GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java...

    java开源包11

    Spring4GWT GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java...

    java开源包2

    Spring4GWT GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java...

    java开源包3

    Spring4GWT GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java...

    java开源包6

    Spring4GWT GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java...

    java开源包5

    Spring4GWT GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java...

    java开源包10

    Spring4GWT GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java...

    java开源包4

    Spring4GWT GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java...

    java开源包8

    Spring4GWT GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java...

    java开源包7

    Spring4GWT GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java...

Global site tag (gtag.js) - Google Analytics