基于位运算的权限验证


后端

位运算权限验证工具类

PermissionUtils.java

/**
 * 权限验证工具类
 */
public class PermissionUtils {

    /**
     * 基础权限,该数字可以是一个范围
     */
    public static int base = 1;

    /**
     * 增加权限
     *
     * @param permission: 原权限
     * @param operate:    需要增加的权限
     * @return int 最终权限值
     **/
    public static int addPermission(int permission, PermissionEnum operate) {
        int per = base << operate.getId();
        //增加权限时使用或,可以将权限对应的二进制位的1赋值给权限变量的0
        permission |= per;
        return permission;
    }

    /**
     * 删除权限
     *
     * @param permission: 原权限
     * @param operate:    需要增加的权限
     * @return int 最终权限值
     **/
    public static int delPermission(int permission, PermissionEnum operate) {
        int per = base << operate.getId();
        // 按位取反,取反之前的1,取反后变为0,再和权限数字进行与运算,1&0=0,1&1=1,0&1=0,这样删除对应权限,并且不影响原有权限
        per = ~per;
        return permission & per;
    }

    /**
     * 验证是否有权限
     *
     * @param permission: 权限值
     * @param operate:    权限
     * @return boolean 是否有权限
     **/
    public static boolean hasPermission(int permission, PermissionEnum operate) {
        int per = base << operate.getId();
        // 1&0=0;1&1=1;0&1=0; permission和per&操作之后,如果permission对应的位为1,则和per&的结果就等于per
        return (permission & per) == per;
    }
}

aop拦截controller统一添加权限值

ControllerPermissionAspectJAdvice.java

@Component
@Aspect
public class ControllerPermissionAspectJAdvice {

    /**
     * action返回值,增加权限值
     *
     * @param point:       切点
     * @param returnValue: 返回值
     **/
    @AfterReturning(pointcut = "execution(public * show.lmm.demo..*.*Controller.*(..))", returning = "returnValue")
    public void addControllerPermissionValue(JoinPoint point, Object returnValue) {
        ControllerPermission controllerPermission = point.getTarget().getClass().getAnnotation(ControllerPermission.class);
        if (returnValue instanceof ResponseHandler<?> responseData && controllerPermission != null) {
            int pageId = controllerPermission.value();
            if (pageId > 0) {
                int pagePermissionValue = AuthUtils.getPagePermissionValue(pageId);
                responseData.setPagePermissionValue(pagePermissionValue);
            }
        }
    }
}

权限验证拦截器

AuthInterceptor.java

@Component
public class AuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        TokenProvider.initAuthData(request);
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        response.setContentType("application/json; charset=utf-8");
        // 验证权限
        if (!this.hasPermission(request, response, handler)) {
            return false;
        }
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        DataHandler.authThreadLocal.remove();
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }

    /**
     * 验证权限
     */
    private boolean hasPermission(HttpServletRequest request, HttpServletResponse response, Object handler) throws RuntimeException {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        Method method = ((HandlerMethod) handler).getMethod();
        Class<?> declaringClass = method.getDeclaringClass();
        if(declaringClass.isAnnotationPresent(NotNeedLogin.class)){
            return true;
        }

        final long userId = DataHandler.getAuthUserId();
        // 如果注解为null, 说明不需要拦截, 直接放过
        if (userId < 1 && isNeedLogin(method)) {
            sendloginExpireMsg();
            return true;
        }
        //token自动续期
        response.addHeader(AuthConstant.AUTHORIZATION, TokenProvider.createToken(userId));

        ControllerPermission controllerPermission = declaringClass.getAnnotation(ControllerPermission.class);
        if (controllerPermission != null) {
            int pageInfoId = controllerPermission.value();
            return hasControllerPermission(pageInfoId, method, request);
        }
        return true;
    }

    /**
     * 是否需要登录
     *
     * @param method: 方法
     * @return boolean 是否需要登录
     **/
    private boolean isNeedLogin(Method method) {
        NeedLogin needLoginAnnotation = method.getAnnotation(NeedLogin.class);
        if (needLoginAnnotation != null) {
            return true;
        }
        ActionPermission actionPermission = method.getAnnotation(ActionPermission.class);
        if (actionPermission != null) {
            return true;
        }
        AddEditActionPermission addEditActionPermission = method.getAnnotation(AddEditActionPermission.class);
        return addEditActionPermission != null;
    }

    /**
     * 验证controller权限
     *
     * @param pageInfoId: 页面id
     * @param method:     action方法
     * @param request:    request请求对象
     * @return boolean
     **/
    private boolean hasControllerPermission(int pageInfoId, Method method, HttpServletRequest request) {
        // 获取方法上的注解
        ActionPermission actionPermission = method.getAnnotation(ActionPermission.class);
        if (actionPermission != null) {
            if (pageInfoId < 1) {
                throw new RuntimeException("无权限");
            }
            PermissionEnum permission = actionPermission.permissionType();
            //校验权限
            if (!hasPermission(pageInfoId, permission)) {
                throw new RuntimeException("无权限");
            }
            return true;
        }
        // 添加、修改方法注解
        AddEditActionPermission addEditActionPermission = method.getAnnotation(AddEditActionPermission.class);
        if (addEditActionPermission != null) {
            if (pageInfoId < 1) {
                throw new RuntimeException("无权限");
            }
            long primaryKey = TypeConvertUtils.toLong(request.getParameter(addEditActionPermission.fieldName()));
            PermissionEnum permission = primaryKey < 1 ? PermissionEnum.ADD : PermissionEnum.EDIT;
            //校验权限
            if (hasPermission(pageInfoId, permission)) {
                return true;
            }
            throw new RuntimeException("无权限");
        }
        return true;
    }

    /**
     * 验证是否有权限
     *
     * @param pageId:     页面id
     * @param permission: 权限类型id @see com.dataxgroup.nex.core.model.Permission
     * @return boolean
     **/
    public static boolean hasPermission(int pageId, PermissionEnum permission) {
        int pagePermissionValue = AuthUtils.getPagePermissionValue(pageId);
        if (pagePermissionValue < 1) {
            return false;
        }
        return PermissionUtils.hasPermission(pagePermissionValue, permission);
    }


    /**
     * 发送登录过期消息
     **/
    private void sendloginExpireMsg() {
        throw new RuntimeException("登录身份过期,请重新登录");
    }
}

