基于 Annotation 的 Spring AOP 权限验证方法的实现

Posted start枫

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于 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,通知系统该用户没有权限。

以上是关于基于 Annotation 的 Spring AOP 权限验证方法的实现的主要内容,如果未能解决你的问题,请参考以下文章

Spring系列基于Annotation(注解)的装配应用

基于 Annotation 的 Spring AOP 权限验证方法的实现

关于是不是使用基于 Annotation 的 spring boot graphql server 的指导

如果基于 Spring Annotation 的控制器在 jar 文件中,则它不起作用

基于Annotation的IOC 初始化

annotation