用简单Annotation为Spring Bean增加权限认证

最近在做dwr+Spring的一个应用,直接把Spring的Bean暴露给dwr生成前端的STUB。这样的话,为dwr调用做权限认证,基本上要么用Filter,要么用切面。
用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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值