前端

权限验证utils

permissionUtils.ts

import type { PagePermission } from '@/types/PagePermission';
import { RightType } from '@/utils/constants'

/**
 * 基础值
 */
const base = 1;// 该数字可以是一个范围

/**
 * 增加权限
 * @param permission 权限值
 * @param operate 权限
 */
export function addPermission(permission: number, operate: number) {
    const per = base << operate;
    permission |= per; // 增加权限时使用或,可以将权限对应的二进制位的1赋值给权限变量的0
    return permission;
}

/**
 * 删除权限
 * @param permission 权限值
 * @param operate 权限
 */
export function delPermission(permission: number, operate: number) {
    let per = base << operate;
    per = ~per; // 按位取反,取反之前的1,取反后变为0,再和权限数字进行与运算,1&0=0,1&1=1,0&1=0,这样删除对应权限,并且不影响原有权限
    return permission & per;
}

/**
 * 验证权限
 * @param permission 权限值
 * @param operate 权限
 */
export function hasPermission(permission: number, operate: number) {
    const per = base << operate;
    return (permission & per) === per; // 1&0=0;1&1=1;0&1=0; permission和per&操作之后,如果permission对应的位为1,则和per&的结果就等于per
}

// 判断是否有新增权限
export function hasAddPermission(permission: number) {
    const per = base << 3;
    return (permission & per) === per; // 1&0=0;1&1=1;0&1=0; permission和per&操作之后,如果permission对应的位为1,则和per&的结果就等于per
}

// 判断是否有编辑权限
export function hasEditPermission(permission: number) {
    const per = base << 4;
    return (permission & per) === per; // 1&0=0;1&1=1;0&1=0; permission和per&操作之后,如果permission对应的位为1,则和per&的结果就等于per
}

// 判断是否有删除权限
export function hasDeletePermission(permission: number) {
    const per = base << 5;
    return (permission & per) === per; // 1&0=0;1&1=1;0&1=0; permission和per&操作之后,如果permission对应的位为1,则和per&的结果就等于per
}

/**
 * 获取页面权限值
 * @param {number} permission 权限值
 */
export function getPagePermission(permission: number): PagePermission {
    return {
        view: hasPermission(permission, RightType.VIEW),
        add: hasPermission(permission, RightType.ADD),
        edit: hasPermission(permission, RightType.EDIT),
        delete: hasPermission(permission, RightType.DELETE)
    }
}

页面中使用

HelloWorld.vue

