MagicWolf

Spring MVC系列(三) 基于注解的权限控制

在Java EE项目中,权限控制是经常遇到的问题。尤其是在多角色的系统中,权限控制的粒度更细,也更为重要。

问题描述

系统中有三种角色,教师,管理员,学生,角色权限部分交叉。如果将权限控制的逻辑添加到每一个接口,代码冗杂且不易扩展,维护。我们将权限控制部分单独分离出来,并使用非侵入式的方法为每一个接口添加权限。

解决方案

  • 注解:利用Java的注解机制,在运行时得到接口的权限信息。
  • AOP:Spring MVC提供了拦截器功能,本质上是AOP。通过拦截器在处理请求前统一检验权限,在不改变业务代码的基础上添加了权限控制。

实现

学生权限:StudentPermission

1
2
3
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface StudentPermission {}

还定义了教师权限(TeacherPermission)、管理员权限(AdminPermission)、自身权限(SelfPermission)。这里只定义空注解,起标记作用。

添加拦截器:PermissionsInterceptor

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
public class PermissionsInterceptor extends HandlerInterceptorAdapter{
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
boolean isPass=true;
if(method.getAnnotation(TeacherPermission.class)!=null){
isPass=isTeacher(request);
}
if(method.getAnnotation(StudentPermission.class)!=null){
isPass=isStudent(request);
}
if(method.getAnnotation(AdminPermission.class)!=null){
isPass=isAdmin(request);
}
if(method.getAnnotation(SelfPermission.class)!=null){
isPass=isSelf(request);
}
if(!isPass){//未授权,返回401信息
Gson gson=new Gson();
ResponseJson json=new ResponseJson();
json.setCode(UNAUTHORIZED.getCode());
json.setMessage(UNAUTHORIZED.getMessage());
response.setCharacterEncoding("UTF-8");
response.getWriter().write(gson.toJson(json));
}
return isPass;
}
private boolean isTeacher(HttpServletRequest request){
//判断是否是老师
}
private boolean isStudent(HttpServletRequest request){
//判断是否是学生
}
private boolean isAdmin(HttpServletRequest request){
//判断是否是管理员
}
private boolean isSelf(HttpServletRequest request){
//判断是否是自身
}
}

配置拦截器

1
2
3
4
5
6
7
8
<mvc:interceptors>
</mvc:interceptor>
<!-- 权限拦截器 -->
<mvc:interceptor>
<mvc:mapping path="/**" />
<bean class="cqupt.nmid.foreign.interceptor.PermissionsInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

注解接口

在需要权限控制的接口添加注释
1
2
3
4
5
6
@TeacherPermission
@RequestMapping(value="/students/{id}",method=RequestMethod.GET)
@ResponseBody
public ResponseJson getStudentsInfo(@PathVariable(value="id") int studentId) {
return studentService.getStudentsInfo(studentId);
}

总结

使用注解和拦截器可以很轻松的实现权限控制,这里只是写个例子,只适合简单的权限控制,但是流程已经制定好,可以很简单的进行扩展。如果权限的验证机制比较复杂,例如App接口的token加密验证,单独实现一个权限模块再利用拦截器进行请求分发可能是更好的方式。