浏览 4634 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-03-16
最后修改:2009-04-01
用Filter有一个问题是,很多时候,我们并不需为每个调用增加权限认证,但这些请求,也会被拦截,所以不推荐Filter,就像不能因一个坏人而惩罚所有的好人。退一步说,我们可以用现成的Acegi,但配置文件也太麻烦了。而且,我对Acegi有很深的阴影,印象里唯一一个源文件使用四五十个字符变量名的开源项目。 Spring支持AspectJ风格的Annotation,但空方法的POINTCUT和个人感觉很复杂的匹配规则,都不太方便使用。 其实做一个基于Spring BEAN的动态代理,加上权限认证的切面很简单,问题是怎样把这个BEAN再放回到Spring 的容器里,因为BeanFactory更新Singleton Bean的方法是protected修饰的,上次写的方法就是扩展了BeanFactory,但扩展BeanFactory会引发另一个问题,无法把BeanFactory再放回到ServletContext中,因为Spring对WEB 的支持使用的是ApplicationContext的子类。如果扩展ApplicationContext同时,再对BeanFactory做一个Proxy也可以解决问题。但这样写,就太ugly了。 索性彻底一点好了。 最近真的喜欢上用TC来驱动了,测试用的case, /** * @author Chiron K */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"applicationContext.xml"}) public class AuthTest { @Autowired OtherService otherService = null; @Test public void testAuthFailed(){ FakeSession.login("user"); try{ otherService.sayHello(); fail(); }catch(Exception e){ if(e instanceof AuthException){ assertTrue(true); System.out.println(e.getMessage()); } } } @Test public void testAuthSuccess(){ FakeSession.login("admin"); try{ otherService.sayHello(); assertTrue(true); }catch(Exception e){ if(e instanceof AuthException){ fail(); System.out.println(e.getMessage()); } } } } 这里我们要进行权限测试的类是OtherService,这个类的sayHello方法要求用户必须拥有admin角色。第一个Case,我们登陆用户角色为user,不可以调用sayHello方法。第个Case登陆用户为admin,可以成功调用。 OtherService的接口. @Secure public interface OtherService{ @Auth("admin") String sayHello(); } 在这里我们定义了两个Annotation, @Secure标记是否需要为Bean做权限认证,@Auth更小粒度的标记方法和需要的角色。 OtherService的实现 @Component("OtherService") public class OtherServiceImpl implements OtherService{ public String sayHello() { System.out.println("hello world"); return "Hello world"; } } 这里还要对applicationContext.xml做一些修改, <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:auth="http://www.iyue.info/schema/auth" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.iyue.info/schema/auth http://www.iyue.info/schema/auth.xsd"> <auth:authenticator class="info.iyue.auth.aop.sample.SimpleAuthenticator"/> <context:annotation-config/> <context:component-scan base-package="info.iyue.auth.aop.simple"/> </beans> 新加了一个命名空间的处理,指定一个Authenticator,这里只做了一个简单的实现, /** * @author Chiron K */ public class SimpleAuthenticator implements Authenticator{ public void authenticate(String role) { if(!FakeSession.getRole().equals(role)){ throw new AuthException("User don't have enough privilege"); } } } FakeSession是对HttpSession的简单Mock, public class FakeSession { public static FakeSession session = new FakeSession(); private String role = null; public static void login(String role){ session.role = role; } public static String getRole(){ return session.role; } public void clear(){ session.role = null; } } 运行TestCase, 输出: User don't have enough privilege hello world 所以对于应用程序来说,只需要: 1 为需要的Spring 容器中的BEAN增加@Secure标记和为需要做权限认证的方法加@Auth标记和角色。 2 在applicationContext.xml中添加对authenticator的定义。 3 实现上一步中指定的authenticator,要求实现Authenticator接口。 很干净吧 这个实现其实主要也是参照了Spring AOP的实现。 通过NamespaceParser,读取到Authenticator的值,并把 自定义BeanFactoryPostProcessor和BeanPostProcessor注册到容器中。 BeanFactoryPostProcessor先于BeanPostProcessor触发,遍历标记有@Secure 的Bean, 当调用BeanPostProcessor.postProcessAfterInitialization时,为具有标记的Bean生成具有Authenticator切面的代理,Spring会更新容器。 具体的实现参附件。测试在sample下面, 依赖包spring2.5.3,spring-test,junit4.4 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2009-03-31
看不懂,请问@Auth("admin") 是如何起作用的?
|
|
返回顶楼 | |
发表时间:2009-03-31
有空去权限管理圈子看看, 也欢迎您将文章发布到改圈子。
http://accessmanager.group.iteye.com/ |
|
返回顶楼 | |
发表时间:2009-03-31
这个解法跟EJB的权限控制很想像啊,不过对EJB容器的依赖少了
|
|
返回顶楼 | |
发表时间:2009-04-01
有的人喜欢在一个action里写n个功能。
|
|
返回顶楼 | |
发表时间:2009-04-01
fangzhouxing 写道 看不懂,请问@Auth("admin") 是如何起作用的? @Auth为需要权限认证的方法做了一个标记,在SpringContainer初始化后, 把需要做权限认证的BEAN替换成自己生成的代理, @Auth的作用就是在生成代理的时侯,决定是不是要加进权限认证的切面, String role = null; if((role = methodNames.get(method.getName())) != null){ authenticator.authenticate(role); } return method.invoke(proxee, args); |
|
返回顶楼 | |
发表时间:2009-04-01
fangzhouxing 写道 看不懂,请问@Auth("admin") 是如何起作用的? @Auth为需要权限认证的方法做了一个标记,在SpringContainer初始化后, 把需要做权限认证的BEAN替换成自己生成的代理, @Auth的作用就是在生成代理的时侯,决定是不是要加进权限认证的切面, |
|
返回顶楼 | |
发表时间:2009-04-01
其实,自己写一个Aspect,切入到你要验证权限的地方。通过就proceed。失败就跳转或抛出异常就可以了。写aspect比写annotation还简单一些,也容易移植。
|
|
返回顶楼 | |
发表时间:2009-04-02
gty509 写道 其实,自己写一个Aspect,切入到你要验证权限的地方。通过就proceed。失败就跳转或抛出异常就可以了。写aspect比写annotation还简单一些,也容易移植。 其实就是自己写一Aspect,通过就proceed。失败就抛出异常.只是基于自定义的Annotation,而没有使用Spring提供AspectJ的Annotation,因为Aspect切面的定义比较繁琐.至于移植性的话,就要看annotation和xml配置的取舍了. |
|
返回顶楼 | |
发表时间:2009-04-02
角色写死在代码里,似乎不妥吧?
建议你采用spring security,比以前的acegi简化了许多 |
|
返回顶楼 | |