用简单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 {@AutowiredOtherService otherService = null;@Testpublic void testAuthFailed(){FakeSession.login("user");try{otherService.sayHello();fail();}catch(Exception e){if(e instanceof AuthException){assertTrue(true);System.out.println(e.getMessage());}}}@Testpublic void testAuthSuccess(){FakeSession.login("admin");try{otherService.sayHello();assertTrue(true);}catch(Exception e){if(e instanceof AuthException){fail();System.out.println(e.getMessage());}}}}
@Securepublic interface OtherService{@Auth("admin")String sayHello();}
@Component("OtherService")public class OtherServiceImpl implements OtherService{public String sayHello() {System.out.println("hello world");return "Hello world";}}
<?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.xsdhttp://www.iyue.info/schema/auth http://www.iyue.info/schema/auth.xsd"><auth:authenticator name="code">/** * @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");}}}
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;}}