`
studying_
  • 浏览: 165607 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

解析Acegi Security for Spring

阅读更多

偶对Acegi Security System景仰已久,它利用了Spring提供的IOC和AOP的机制,实现了一个安全架构,让我们的业务逻辑和安全检查逻辑完全解耦,所有的安全逻辑只要通过Spring的标准配置文件的定义就可实现了。不过说得简单,真的每次拿起Acegi Security System来看的时候,感觉就象云里雾里,如果不仔细看它的文档,再深入研究的话,根本就看不出头绪。某种程度上来说,学习Acegi Security System似乎比学习Spring还要难。

最近偶沉下心来,花了好多时间,看了文档和主要的源代码,终于摸出了点头绪。为什么Acegi Security System会这么难?我觉得原因有以下几个:

一、Acegi Security System的配置文件是用的是Spring的标准配置。某种程度上,它的配置是针对Acegi的设计和实现都很了解的人,而不是针对最终用户的。配置文件里那么多的类,那么复杂的对象关联,如果没有对Acegi的设计有深入了解,只能看得云里雾里。也许我们能在Acegi Security上包一层,定义自己的配置文件来简化Acegi Security。但是仔细想想,Acegi Security的设计是有道理的,Security本来就不是一件简单的事,我们不能指望一个新手来完成所有Security的配置。而利用Spring的配置文件却能达到最大的灵活性,利用Spring的IOC机制,所有的配置都可以提供我们的自定义的实现。一旦当我们对Acegi有了深入了解之后,所有的这些配置就会变得很自然了。


二、Acegi的文档相当的好,它将Acegi的内部实现、类之间的关联,实现的目标进行了很深入的分析。但是却忽略了一点,它的整个文档是从设计出发,而不是从需求出发的,整个文档似乎更关注于Acegi Security是怎么设计的,而不是怎么用的。当然我承认如果要深入了解Acegi,我们必须要知道它的设计和实现,但是对于一个新手来说,假如我们能马上知道Acegi的安全认证执行流程是怎么样的,再通过个认证流程展开说明authentication和authorization所涉及到的东西岂不更好?

今天先写到这里啦,过两天再写一下Acegi Security的认证流程吧,还是蛮好玩的,虽然写起来可能会有点累。

我发现我给自己设了个陷阱,对于Acegi Security System的解析并不是光写个安全认证的流程就能说清楚的。虽然看起来Acegi的文档确实挺累,但是当我动笔写时却发现要写得比这个文档更好还是挺难的。毕竟我们不能让本来很难的事情一下子就变得很简单了,我也是昨天重要看了一遍了Acegi Security System的源代码,才发现我自己有明白了好多不明白的东西。但是我仍然希望我写的东西能够帮助那些正在学习和研究Acegi Security System的人们,所以我又开始动笔了,呵呵!

虽然我不想介绍太多与安全认证流程无关的东西,但是有些东西的讲解却是必不可少的,因为没有这些基本的概念和类,后面的东西就没法说清了。不过对于具体的类、类图和它们之间的关联,我还是推荐去看Professional Java Development with the Spring Framework里关于Acegi的那一章,对于想读Acegi的源代码和了解Acegi内部设计的人来说,这一章真是太有用了。



本来不想贴这幅类图的,毕竟有盗版的嫌疑,但是发现有些东西不贴出来又说不清楚。整个认证功能的核心是Authentication接口,我们只把Authentication想象成一个包含用户基本信息的类就行了,它里面放了用户名、密码、这个用户的具体权限有哪些(当然具体的东西是由它的子类UsernamePasswordAuthenticationToken实现的,其它类的存放的信息稍有不同,毕竟验证的方式还是多种多样的,我这里描述的所有东西Acegi最常用的实现方式,而不考虑其它的东西,否则只会分散注意力,看了之后一头雾水)。Authentication里还包含了一个GrantedAuthority接口,今天暂且不讨论Authorization的问题了,毕竟它与验证的流程是不相关的,而具体的细节又极复杂。


我们通过AuthenticationManager这个接口来验证这个Authtication对象的合法性,真正的验证过程看上去很悬,其实最后的实现无非是去数据库搜索一下是否存在这个用户,密码是否匹配(说的仍然是最常用的实现方式,呵呵),只是它设计的时候对象的关联比较巧妙,类图看熟了就会觉得没什么,真正查数据库的那个类是DaoAuthenticationProvider。这个接口真正巧妙的地方是它执行后返回的结果是一个Authentication,而不是用一个布尔值来表示验证成功或失败。Why? 记得当年在JavaEye上有个讨论Exception的贴子,robbin认为用户安全认证应该用Checked Exception来控制流程,更多的人认为密码错误是正常的事件流,返回布尔值更为恰当,这里不讨论这两种观点的对错,毕竟每个人站的角度不同,具体的情况也不同。

但是如果要实现认证的透明性,我们要用到的却是unchecked exception,这个Exception叫做AuthenticationException(如果是authorization会抛出AccessDeniedException,不过道理类似),这真是奇妙的事,因为Exeception是可以传递的,如果在本类里面处理不了这个Exception,我们就会将这个Exception抛给调用这个类的类,如此循环,直到有一个类可以处理它为止(对于Web来说应该是在页面上提示登录出错信息)。没想到Exception的这个种特性用在安全认证里如此的合适,权限不够?用户密码不对?我才不管呢,只要抛出个异常,最后会有人把它接住处理掉的。当然这里的另一个条件是Unchecked,只有unchecked才不会导致接口的污染,才能达到完全的透明性。


有了前面的基础接口,我们要提出下一个问题了,这个Authentication对象应该存放在哪里?几乎每个做过Web应用的人告诉我:HttpSession。Acegi也不例外,虽然还有其它的存放地点(要跟具体的Web容器结合,会导致不兼容性,一般不提倡用)。但是我们马上会问下一个问题:我们怎么得到Authentication对象?当然我们可以去HttpSession里去取,但是很多时候我们在验证的是与Web层无关的(比如要用户调用Service层的权限或Domain Object的权限)。我们必须用与Web无关的API来获取Authentication。这让我们想起了什么?对,是Webwork,Webwork的Action是完全与Web API无关的,它的Request里的参数自动populate成了Action的属性,但是我们仍然可以通过ActionContext来获取这些信息。它是怎么做到的?是Threadlocal,因为每个Web Request都会使Web容器生成一个新的线程来处理它的这个特性使我们可以将这些Web的数据一股脑塞给Threadlocal。这个存放Authentication的对象叫做SecurityContext,而把SecurityContext放入Threadloal或取出的则是SecurityContextHolder,下面就是它的类图:



讲完这些基础设施,我们就可以看具体的认证流程啦,真正的认证是一串的Filter(对Servlet容器熟悉的人应该都不要解释了)。只不过Acegi在这些Filter上稍微玩的点花招,因为一般的Filter是不能定义在Spring的ApplicationContext里的,所以这用了一个代理的Filter对象FilterToBeanProxy将Filter操作Delegate给定义在ApplicationContext里的Filter。这个似乎跟主题无关,不过如果以后真有类似的需求的话,这倒是蛮管用的一招。当然还有FilterChainProxy,它把一串的Filter全部定义在一个bean里,使配置简化了好多,呵呵。


我们来看看Filter的一头一尾。头是httpSessionContextIntegrationFilter,其实它的功能前面已经讨论过了,在执行前把HttpSession里的Authentication取出来放到SessionContextHolder(Threadlocal)里,在执行完毕后,把Authentication塞回到HttpSession。真正的实现代码有一堆,不过核心的代码无非就这么几行:

Object contextFromSessionObject = httpSession.getAttribute(ACEGI_SECURITY_CONTEXT_KEY);

SecurityContextHolder.setContext((SecurityContext) contextFromSessionObject);

chain.doFilter(request, response);

httpSession.setAttribute(ACEGI_SECURITY_CONTEXT_KEY,,SecurityContextHolder.getContext());


Filter的尾是securityEnforcementFilter,它的工作就是进真正的用户认证的流程控制了,具体的认证工作它会delegate给FilterSecurityInterceptor,但是不管怎么认证,结果无非是认证成功或失败,securityEnforcementFilter只要管抓住异常再转到特定的页面就行了。下面就是这个类的信心代码:

try {

filterSecurityInterceptor.invoke(fi);

}

} catch (AuthenticationException authentication) {

sendStartAuthentication(fi, authentication);

} catch (AccessDeniedException accessDenied) {

sendAccessDeniedError(fi, accessDenied);

}


我们再来看看用户登录是怎么干的吧。Acegi的用户登录很有意思,为了不让用户写任何这方面的代码,它也直接把这个功能放到Filter里了,这个Filter叫做authenticationProcessingFilter。这个Filter的要求是页面上的form的Action名字,登录名、密码的字段名都是定死的。一个简单的页面就这些啦:

<form action="<c:url value=' j_acegi_security_check '/>" method="POST">

<input type='text' >

<input type='password' >

<input type="submit">

</form>

记住action必须叫j_acegi_security_check,用户名必须叫j_username,密码必须叫j_password。呵呵,代码就不写了,无非就是判断只要Action名字对,就把用户名、密码取出来认证一把,最后把认证成功的Authetication对象填到SecurityContextHolder里再导到相应页面,认证失败就导到出错页面。


呵呵,好了,认证的核心过程其实就这些了,虽然还有其它的好多的Filter和关联,但是当我们把核心的内容分析清楚之后,其它的都不难了。(Authorization是例外,有些部分我还没看明白)。

  • 大小: 70 KB
  • 大小: 22.3 KB
分享到:
评论

相关推荐

    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...

    java开源包9

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

    java开源包101

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

    Java资源包01

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

    JAVA上百实例源码以及开源项目

    百度云盘分享 简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级、中级、高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情、执着,对...

    JAVA上百实例源码以及开源项目源代码

    Java 源码包 Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 ...

Global site tag (gtag.js) - Google Analytics