Spring Boot整合Aop面向切面编程实现权限校验,SpringAop+自定义注解+自定义异常+全局异常捕获,实现权限验证,要求对每个接口都实现单独的权限校验。

目录

1.介绍SpringAop

(1)切面(Aspect)

(2)连接点(Join Point)

(3)切点(Pointcut)

(4)通知(Advice)

2.模块前景

1.准备一个切面

2.自定义注解类

3.创建常量类

4.自定义异常(运行时异常)

5.全局异常捕获

6.Controller层使用



1.介绍SpringAop

组成:

(1)切面(Aspect)

定义 AOP 是针对某个统一的功能的,这个功能就叫做一个切面,比如用户登录功能或方法的统计日志,他们就各是一个切面。切面是由切点和通知组成的

(2)连接点(Join Point)

所有可能触发 AOP(拦截方法的点)就称为连接点

(3)切点(Pointcut)

切点的作用就是提供一组规则来匹配连接点,给满足规则的连接点添加通知,总的来说就是,定义 AOP 拦截的规则的

切点相当于保存了众多连接点的一个集合(如果把切点看成一个表,而连接点就是表中一条一条的数据)

(4)通知(Advice)

切面的工作就是通知

通知:规定了 AOP 执行的时机和执行的方法

2.模块前景

由于做的是安全产品管理系统,所以对产品的每个模块和操作都有明确的权限限制,采用SpringAop和自定义注解实现是我能想到的最简单的实现方式,

下面展示代码实现:

1.准备一个切面

这里的切点和其他依赖自行创建,核心就是当前用户权限和接口权限若不匹配就抛出异常,

抛出的异常也是自定义异常,代码在后面


import com.shenyan.domain.annotation.ApiLimitedRole;
import com.shenyan.domain.response.vo.LoginUserRespVo;
import com.shenyan.domain.response.vo.RoleInfoRespVo;
import com.shenyan.common.exception.ConditionException;
import com.shenyan.common.exception.ConditionExceptionEnum;
import com.shenyan.service.RoleInfoService;
import com.shenyan.service.UserInfoService;
import com.shenyan.common.support.UserSupport;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;

@Order(1)
@Component
@Aspect
public class ApiLimitedRoleAspect {

    @Autowired
    private UserSupport mUserSupport;

    @Autowired
    private UserInfoService userInfoService;

    @Autowired
    private RoleInfoService mRoleInfoService;

    @Pointcut("@annotation(com.shenyan.domain.annotation.ApiLimitedRole)")
    public void check(){
    }

    @Before("check() && @annotation(apiLimitedRole)")
    public void doBefore(JoinPoint joinPoint, ApiLimitedRole apiLimitedRole) {
        UUID userId = mUserSupport.getCurrentUserId();
        LoginUserRespVo loginUserRespVo = userInfoService.queryUserById(userId);
        if (Objects.isNull(loginUserRespVo)) {
            throw new ConditionException(ConditionExceptionEnum.AUTHENTICATION_ERROR);
        }
        Long roleId = loginUserRespVo.getRoleId();
        RoleInfoRespVo roleInfoRespVo = mRoleInfoService.queryRole(roleId);

        String roleCode = roleInfoRespVo.getRoleCode();
        
        //获取当前接口需要的权限
        String limitedRoleCodeList = apiLimitedRole.limitedRoleCodeList();

        int i = limitedRoleCodeList.lastIndexOf("1");
        
        //判断当前用户是否具有此权限
        if (roleCode.charAt(i) != '1') {
            throw new ConditionException(ConditionExceptionEnum.AUTHENTICATION_NOT_ENOUGH);
        }
    }
}

2.自定义注解类

import org.springframework.stereotype.Component;

import java.lang.annotation.*;


@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
@Component
public @interface ApiLimitedRole {

    //权限列表
    String limitedRoleCodeList() default "";
}

3.创建常量类

这个类包含30种权限

package com.shenyan.common.constant;

public interface Role {
    /**
     * 主程序看板
     */
    public static final String MAIN_BOARD =
            "1";
    /**
     * 视频预览
     */
    public static final String VIDEO_PREVIEW =
            "01";
    /**
     * 设备查询
     */
    public static final String DEVICE_QUERY =
            "001";
    /**
     * 设备新增
     */
    public static final String DEVICE_ADD =
            "0001";
    /**
     * 设备修改
     */
    public static final String DEVICE_UPDATE =
            "00001";
    /**
     * 设备删除
     */
    public static final String DEVICE_DELETE =
            "000001";
    /**
     * 任务管理
     */
    public static final String TASK_QUERY =
            "0000001";
    /**
     * 任务新增
     */
    public static final String TASK_ADD =
            "00000001";

    /**
     * 任务修改
     */
    public static final String TASK_UPDATE =
            "000000001";
    /**
     * 任务删除
     */
    public static final String TASK_DELETE =
            "0000000001";
    /**
     * 事件查询
     */
    public static final String EVENT_QUERY =
            "00000000001";

    /**
     * 系统管理
     */
    public static final String SYSTEM_MANAGE =
            "000000000001";

