基于 Annotation 的 Spring AOP 权限验证方法的实现
1. 配置 applicationContext
在 Spring 中支持 AOP 的配置非常的简单,只需要在 Spring 配置文件 applicationContext.xml 中添加:
<aop:aspectj-autoproxy/>
同时在 applicationContext.xml 的 schema 中配置:
清单 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
< beans xmlns = "http://www.springframework.org/schema/beans" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns:p = "http://www.springframework.org/schema/p" xmlns:aop = "http://www.springframework.org/schema/aop" xmlns:context = "http://www.springframework.org/schema/context" xmlns:tx = "http://www.springframework.org/schema/tx" xmlns:cache = "http://www.springframework.org/schema/cache" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd"> |
在配置时,我们需要将引用的 jar 包放置在 WEB-INF/lib 目录下面:
需要的 jar 包有
配置这些之后 Spring AOP 就可以开始工作了。
2. 定义 Annotation
首先,我们定义一个常量来表示用户是否登录:
清单 2
1
2
3
4
5
6
|
package com.example.myenum; public enum ISLOGIN { YES, LOGOUT, NO } |
这里也可以选择不使用 enum,UserAccessAnnotation 中的 isLogin() 方法也可以返回整数或 String 类型,返回类型并没有限制。常量定义之后,我们再定义 Annotation,在 UserAccessAnnotation 中定义 isLogin(),表示用户是否已经登录:
清单 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package com.example.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import com.example.myenum.ISLOGIN; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface UserAccessAnnotation { /** * User has been login or not. * */ ISLOGIN isLogin(); } |
定义好之后,这个 Annoatation 将可以被放置在需要验证用户是否登录的方法前面,就像下面这样:
清单 4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package com.example.aspect; public class OrderAction extends BaseAction{ …… @UserAccessAnnotation(>isLogin=ISLOGIN.YES) public String Order(){ try{ Boolean result = orderService.order(Quote quote); if(result) return SUCCESS; }catch(Exception e) { logger.debug(e); this.addActionError(getText("user_no_permission_error")); } return INPUT; } …… } |
在这里我们使用 UserAccessAnnotation 来表示需要在 Order 方法执行之前判断用户是否已经登录,如果没有登录,在 struts2 中,通过下面定义的 Exception 的捕获机制,将页面转到登录页面。
3. 在 applicationContext.xml 中定义 Aspect
清单 5
1
2
3
|
< bean id = "permission" class = "com.example.aspect.PermissionAspect" scope = "prototype" > < property name = "authService" ref = "AuthService" /> </ bean > |
我们要在 Spring 中定义 PermissionAspect。在 Struts+Spring 架构中可以把 Aspect 看作是一个 Action,只不过 Aspect 是其他 Action 的前提条件或者结束动作。Aspect 定义中的 Service 属性和 Action 中的 Service 属性没有任何区别。这里我们用 AuthService 类来实现判断用户是否已经登录的逻辑。
4. 定义 PointCut
清单 6
1
2
3
4
5
6
7
8
9
10
|
@Aspect public class SystemArchitecture { /** * A Join Point is defined in the action layer where the method needs * a permission check. */ @Pointcut("@annotation(com.example.annotation.UserAccessAnnotation)") public void userAccess() {} } |
PointCut 即切入点,就是定义方法执行的点,before、after 或者 around。 一般情况下,我们把 PointCut 全部集中定义在 SystemArchitecture 类中,以方便修改和管理。
当实现 Aspect 时可以很方便的使用我们在 SystemArchitecture 中定义的 PointCut。
5. 实现 Aspect
清单 7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package com.example.aspect; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import com.example.annotation.UserAccessAnnotation; import com.example.base.action.BaseAction; import com.example.myenum.USERTYPE; import com.example.service.AuthService; @Aspect public class PermissionAspect extends BaseAction{ …… AuthService authService = null; @Before(value="com.example.aspect.SystemArchitecture.userAccess()&&"+ "@annotation(userAccessAnnotation)",argNames="userAccessAnnotation") public void checkPermission(UserAccessAnnotation userAccessAnnotation) throws Exception{ IsLogin isLogin = userAccessAnnotation.isLogin (); if(!authService.userLogin(user).equals(isLogin.toString())){ throw new NoPermissionException(getText("user_no_permission_error")); } } …… } |
在 checkPermission 方法前,我们首先定义 PointCut:
1
2
|
@Before(value="com.example.aspect.SystemArchitecture.userAccess()&&"+ "@annotation(userAccessAnnotation)",argNames="userAccessAnnotation"). |
argNames="userAccessAnnotation" 的意思是把 Annotation 当做参数传递进来,并判断用户登录状态是否与 Annotation 中的定义一致。如果不一致,就要抛出 NoPermissionException,通知系统该用户没有权限。