网站建设需要什么软件有哪些排名优化公司哪家靠谱
目录
- 前言
- 设计
- 参考资料
前言
本文是设计一个权限注解,
这个注解可以根据当前登陆人所拥有的权限做出接口拦截,当拥有该接口的权限时 才可以进入controller接口 (推荐修饰在controller接口上,因为原则来说没有权限直接就不应该进入controller了)
同时如果同一个接口同时有多个权限的时候,只要其中一个权限生效即可进入
技术是用的spring的切面和注解,以及重复注解技术
权限可以在数据库中配置。
设计
表结构:
- tmenu表(菜单表):menu_id(菜单id),menu_name(菜单名字)
- tfunc表(菜单权限表):menu_id(菜单id),func_id(权限标识),func_name(权限名字)
- trole表(角色表):role_id(角色id),role_name(角色名字)
- trolemenu表(角色菜单表):roleId(角色id),menu_id(菜单id)
- trolefunc表(角色权限表):role_id(角色id),menu_id(菜单id),func_id(权限标识)
默认登陆的时候,当前登录域里已经存放了当前登陆人所拥有的菜单以及权限列表
首先定义一个注解类,该类用于修饰controller的接口上,所以Target里设有method,EBPermissionRepeat是用于修饰重复注解的,下个再说明
package com.gdxx.core.annotation;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;// 如果是非重复注解,吧这行去掉即可
@Repeatable(value = MyPermissionRepeat.class)
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyPermissions {/*** 菜单id* * @return*/public String mid() default "";/*** 功能点id* * @return*/public String fid() default "";}
EBPermissionRepeat类,用于修饰重复注解,如果是非重复注解则不用加
package com.gdxx.core.annotation;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/** 用于定义重复注解*/
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyPermissionRepeat {EBPermissions[] value();
}
如果没有这个类,在修饰controller接口时只能修饰一次
@RestController // restful api
@RequestMapping("/user") // 迁移前缀
public class UserController extends BaseController {@EBPermissions(mid = "users", fid = "user.search")@PostMapping("/queryUsers")public ResultResponse<IPage<UserDo>> queryUsers(}
}
有了这个类后就可以重复修饰这个接口了,
@RestController // restful api
@RequestMapping("/user") // 迁移前缀
public class UserController extends BaseController {@EBPermissions(mid = "users", fid = "user.xxxxx")@EBPermissions(mid = "users", fid = "user.search")@PostMapping("/queryUsers")public ResultResponse<IPage<UserDo>> queryUsers(}
}
接下来我们要使用springaop拦截这个注解,即切点就是这个注解,切面则是在进入修饰的接口前。
同时如果多个权限(user.xxxxx或者user.search)中,当前登陆人已经拥有了任意一个,那么都可以进入
注意下面@Pointcut切点:不仅写了EBPermissions还写了EBPermissionRepeat!是同时拦截了两个!滑块右边是有内容的
/*** controller接口权限检查* * @author */@Slf4j
@Aspect
@Component
public class MyPermissionsAspect {// 配置织入点,注意此处后面有个||,是同时拦截EBPermissions和EBPermissionRepeat// 如果是非重复注解,则把后面的EBPermissionRepeat去掉即可@Pointcut("@annotation(com.gdxx.core.annotation.MyPermissions) || @annotation(com.gdxx.core.annotation.MyPermissionRepeat)")public void permitPointCut() {}/*** 处理完请求后执行** @param joinPoint 切点*/@Before("permitPointCut()")public void checkPermission(JoinPoint joinPoint) {// 获得注解MyPermissions[] epArr = getAnnotation(joinPoint);if (epArr == null || epArr.length == 0) {log.warn("EBPermissions.getAnnotation() is null");return;}// 获取当前登录的用户// 每个系统的不一样,这里自己替换自己系统的获取用户UserDto user = SessionHelper.getCurrentUser();if (user == null) {throw new ServiceException(StatusEnum.UNAUTHORIZED);}// 是否全部权限中有一个拥有boolean existsPassCheck = false;for (int i = 0; i < epArr.length; i++) {MyPermissions ep = epArr[i];String rightId = new StringBuilder().append(ep.mid()).append("_").append(ep.fid()).toString();// 多个相同的注解修饰,只要有一个符合即可// 获取当前登录用户是否拥有该权限// 每个系统的不一样,这里自己替换自己系统的获取用户if (SessionHelper.checkUserRight(rightId)) {// throw new ServiceException(StatusEnum.METHOD_NOT_ALLOWED);existsPassCheck = true;break;}}// 抛出异常if (existsPassCheck == false) {// 如果想被全局捕获的话// throw new HttpRequestMethodNotSupportedException();throw new ServiceException(StatusEnum.METHOD_NOT_ALLOWED);}}/*** 是否存在注解,如果存在就获取*/private MyPermissions[] getAnnotation(JoinPoint joinPoint) {Signature signature = joinPoint.getSignature();MethodSignature methodSignature = (MethodSignature) signature;Method method = methodSignature.getMethod();if (method != null) {// 重复注解的获取return method.getAnnotationsByType(MyPermissions.class);// 如果是非重复注解则使用这种,直接返回EBPermissions而不是数组// return method.getAnnotation(EBPermissions.class);}return null;}
}
至此完成!
异常还可以用全局异常做捕获(可选)
可以搜索下@RestControllerAdvice这个注解用法,是spring自带的处理异常的解决方案
/*** 全局异常处理器* * @author rain*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {/*** 处理HTTP请求方法不支持异常*/@ExceptionHandler(HttpRequestMethodNotSupportedException.class)public ResultResponse<Object> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex,HttpServletRequest request) {String message = String.format("不支持'%s'请求方法", ex.getMethod());log.warn(message + ", 请求路径: {}", request.getRequestURI());return ResultResponse.error(StatusEnum.METHOD_NOT_ALLOWED, message).path(request.getRequestURI());}
}
参考资料
- Spring AOP自定义可重复注解没有生效问题.
- java8新特性——重复注解.