您的位置:首页 > 编程语言 > Java开发

spring AOP + 自定义注解实现权限控制小例子

2017-02-16 20:28 537 查看
今天看了一下黑马程序员的视频,上面讲到一个使用spring AOP + 自定义注解的方式来实现权限控制的一个小例子,个人觉得还是可以借鉴,整理出来与大家分享。

需求:service层有一些方法,这些方法需要不同的权限才能访问。

实现方案:自定义一个PrivilegeInfo的注解,使用这个注解为service层中的方法进行权限配置,在aop中根据PrivilegeInfo注解的值,判断用户是否拥有访问目标方法的权限,有则访问目标方法,没有则给出提示。

关键技术:自定义注解及注解解析,spring aop

最终实现后的目录结构:



具体步骤: 

下面我们来具体实现这个需求。 

首先来实现这个自定义注解,为了简单起见,我们演示的这个注解,只是给了一个权限名的属性。
package privilege.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 权限注解
* @author Minhellic
*
*/
@Target(ElementType.METHOD)//这个注解是应用在方法上
@Retention(RetentionPolicy.RUNTIME)
public @interface PrivilegeInfo {
/**
* 权限的名称
* @return
*/
String value() default "";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

为这个自定义的注解,写一个解析器,主要是用于返回目标方法上的注解PrivilegeInfo设置的value值
package privilege.annotation;

import java.lang.reflect.Method;

/**
* 权限注解解析器
* 这个解析器的主要功能,是解析目标方法上如果有PrivilegeInfo注解,那么解析出这个注解中的value值(权限的值)
* @author Minhellic
*
*/
public class PrivilegeAnnotationParse {
/**
* 解析注解
* @param targetClass 目标类的class形式
* @param methodName 在客户端调用哪个方法,methodName就代表哪个方法 
* @return
* @throws Exception
*/
public static String parse(Class targetClass, String methodName) throws Exception {
String methodAccess = "";
/*
* 为简单起见,这里考虑该方法没有参数
*/
Method method = targetClass.getMethod(methodName);
//判断方法上是否有Privilege注解
if (method.isAnnotationPresent(PrivilegeInfo.class)) {
//得到方法上的注解
PrivilegeInfo privilegeInfo = method.getAnnotation(PrivilegeInfo.class);
methodAccess = privilegeInfo.value();
}
return methodAccess;
}
}
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
29
30
31
32
33
34
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
29
30
31
32
33
34

自定义的注解和解析器有了,我们把对应的Service层写出来,并在需要使用这个注解配置权限的方法上,添加上这个注解。我们知道Service层是由接口和实现类组成,这是规范,虽然对我们这次的演示没有什么作用,但我们还是遵守一下:

接口源码:
package privilege.service;

/**
* 用户业务接口
* @author Minhellic
*
*/
public interface FirmService {
/**
* 在需要权限的目标方法上,使用PrivilegeInfo注解,配置权限为save
*/
public void save();
/**
* 在需要权限的目标方法上,使用PrivilegeInfo注解,配置权限为update
*/
public void update();
/**
* 不需要权限的目标方法上,则不添加PrivilegeInfo注解
* 在切面中,默认用户拥有权限
*/
public void get();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

实现类源码:
package privilege.service.impl;

import privilege.annotation.PrivilegeInfo;
import privilege.service.FirmService;

/**
* 用户业务实现
* @author Minhellic
*
*/
public class FirmServiceImpl implements FirmService {

/**
* 在需要权限的目标方法上,使用PrivilegeInfo注解,配置权限
*/
@Override
@PrivilegeInfo("save")
public void save() {
System.out.println("FirmServiceImpl.save()");

}

/**
* 在需要权限的目标方法上,使用PrivilegeInfo注解,配置权限
*/
@Override
@PrivilegeInfo("update")
public void update() {
System.out.println("FirmServiceImpl.update()");

}

/**
* 不需要权限的目标方法上,则不添加PrivilegeInfo注解
* 在切面中,默认用户拥有权限
*/
@Override
public void get() {
System.out.println("FirmServiceImpl.get()");

}
}
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

为了更好地管理权限,我们专门建立一个权限类,当然,为了简单,这里还是只封装了权限的名称
package privilege.userprivilege;

/**
* 封装用户权限
* 为简单,只封装了权限的名称
* @author Minhellic
*
*/
public class FirmPrivilege {
/**
* 用户权限的名称
*/
private String value;

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

public FirmPrivilege(String value) {
this.value = value;
}

public FirmPrivilege() {
}

}
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
29
30
31
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
29
30
31

现在Service层和注解都已经有了,就需要写切面的代码了。 

在切面中,我们使用环绕通知,在调用目标方法之前,我们先用目标方法上的PrivilegeInfo注解配置的权限,与用户拥有的权限进行匹配,如果匹配成功,则认为用户拥有这个目标方法的权限,则调用目标方法,否则,给出提示信息,不调用目标方法。 

这里之所以不使用前置通知的方式来匹配权限,就是因为前置通知虽然也可以检查,但是无论检查是否通过,目标方法都会被调用,起不到根据权限来控制目标方法调用的目的。
package privilege.aspect;

import java.util.List;

import org.aspectj.lang.ProceedingJoinPoint;

import privilege.annotation.PrivilegeAnnotationParse;
import privilege.userprivilege.FirmPrivilege;

/**
* 权限检查切面
* 根据用户原有的权限,与目标方法的权限配置进行匹配,
* 如果目标方法需要的权限在用户原有的权限以内,则调用目标方法
* 如果不匹配,则不调用目标方法
* @author Minhellic
*
*/
public class PrivilegeAspect {
/**
* 用户本身的权限
*/
private List<FirmPrivilege> privileges;

public List<FirmPrivilege> getPrivileges() {
return privileges;
}

public void setPrivileges(List<FirmPrivilege> privileges) {
this.privileges = privileges;
}

/**
* aop中的环绕通知
* 在这个方法中检查用户的权限和目标方法的需要的权限是否匹配
* 如果匹配则调用目标方法,不匹配则不调用
* @param joinPoint 连接点
* @throws Throwable
*/
public void isAccessMethod(ProceedingJoinPoint joinPoint) throws Throwable {
/**
* 1.获取访问目标方法应该具备的权限
*  为解析目标方法的PrivilegeInfo注解,根据我们定义的解析器,需要得到:目标类的class形式 方法的名称
*/
Class targetClass = joinPoint.getTarget().getClass();
String methodName = joinPoint.getSignature().getName();
//得到该方法的访问权限
String methodAccess = PrivilegeAnnotationParse.parse(targetClass, methodName);
/*
* 2.遍历用户的权限,看是否拥有目标方法对应的权限
*/
boolean isAccessed = false;
for (FirmPrivilege firmPrivilege : privileges) {
/*
* 如果目标方法没有使用PrivilegeInfo注解,则解析出来的权限字符串就为空字符串
* 则默认用户拥有这个权限
*/
if ("".equals(methodAccess)) {
isAccessed = true;
break;
}
/*
* 用户原有权限列表中有的权限与目标方法上PrivilegeInfo注解配置的权限进行匹配
*/
if (firmPrivilege.getValue() != null &&
firmPrivilege.getValue().equalsIgnoreCase(methodAccess)) {
isAccessed = true;
break;
}
}
/*
* 3.如果用户拥有权限,则调用目标方法 ,如果没有,则不调用目标方法,只给出提示
*/
if (isAccessed) {
joinPoint.proceed();//调用目标方法
} else {
System.out.println("你没有权限");
}
}
}
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

最后,配置好spring的配置文件,要使用spring aop,配置文件中,必须包含有aop的命名空间,并引入相应的xsd



配置文件的源码为:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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"> 
<bean id="firmService" class="privilege.service.impl.FirmServiceImpl"></bean>
<bean id="privilegeAspect" class="privilege.aspect.PrivilegeAspect"></bean>

<!-- 配置切面 -->
<aop:config>
<!--
切入点表达式,确认目标类
privilege.service.impl包中的所有类中的所有方法
-->
<aop:pointcut expression="execution(* privilege.service.impl.*.*(..))" id="perform"/>
<!-- ref指向的对象就是切面 -->
<aop:aspect ref="privilegeAspect">
<!-- 环绕通知 -->
<aop:around method="isAccessMethod" pointcut-ref="perform"/>
</aop:aspect>
</aop:config>

</beans>
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
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

所有的工作都已做好,我们来测试一下:
package privilege.test;

import java.util.ArrayList;
import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import privilege.aspect.PrivilegeAspect;
import privilege.service.FirmService;
import privilege.userprivilege.FirmPrivilege;

/**
* aop+注解权限控制测试类
*
* @author Minhellic
*
*/
public class PrivilegeTest {
/**
* 客户端直接调用这个Service的方法,而不需要关心权限问题
*/
private FirmService firmService;

/**
* 在初始化方法中,初始化firmService
* 同时为用户赋上原始权限,这个在项目中,会使用别的方式实现,这里只是模拟,就不搞那么复杂了
*/
@Before
public void init() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
firmService = (FirmService) context.getBean("firmService");

/*
* 给用户添加默认权限
*/
PrivilegeAspect privilegeAspect = (PrivilegeAspect) context.getBean("privilegeAspect");
List<FirmPrivilege> privileges = new ArrayList<FirmPrivilege>();
//privileges.add(new FirmPrivilege("save"));
privileges.add(new FirmPrivilege("update"));
privilegeAspect.setPrivileges(privileges);
}

/**
* 客户端直接调用Service中的方法,而不需要关心权限问题,会有切面去做
*/
@Test
public void test() {
firmService.save();
firmService.update();
firmService.get();
}
}
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

运行test方法,根据控制台的输出结果,就可以看到权限控制是起到了作用,因为用户初始权限中的save权限被注释掉,则用户不会拥有save权限,调用save方法时,提示没有权限。 

从上面的测试方法可以看出,使用了aop之后,我们只需要关心主要业务,而不需要再分心去管理权限问题。



这篇博客,虽是我本人整理,但所有的思路及实现方式,都来源于黑马程序员的视频,算是半原创吧。如写得很烂,欢迎大神们喷,但请不要辱骂。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: