权限控制的几种情况

从权限的类型来讲,目前在后端项目中碰到的限权类型包括:页面元素的控制、接口访问的权限、数据可传输性的控制。对这三种情况进行记录。

基础准备工作:

用户表

页面元素的控制

页面元素的控制表示:前端暴露给使用者的元素或按钮。比如:对于被禁言用户,就无法点击发言框,这样就无法使用发言标签了。最好通过前端实现这一功能,后端需要返回发言标签的值即可。因此,该功能与普通的查询功能差异不大,遍历用户的权限即可。

本项目的例子:

public UserAuthentic getUserAuthenticByUserId(Long userId) {//要查询一个用户的所有权限,分为两步//首先,查询该用户对应的角色:一对多,结果是list//然后,根据角色查询两个方面的权限:一对多List userRoleList = userRoleService.getUserRoleById(userId);//提取出role的id列表Set roleIdList = userRoleList.stream().map(UserRole::getRoleId).collect(Collectors.toSet());//查询“元素操作elementOperation”的权限(对应 roleElement 中间表)List roleElementOperationList = authRoleAuthenticService.getElementOperationByRoleId(roleIdList);for (AuthRoleElementOperation a : roleElementOperationList) {System.out.println(a.getElementOperationId());}//查询“菜单roleMenu”的权限(对应 RoleMenu 中间表)List roleRoleMenuList = authRoleAuthenticService.getRoleMenuByRoleId(roleIdList);UserAuthentic userAuthentic = new UserAuthentic();userAuthentic.setRoleElementOperationList(roleElementOperationList);userAuthentic.setMenuRoleList(roleRoleMenuList);return userAuthentic;

在上述例子中,主要涉及到“一对多”的查询,一个用户对应多个角色,采用关联查询。注意其中的association标签的使用。



接口访问权限的控制

接口访问权限即字面意思,在Controller的接口被访问时,需要进行逻辑判断。这里采用AOP的方法织入判断逻辑,总体是注解+AOP的思路,需要重点学习AOP。AOP包含切面、切入点等重点概念。

以动态发布接口UserMoment为例,规定等级为lv0的用户无法发布动态。

其中,注解的内部逻辑如下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Component
public @interface ApiLimitedRole {//只需要定义一个被限制用户的字符串String[] limitedRoleCodeList() default {};
}

在该注解中包含一个字符串,表示禁止访问的字符串,避免耦合,该字符串在注解使用时才被赋值。

其次,在AOP切面方法中,设置切入点、前置通知。

@Component
@Order(1)//设置优先级
@Aspect
//设置切点,编写切面(切面表示对切点进行处理)
//具体来说,切点就是需要进行处理的点;切面就是在切点进行处理的方法。
public class ApiLimitedAspect {//首先引入两个依赖,为切面方法的编写做准备@Autowiredprivate UserSupport userSupport;@Autowiredprivate UserRoleService userRoleService;@Pointcut("@annotation(com.imooc.domain.annotation.ApiLimitedRole)")//需要给出注解的实际位置,也可以是某个包或者某个类public void check(){}//给一个空方法//编写具体的切面方法@Before("check() && @annotation(apiLimitedRole)")//设置切面条件:当check()方法时。增加该注解的原因是doBefore需要该参数public void doBefore(JoinPoint joinPoint , ApiLimitedRole apiLimitedRole){//@ param:joinPoint携带了目标方法的参数对象//@ param:apiLimitedRole表示注解的实例化,携带了被限制的用户组Long userId = userSupport.getUserId();List userRoleList = userRoleService.getUserRoleById(userId);Set userRoleSet = userRoleList.stream().map(UserRole::getRoleCode).collect(Collectors.toSet());//将roleCode转化为set集合String[] limitedRoleCodeList= apiLimitedRole.limitedRoleCodeList();Set limitedRoleCodeSet = Arrays.stream(limitedRoleCodeList).collect(Collectors.toSet());//转化为set//取交集userRoleSet.retainAll(limitedRoleCodeSet);if (userRoleSet.size() > 0) {throw  new ConditionException("用户无访问权限!");}}}

在切面方法的doBefore通知中的逻辑为:如果用户的roleCode与禁止访问字符串有交集,则判定为无权限用户。

最后,在接口方法位置打上该注解,并给禁止访问字符串赋值。表示在该方法处织入切面逻辑。

    //增加用户动态@ApiLimitedRole(limitedRoleCodeList = {UserConstant.ROLE_CODE_LV0})@DataLimitedRole@PostMapping("/user-moment")public JsonResponse addUserMoment(@RequestBody UserMoment userMoment) throws InterruptedException, RemotingException, MQClientException, MQBrokerException {Long userId = userSupport.getUserId();userMoment.setUserId(userId);userMomentService.addUserMoment(userMoment);return JsonResponse.success();}

对数据的权限设置

该概念比较抽象,比如弹幕发送是一个接口,所有用户都能发送弹幕,但是弹幕有很多种类型type,新用户只能发送普通弹幕。那就需要依据用户role控制弹幕的type。

其实思路跟接口权限控制的思路是一样的,只是逻辑不一样,以下给出两部分的代码:

@Component
@Order(1)//设置优先级
@Aspect
//设置切点,编写切面(切面表示对切点进行处理)
//具体来说,切点就是需要进行处理的点;切面就是在切点进行处理的方法。
public class DataLimitedAspect {//首先引入两个依赖,为切面方法的编写做准备@Autowiredprivate UserSupport userSupport;@Autowiredprivate UserRoleService userRoleService;@Pointcut("@annotation(com.imooc.domain.annotation.DataLimitedRole)")//需要给出注解的实际位置,也可以是某个包或者某个类public void check(){}//给一个空方法//编写具体的切面方法@Before("check() ")//设置切面条件:当check()方法,并使用该注解时。public void doBefore(JoinPoint joinPoint){//@ param:joinPoint携带了目标方法的参数对象//@ param:apiLimitedRole表示注解的实例化,携带了被限制的用户组Long userId = userSupport.getUserId();List userRoleList = userRoleService.getUserRoleById(userId);Set userRoleSet = userRoleList.stream().map(UserRole::getRoleCode).collect(Collectors.toSet());//将roleCode转化为set集合//获取传递的参数列表Object[] args = joinPoint.getArgs();for (Object arg : args) {if (arg instanceof UserMoment) {UserMoment userMoment = (UserMoment) arg;String momentType = userMoment.getType();//获取动态的等级,只有Lv0以上的用户才能发type为0的动态if (userRoleList.contains(UserConstant.ROLE_CODE_LV0) && "0".equals(momentType)) {throw new ConditionException("用户等级不足,无法发送动态!");}}}}}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Component
public @interface DataLimitedRole {//不需要定义一个被限制用户的字符串
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部