aop+自定义注解实现接口权限控制

实现思路:控制层添加自定义权限注解 @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;
    }

}

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值