    /**
     * 用户查询
     */
    public static final String USER_QUERY =
            "0000000000001";
    /**
     * 用户新增
     */
    public static final String USER_ADD =
            "00000000000001";
    /**
     * 用户修改
     */
    public static final String USER_UPDATE =
            "000000000000001";
    /**
     * 用户删除
     */
    public static final String USER_DELETE =
            "0000000000000001";
    /**
     * 角色查询
     */
    public static final String ROLE_QUERY =
            "00000000000000001";
    /**
     * 角色新增
     */
    public static final String ROLE_ADD =
            "000000000000000001";
    /**
     * 角色修改
     */
    public static final String ROLE_UPDATE =
            "0000000000000000001";
    /**
     * 角色删除
     */
    public static final String ROLE_DELETE =
            "00000000000000000001";
    /**
     * 看板查询
     */
    public static final String DASHBOARD_QUERY =
            "000000000000000000001";
    /**
     * 看板新增
     */
    public static final String DASHBOARD_ADD =
            "0000000000000000000001";
    /**
     * 看板修改
     */
    public static final String DASHBOARD_UPDATE =
            "00000000000000000000001";
    /**
     * 看板删除
     */
    public static final String DASHBOARD_DELETE =
            "000000000000000000000001";

    /**
     * 学校查询
     */
    public static final String SCHOOL_QUERY =
            "0000000000000000000000001";
    /**
     * 学校新增
     */
    public static final String SCHOOL_ADD =
            "00000000000000000000000001";
    /**
     * 学校修改
     */
    public static final String SCHOOL_UPDATE =
            "000000000000000000000000001";
    /**
     * 学校删除
     */
    public static final String SCHOOL_DELETE =
            "0000000000000000000000000001";
    /**
     * 添加设备
     */
    public static final String DEVICE_ADD1 =
            "00000000000000000000000000001";
    /**
     * 系统日志
     */
    public static final String SYSTEM_LOG =
            "000000000000000000000000000001";
}

4.自定义异常(运行时异常)

public class ConditionException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    private String code;

    public ConditionException(ConditionExceptionEnum conditionExceptionEnum){
       super(conditionExceptionEnum.getDesc());
       this.code = conditionExceptionEnum.getCode();
    }

    public ConditionException(String code, String name) {
        super(name);
        this.code = code;
    }


    public ConditionException(String name){
        super(name);
        code="500";
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
}

5.全局异常捕获

捕获自定义异常

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.shenyan.common.constant.Constants;
import com.shenyan.common.exception.ConditionException;
import com.shenyan.common.exception.ConditionExceptionEnum;
import com.shenyan.domain.JsonResponse;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.ResourceAccessException;

import javax.servlet.http.HttpServletRequest;


@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ComonGlobalExceptionHandler {

    @ExceptionHandler(value = ConditionException.class)
    @ResponseBody
    public JsonResponse<String> conditionException(ConditionException e){
        return JsonResponse.fail(e.getCode(),e.getMessage());
    }
}

6.Controller层使用

    @PostMapping("/getAllDevice/{page}/{pageSize}")
    @ApiLimitedRole(limitedRoleCodeList =Role.DEVICE_QUERY )
    public JsonResponse<Object> selectAllDevice() {
        
        return null;
 
    }

整个流程就是   

用户请求----》在controller层之前被Aop拦截验证是否符合------》不符合抛出异常------》全局异常捕获异常

  • 44
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
首先,我们需要定义一个自定义注解 `@RequiresPermissions`,用于标识需要授权访问的方法,例如: ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface RequiresPermissions { String[] value(); // 权限值 } ``` 然后,我们需要实现一个切面,用于拦截被 `@RequiresPermissions` 标识的方法,并进行权限校验,例如: ```java @Component @Aspect public class PermissionCheckAspect { @Autowired private AuthService authService; @Around("@annotation(requiresPermissions)") public Object checkPermission(ProceedingJoinPoint joinPoint, RequiresPermissions requiresPermissions) throws Throwable { // 获取当前用户 User user = authService.getCurrentUser(); if (user == null) { throw new UnauthorizedException("用户未登录"); } // 获取当前用户的权限列表 List<String> permissions = authService.getUserPermissions(user); // 校验权限 for (String permission : requiresPermissions.value()) { if (!permissions.contains(permission)) { throw new ForbiddenException("没有访问权限:" + permission); } } // 执行目标方法 return joinPoint.proceed(); } } ``` 在切面中,我们首先通过 `AuthService` 获取当前用户及其权限列表,然后校验当前用户是否拥有被 `@RequiresPermissions` 标识的方法所需的所有权限,如果没有则抛出 `ForbiddenException` 异常,如果有则继续执行目标方法。 最后,我们需要在 Spring 配置文件中启用 AOP 自动代理,并扫描切面所在的包,例如: ```xml <aop:aspectj-autoproxy /> <context:component-scan base-package="com.example.aspect" /> ``` 这样,我们就通过 Spring AOP自定义注解模拟实现了类似 Shiro 权限校验的功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值