一、通过拦截器实现
1 权限表
为了方便,我直接用的现成的权限表,这是表结构
2 自定义注解
首先,创建一个自定义注解,用于controller层的方法或类上
// @Target表示该注解可以用在方法和类上
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiPermission {
PermissionEnum permission();
}
3 权限枚举类
PermissionEnum枚举内是权限枚举,值对应数据表中 api_permission 字段,后期会通过这个来校验权限
@Getter
public enum PermissionEnum {
DEVICELIST_A("north:device:list"),
DEVICEINFO_A("north:device:info"),
DEVICEEXTEND_A("north:device:info:extend"),
DEVICEHISTORICALDATA_A("north:device:historicalData"),
DEVICESTATUS_A("north:device:status")
;
private String value;
PermissionEnum(String value) {
this.value = value;
}
}
4 拦截器
最重要的就是拦截器了,通过拦截器获取方法或类上的注解信息,然后比对权限表,判断是否拥有权限
@Component
@Slf4j
public class PermissionInterceptor implements HandlerInterceptor {
@Autowired
INorthAppApiService iNorthAppApiService;
// 前置处理器
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(handler instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod) handler;
ApiPermission methodAnnotation = method.getMethodAnnotation(ApiPermission.class);
//获取方法上的注解
if (methodAnnotation == null) {
System.out.println("内部错误");
return false;
}
String appId = request.getParameter("appId");
List<NorthAppApi> northAppApiList = iNorthAppApiService.getInfo(appId);
boolean perBools = false;
for (NorthAppApi northAppApi : northAppApiList) {
if(northAppApi.getApiPermission().equals(methodAnnotation.permission().getValue())) {
perBools = true;
}
}
if(!perBools) {
System.out.println("无权限");
return false;
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
5 WebMvcConfig
别忘了将拦截器注册,不然拦截器不会生效
@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {
@Autowired
private PermissionInterceptor permissionInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加拦截器
registry.addInterceptor(permissionInterceptor).addPathPatterns("/**");
}
}
6 使用
最后在controller层使用自己的自定义注解,在这里要注意,要使用@RequestMapping,不然会获取不到注解信息,但是我同事使用Post又可以获取,我也不知道为什么,有知道的可以评论告知
@RestController
@RequestMapping("/device")
public class DeviceController {
@Autowired
private DeviceService deviceService;
// POSTMapping不行
@ApiPermission(permission = PermissionEnum.DEVICELIST_A)
@RequestMapping(value = "/list" )
@ResponseBody
public List<DeviceInfo> list(String appId) {
System.out.println(deviceService.list());
return null;
}
@ApiPermission(permission = PermissionEnum.DEVICEINFO_A)
@RequestMapping(value = "/info" )
@ResponseBody
public List<DeviceInfo> info(String appId) {
System.out.println(deviceService.list());
return null;
}
}
当我访问:http://localhost:9999/device/list?appId=dididache时,就可以通过权限返回数据,因为/device/list这个接口中写了我的自定义注解,传的appId可以查到该应用下拥有自定义注解中的权限记录,所以放行
如果我访问:http://localhost:9999/device/info?appId=dididache,我事先把数据表中第四条记录删了,因此权限表中 appId为dididache 的应用就不存在这个权限,因此无法放行
二、通过AOP实现
前面三个步骤跟上面的一样
4 切面类
@Aspect
@Component
public class InnerAuthAspect implements Ordered
{
@Around("@annotation(innerAuth)")
public Object innerAround(ProceedingJoinPoint point, InnerAuth innerAuth) throws Throwable
{
String source = ServletUtils.getRequest().getHeader(SecurityConstants.FROM_SOURCE);
// 内部请求验证
if (!StringUtils.equals(SecurityConstants.INNER, source))
{
throw new BusinessException("没有内部访问权限,不允许访问");
}
return point.proceed();
}
/**
* 确保在权限认证aop执行前执行
*/
@Override
public int getOrder()
{
return Ordered.HIGHEST_PRECEDENCE + 1;
}
}
这个切面类,环绕通知 @innerAuth注解,所以当执行带 @innerAuth 注解的方法或类时,就会执行这个通知逻辑,也就实现了代替拦截器实现权限校验的功能
然后可以跳过第五步,直接第六个步骤调用接口试一下,效果跟拦截器那个版本一样的