<script lang="ts" src="./HelloWorld.Service.ts"></script>
<template>
  <div class="greetings">
    <h3>当前权限值:{{ currentPermissionValue }}</h3>
  </div>
  <div class="permission-action">
    增加权限:
    <button @click="onAddPermission(1)">+浏览权限</button>
    <button @click="onAddPermission(2)">+添加权限</button>
    <button @click="onAddPermission(3)">+修改权限</button>
    <button @click="onAddPermission(4)">+删除权限</button>
  </div>
  <div class="permission-action">
    删除权限:
    <button @click="onDeletePermission(1)">-浏览权限</button>
    <button @click="onDeletePermission(2)">-添加权限</button>
    <button @click="onDeletePermission(3)">-修改权限</button>
    <button @click="onDeletePermission(4)">-删除权限</button>
  </div>
  <div class="actions">
    拥有权限:
    <a v-if="pagePermissions.view">查看</a>
    <a v-if="pagePermissions.add">添加</a>
    <a v-if="pagePermissions.edit">修改</a>
    <a v-if="pagePermissions.delete">删除</a>
  </div>
</template>

<style scoped>
.actions {
  margin-top: 1rem;
}
a {
  text-decoration: none;
  color: hsla(160, 100%, 37%, 1);
  transition: 0.4s;
  cursor: pointer;
  margin-left: 0.2rem;
}
.greetings {
  text-align: center;
}
.permission-action {
  margin-top: 1rem;
}
.permission-action button {
  margin-left: 1rem;
}
h1 {
  font-weight: 500;
  font-size: 2.6rem;
  top: -10px;
}

h3 {
  font-size: 1.2rem;
}

.greetings h1,
.greetings h3 {
  text-align: center;
}
</style>

HelloWorld.Service.ts

import { defineComponent, ref } from "vue";
import type { Ref } from "vue";
import { addPermission, delPermission, getPagePermission } from '@/utils/permissionUtils'
import { RightType } from '@/utils/constants'
import type { PagePermission } from '@/types/PagePermission';

class HelloWorldService {

    /**
     * 当前权限值
     */
    currentPermissionValue = ref(0)

    /**
     * 页面权限
     */
    pagePermissions: Ref<PagePermission> = ref({
        view: false,
        add: false,
        edit: false,
        delete: false
    })

    constructor() {
        this.pagePermissions.value = getPagePermission(this.currentPermissionValue.value)
    }

    /**
     * 添加权限
     * @param typeId 类型id
     */
    onAddPermission(typeId: number) {
        switch (typeId) {
            case 1:
                this.currentPermissionValue.value = addPermission(this.currentPermissionValue.value, RightType.VIEW)
                break;
            case 2:
                this.currentPermissionValue.value = addPermission(this.currentPermissionValue.value, RightType.ADD)
                break;
            case 3:
                this.currentPermissionValue.value = addPermission(this.currentPermissionValue.value, RightType.EDIT)
                break;
            case 4:
                this.currentPermissionValue.value = addPermission(this.currentPermissionValue.value, RightType.DELETE)
                break;
        }
        this.pagePermissions.value = getPagePermission(this.currentPermissionValue.value)
    }

    /**
     * 删除权限
     * @param typeId 类型id
     */
    onDeletePermission(typeId: number) {
        switch (typeId) {
            case 1:
                this.currentPermissionValue.value = delPermission(this.currentPermissionValue.value, RightType.VIEW)
                break;
            case 2:
                this.currentPermissionValue.value = delPermission(this.currentPermissionValue.value, RightType.ADD)
                break;
            case 3:
                this.currentPermissionValue.value = delPermission(this.currentPermissionValue.value, RightType.EDIT)
                break;
            case 4:
                this.currentPermissionValue.value = delPermission(this.currentPermissionValue.value, RightType.DELETE)
                break;
        }
        this.pagePermissions.value = getPagePermission(this.currentPermissionValue.value)
    }
}

export default defineComponent({
    name: "HelloWorld",
    setup: () => {
        const helloWorldService = new HelloWorldService();
        return {
            currentPermissionValue: helloWorldService.currentPermissionValue,
            pagePermissions: helloWorldService.pagePermissions,
            onAddPermission: helloWorldService.onAddPermission.bind(helloWorldService),
            onDeletePermission: helloWorldService.onDeletePermission.bind(helloWorldService),
        }
    },
});

说明

优势:一个数字即可表示一个页面所有权限

实际工作中需要前后端配合使用。

  • 后端返回数字类型的页面权限值
  • 前端根据需要解析页面权限值

示例

后端:https://gitee.com/luoye/examples/tree/main/bitOperationPermissionVerificationApi
前端:https://gitee.com/luoye/examples/tree/main/bitOperationPermissionVerificationWeb


文章作者: Ming Ming Liu
文章链接: https://www.lmm.show/10/
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Ming Ming Liu !
  目录