自定义简单的鉴权功能(非常简单!!!)

灵感来源:ruoyi-plus中推荐的Sa-Token权限认证框架.

Sa-Token

需求:对于不用角色用户我们需要对其接口权限进行控制,首先在体验上我们需要在前端控制那些没有权限的按钮不显示

前端实现

我们的前端是使用vue2,可以通过自定义标签v-permission控制是否展示该按钮,实现过程,用户登录的时候后台提供该用户所有的权限标识符,前端将这些数据缓存起来,我是存在了sessionStorage中,代码如下:

/**
 * 自定义指令,按钮是否有权限
 */
Vue.directive("permission", {
    inserted(el, binding) {
        el.style.display = "none";
        let permission = binding.value
        let permissionStr = window.sessionStorage.getItem("permission");
        if ((!permissionStr)) {
            removeToken()
            removeInformation()
            router.push("/login");
            return;
        }
        let hasPermission = permissionStr.indexOf(permission) !== -1;
        if (permissionStr === '*' || hasPermission) {
            el.style.display = "inline-block";
        } else isPermission(permission).then(hasPermission => {
            if (hasPermission) {
                el.style.display = "inline-block";
            }
        })
    }
})

isPermission方法是后台请求,因为存在一种情况,用户发现自己没有权限,去问了上级领导,上级领导发现是没有给他分配权限,于是给他分配了权限,但是我们的权限标识是缓存起来的,于是我们提供了请求后台的接口.

使用:

<el-button v-permission="'crm:repair:delete'" type="danger" size="mini"
                                   slot="reference">
                            删除
                        </el-button>

后端实现:为了接口安全,后端也必须对接口权限进行控制

1.自定义注解@ApiCheckPermission

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface ApiCheckPermission {
    // 权限字符,支持多个
    String[] value() default {};

    // 权限字符连接类型 and全部包括, or一个符合即可
    SysMode permissionMode() default SysMode.OR;

    // 是否需要管理员权限("admin")
    String type() default "";
    String[] orRole() default {};
}

2.处理鉴权逻辑

@Component
@Aspect
@EnableAsync
@Slf4j
public class AspectCheckPermission {

    // 用注解的方式定义切点
    @Pointcut("@annotation(com.gegeda.sp.log.aop.ApiCheckPermission)")
    public void point() {
    }

    // 用前置通知的方式鉴权
    @Before("point()")
    public void before(JoinPoint jp) throws GraceException {
        // 获取操作用户
        LoginUser loginUser = LoginUtils.getLoginUser();
        // 需要鉴权的方法必须存在操作用户
        if (loginUser == null) {
            throw new InjectException(ResponseStatusEnum.UN_LOGIN);
        }
        // bug用户
        if (loginUser.getId() == 1) {
            return;
        }
        // 获取需要鉴权的方法注解
        MethodSignature signature = (MethodSignature) jp.getSignature();
        Method method = signature.getMethod();
        ApiCheckPermission annotation = method.getAnnotation(ApiCheckPermission.class);
        // 获取注解参数
        List<String> permissionList = Arrays.asList(annotation.value());
        SysMode permissionMode = annotation.permissionMode();
        Set<String> permissions = loginUser.getPermissions();

        // 缓存中不存在用户权限字符数据需要用户重新登录刷新
        if (permissions == null) {
            throw new InjectException("权限校验失败, 您可以尝试退出并重新登录...");
        }
        // 判断是否需要管理员权限
        String type = annotation.type();
        if ("admin".equals(type)) {
            Integer integer = loginUser.getIsAdmin();
            if (integer != 1) {
                throw new NotPermissionException("您不是管理员, 操作权限不足...").setCode(503);
            }
        }
        // 校验权限字符
        boolean checkPermissionRes = this.checkPermission(permissionMode, permissions, permissionList);

        // 权限字符校验不通过抛出权限不足异常
        if (!checkPermissionRes) {
            throw new NotPermissionException();
        }
    }

    private boolean checkPermission(SysMode mode, Set<String> permissions, List<String> permissionList) {
        switch (mode) {
            case AND:
                return permissions.containsAll(permissionList);
            case OR:
                for (String permission : permissionList) {
                    if (permissions.contains(permission)) {
                        return true;
                    }
                }
        }
        return false;
    }

注意:LoginUtils是自定义工具类,http请求中有token,可以获取Redis中的用户缓存数据信息,从而避免数据库查询.

3.自定义异常类

public class NotPermissionException extends RuntimeException {
    private static final long serialVersionUID = 6806129545290130132L;
    private int code = ResponseStatusEnum.NO_PERMISSION.status();

    public NotPermissionException() {

    }
    public NotPermissionException(int code) {
        this.code = code;
    }

    public NotPermissionException(String message) {
        super(message);
    }

    public NotPermissionException(int code, String message) {
        super(message);
        this.code = code;
    }

    public NotPermissionException(Throwable cause) {
        super(cause);
    }

    public NotPermissionException(String message, Throwable cause) {
        super(message, cause);
    }

    public int getCode() {
        return this.code;
    }

    public NotPermissionException setCode(int code) {
        this.code = code;
        return this;
    }

    public static void throwBy(boolean flag, String message, int code) {
        if (flag) {
            throw new NotPermissionException(message);
        }
    }

    public static void throwByNull(Object value, String message, int code) {
        if (ObjectUtil.isEmpty(value)) {
            throw (new NotPermissionException(message)).setCode(code);
        }
    }

4.注册异常拦截处理

    /**
     * 权限不足异常
     */
    @ExceptionHandler(value = NotPermissionException.class)
    public Ret<Void> NotPermissionExceptionHandler(NotPermissionException e) {
        return Ret.fail(ResponseStatusEnum.NO_PERMISSION.status(), ResponseStatusEnum.NO_PERMISSION.msg());
    }

5.使用

    /**
     * 补录订单导入
     */
    @ApiCheckPermission("crm:repair:import")
    @PostMapping("/importOrderRepair")
    public Ret<ImportVO> importOrderRepair(
            @RequestBody List<OrderRepairExcel> data) {
        LoginUser loginUser = LoginUtils.getLoginUser();
        if (loginUser == null) {
            return Ret.fail("登录状态异常");
        }
        ImportVO importVO = orderRepairBiz.importOrderRepair(data, loginUser);
        return Ret.ok(importVO);
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值