自己实现注解式权限校验(SpringBoot)

自己实现注解式权限校验(SpringBoot)

权限校验是很多情况都会用到的,结合Java注解和拦截器,直接在Controller层的方法上添加一个注解,可以无侵入式的进行权限校验。

一.Java注解

1.RequestMapping

我们打开一个最常用的Spring注解

在这里插入图片描述
可以看到,RequestMapping注解上,还有几个注解,分别代表

Target:注解目标(如:可以在方法、类、参数中使用)

Retention:是注解的注解,称为元注解。 分为3类 :

  • RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
  • RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
  • RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;

Documented:表明这个注释是由 javadoc记录的。

Mapping:Spring的注解,不做过多说明

二.自定义一个判断权限的注解

1.Permission.java

在了解了注解的组成后,我们可以尝试着,自己编写一个注解。用来判断权限。

注意:此注解使用了Spring的AliasFor注解,改注解成对出现,表示双方互为别名属性。(既设置了name,则value同样有该值),使用AliasFor注解后,必须用Spring的AnnotationUtils工具获取注解才可以达到上述效果。

import org.springframework.core.annotation.AliasFor;

import java.lang.annotation.*;

/**
 * @author litong
 * @date 2019/11/29 16:18
 */

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface Permission {

	/**
	 * 权限
	 */
	@AliasFor("value")
	PermissionEnum[] name() default {};

	/**
	 * 权限
	 */
	@AliasFor("name")
	PermissionEnum[] value() default {};

}

2.PermissionEnum.java

权限的枚举,使用枚举可以更好的说明参数,易读性高,且可以减少报错几率。

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * @author litong
 * @date 2019/11/29 16:20
 */
@Getter
@AllArgsConstructor
public enum PermissionEnum {
   /**
    * 用户管理权限
    */
   USER(1, "用户管理权限"),
   /**
    * 教师管理权限
    */
   TEACHER(2, "教师管理权限"),

   /**
    * 无需校验,
    */
   NO(-99999, "无需权限"),
   ;
   /**
    * 权限编码
    */
   private Integer code;
   /**
    * 权限名称
    */
   private String msg;
}

三.自定义拦截器

注:此拦截器,一般还会有未登录拦截,Token解析,用户注入、权限校验等功能,现只展示其中之一,权限校验。

1.AuthenticationInterceptor.java

/**
 * @Author: litong
 * @Date: 2019-09-20 11:50
 * @Description: 拦截器
 */
@Slf4j
public class AuthenticationInterceptor implements HandlerInterceptor {
    /**
	 * 在业务处理器处理请求之前被调用
	 *
	 * @param request
	 * @param response
	 * @param handler
	 * @return
	 * @throws Exception
	 */
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		// 如果不是映射到方法直接通过
		if (!(handler instanceof HandlerMethod)) {
			return true;
		}
         // 获取方法中的注解
         HandlerMethod handlerMethod = (HandlerMethod) handler;
		Method method = handlerMethod.getMethod();
        // 省略判断是否需要登录的方法.....
        // 省略Token解析的方法.....
        // 此处根据自己的系统架构,通过Token或Cookie等获取用户信息。
        UserInfo userInfo = userService.getUserByToken(token);
        
        // 获取类注解
		Permission permissionClass = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Permission.class);
		// 获取方法注解
		Permission permissionMethod = AnnotationUtils.findAnnotation(method,Permission.class);
		// 判断是否需要权限校验
		if (permissionClass == null && permissionMethod == null) {
			// 不需要校验权限,直接放行
			return true;
		}
        // 获取该方法注解,优先级:方法注解>类注解
		PermissionEnum[] permissionEnums;
		if (permissionClass != null && permissionMethod == null) {
			// 类注解不为空,方法注解为空,使用类注解
			permissionEnums = permissionClass.name();
		} else if (permissionClass == null) {
			// 类注解为空,使用方法注解
			permissionEnums = permissionMethod.name();
		} else {
			// 都不为空,使用方法注解
			permissionEnums = permissionMethod.name();
		}
        // 校验该用户是否有改权限
        // 校验方法可自行实现,拿到permissionEnums中的参数进行比较
        if(userService.checkPermissionForUser(userInfo,permissionEnums)){
            // 拥有权限
            return true;
        } else {
            // 抛出自定义异常,可在全局异常捕获后自行处理。
            throw new AuthTokenException(CheckConstants.PERMISSION_ERROR);
        }

    }
    
    /**
	 * 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
	 *
	 * @param httpServletRequest
	 * @param httpServletResponse
	 * @param o
	 * @param modelAndView
	 * @throws Exception
	 */
	@Override
	public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

	}

	/**
	 * 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
	 *
	 * @param httpServletRequest
	 * @param httpServletResponse
	 * @param o
	 * @param e
	 * @throws Exception
	 */
	@Override
	public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
	}
}

四.使用

1.类(该类中,所有方法校验权限)

/**
 * @author litong
 * @date 2019/9/20 13:26
 */
@RestController
@RequestMapping("/auth")
@AllArgsConstructor
@Slf4j
@Permission({PermissionEnum.USER})
public class AuthController {
    
}

2.方法(此方法校验权限,优先级大于类上的注解)

@PostMapping("/get")
@Permission({PermissionEnum.USER})
public R get() {
}
发布了93 篇原创文章 · 获赞 167 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/LitongZero/article/details/103628706