后端
位运算权限验证工具类
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