实现思路:控制层添加自定义权限注解 @PreAuthorize(“system:menu:list”),然后权限组件通过AOP和反射原理获取权限注解中配置的权限标识,与菜单维护的权限标识做匹配,一致则放行,不一致则返回未授权信息
- 菜单表维护权限标识字段:perms
CREATE TABLE `sys_menu` (
`menu_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '菜单ID',
`menu_name` varchar(50) NOT NULL COMMENT '菜单名称',
`parent_id` bigint(20) DEFAULT '0' COMMENT '父菜单ID',
`order_num` int(4) DEFAULT '0' COMMENT '显示顺序',
`path` varchar(200) DEFAULT '' COMMENT '路由地址',
`component` varchar(255) DEFAULT NULL COMMENT '组件路径',
`is_frame` int(1) DEFAULT '1' COMMENT '是否为外链(0是 1否)',
`is_cache` int(1) DEFAULT '0' COMMENT '是否缓存(0缓存 1不缓存)',
`menu_type` char(1) DEFAULT '' COMMENT '菜单类型(M目录 C菜单 F按钮)',
`visible` char(1) DEFAULT '0' COMMENT '菜单状态(0显示 1隐藏)',
`status` char(1) DEFAULT '0' COMMENT '菜单状态(0正常 1停用)',
`perms` varchar(100) DEFAULT NULL COMMENT '权限标识',
`icon` varchar(100) DEFAULT '#' COMMENT '菜单图标',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) DEFAULT '' COMMENT '备注',
PRIMARY KEY (`menu_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2038 DEFAULT CHARSET=utf8 COMMENT='菜单权限表';
- 自定义权限注解@PreAuthorize
/**
* @description: 自定义权限注解
* @author: Han LiDong
* @create: 2021/10/19 15:32
* @update: 2021/10/19 15:32
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface PreAuthorize {
// 权限标识
String value();
}
- AOP权限校验
package com.yss.datamiddle.aspect;
import com.yss.datamiddle.dto.ComposeAuthorityVerifyDto;
import com.yss.datamiddle.exceptions.DmBizException;
import com.yss.datamiddle.feign.service.RemoteService;
import com.yss.datamiddle.service.RemoteManagerComposeService;
import com.yss.datamiddle.service.RemoteManagerPreService;
import com.yss.datamiddle.tools.GsonUtil;
import com.yss.datamiddle.tools.StringUtil;
import com.yss.datamiddle.util.YamlUtil;
import com.yss.datamiddle.vo.DataAuthVo;
import com.yss.datamiddle.vo.R;
import com.yss.datamiddle.vo.RetResult;
import com.yss.datamiddle.vo.UserInfo;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.List;
/**
* @description: aop拦截权限校验注解,校验用户是否拥有接口权限
* @author: Han LiDong
* @create: 2021/10/19 15:32
* @update: 2021/10/19 15:32
*/
@Slf4j
@Aspect
@Component("ss")
public class PreAuthorityAspect {
// 网关reques请求头标识key
private static final String GATEWAY_REQUEST_MARK = "gateway_request_mark";
/**
* 配置切入点表达式。@Before前置通知, 在方法执行之前执行
* @author: hld
* @update: 2021-10-20 10:07:27
* @param joinPoint
* @return
*/
@Before("@annotation(com.yss.datamiddle.aspect.PreAuthorize)")
public void hasPermi(JoinPoint joinPoint) throws Throwable {
HttpServletRequest request = getRequest();
log.info("请求地址:{}",request.getRequestURI());
// 请求头有网关标识,标识内部调用(走网关),进行权限校验
if (!StringUtil.isEmpty(request.getHeader(GATEWAY_REQUEST_MARK))){
log.info("请求头有网关标识:{}",GATEWAY_REQUEST_MARK);
//验证数据权限
hasPermi(joinPoint,request);
}
}
/**
* 接口权限校验
* @param joinPoint
* @param request
*/
private void hasPermi(JoinPoint joinPoint, HttpServletRequest request) {
//获取用户相关信息(这里根据项目实际情况获取用户信息)
R<UserInfo> info = remoteService.info();
if(null==info||info.getData()==null){
return;
}
UserInfo userInfo = info.getData();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
PreAuthorize preAuthorize = method.getAnnotation(PreAuthorize.class);
// 获取权限标识value 例: system:menu:list
String value = preAuthorize.value();
log.info("权限标识:{},用户id:{}",value,userInfo.getSysUser().getUserId());
/*
* 根据自定义注解权限标识 获取用户是否有权限(自定义实现)。
* 由于我们是微服务项目,所以此处我们feign调用系统管理服务,获取权限信息
*/
RetResult<List<String>> dataAuthVoRetResult = remoteManagerPreService.queryPreAuth(value, userInfo.getSysUser().getUserId());
if (dataAuthVoRetResult.getData() == null || dataAuthVoRetResult.getData().isEmpty()){
log.info("无接口权限");
//自定义抛出异常
throw new DmBizException("没有权限调用接口");
}
}
/**获取request*/
private HttpServletRequest getRequest(){
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
return request;
}
}
- feign调用接口
package com.yss.datamiddle.service;
import com.yss.datamiddle.vo.DataAuthVo;
import com.yss.datamiddle.vo.RetResult;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
/**
* @Description
* @Author 86199
* @Date 2021/10/19 10:29
*/
@FeignClient(
contextId = "remoteManagerPreService",
value = "yss-datamiddle-manager"
)
public interface RemoteManagerPreService {
@ApiOperation(value = "查询用户数据权限")
@GetMapping("/menu/preAuth")
RetResult<List<String>> queryPreAuth(@RequestParam("perm") @ApiParam(name = "perm", value = "权限标识", required = true) String perm, @RequestParam("userId") Integer userId) ;
}
断路器
package com.yss.datamiddle.service.impl;
import com.yss.datamiddle.service.RemoteManagerComposeService;
import com.yss.datamiddle.service.RemoteManagerPreService;
import com.yss.datamiddle.vo.DataAuthVo;
import com.yss.datamiddle.vo.RetResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @Description
* @Author 86199
* @Date 2021/10/19 10:35
*/
@Component
public class ManagerPreFallbackImpl implements RemoteManagerPreService {
private final Logger log = LoggerFactory.getLogger(getClass());
private Throwable cause;
public ManagerPreFallbackImpl() {
}
public void setCause(Throwable cause) {
this.cause = cause;
}
@Override
public RetResult<List<String>> queryPreAuth(String perm, Integer userId) {
log.error("查询接口权限异常!perm={},userId={}",perm,userId);
return null;
}
}