自定义注解+AOP实现权限控制

应用场景二:使用注解+AOP对权限的校验

在实际开发中,我们常常需要对方法进行权限控制,就比如对用户身份的校验,判断其是不是管理员身份,此时我们就可以使用自定义注解+AOP进行权限的校验

接下来我们开始演示一个Demo,管理员能够访问用户和管理员对应的接口,而用户只能访问用户的接口,并且还对登录的用户的用户名和密码进行校验

使用到的依赖

<properties>
        <java.version>1.8</java.version>
        <spring-aop.version>5.2.8.RELEASE</spring-aop.version>
        <aspectjweaver.version>1.8.7</aspectjweaver.version>
</properties>
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.5</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring-aop.version}</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
    </dependencies>

自定义@AdminLoginToken注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AdminLoginToken {
    @AliasFor("admin")
    boolean value() default true;

    @AliasFor("value")
    boolean admin() default true;
}

编写两个接口

@RestController
public class UserController {

    /**
     * 该接口用户和管理员都可以进行访问
     * @return
     */

    @GetMapping("/user")
    @AdminLoginToken(false)
    public TransDTO userLogin(){
        return new TransDTO<>().withMessage("用户页面访问成功!").withSuccess(true).withCode(200);
    }

    /**
     * 该接口只有管理员可以进行访问
     * @return
     */
    @AdminLoginToken()
    @GetMapping("/admin")
    public TransDTO adminLogin(){
        return new TransDTO().withCode(200).withSuccess(true).withMessage("管理员页面访问成功!");
    }
}

对AOP进行配置

@Component
@Aspect
public class AdminLoginTokenAOP {

    /**
     * 配置切入点表达式
     */
    @Pointcut("@annotation(com.lq.annotationspringboot.annotation.AdminLoginToken)")
    public void loginPointCut(){

    }

    /**
     * 配置环绕通知
     * @param point
     * @return
     */
    @Around("loginPointCut()")
    public Object tokenHandler(ProceedingJoinPoint point) throws Throwable {
        //1、获取请求对象
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();

        //2、获取请求头
        String loginToken = request.getHeader("login_token");

        //3、获取注解权限属性
        Class<?> aClass = point.getTarget().getClass();
        //获取方法签名(通过此签名获得目标方法信息)
        MethodSignature ms = (MethodSignature)point.getSignature();
        Method method = aClass.getDeclaredMethod(ms.getName(),ms.getParameterTypes());
        //获取注解
        AdminLoginToken annotation = method.getAnnotation(AdminLoginToken.class);
        //获取注解属性
        boolean admin = annotation.value();
        //返回结果
        Object o = null;

        //这里可以根据和前端约定的格式对请求头进行解析后查询数据库进行解析,也可以使用JWT自校验框架进行token校验,我们为了演示方便直接使用user和admin
        if(!loginToken.equals("admin") && !loginToken.equals("user")){
            throw new BusinessException("访问失败,用户校验失败!");
        }
        if(admin){
            if(!loginToken.equals("admin")){
                throw new BusinessException("访问失败,只有管理员才能进行访问");
            }
            //继续执行方法
            o = point.proceed(point.getArgs());
        }else {
            //继续执行方法
            o = point.proceed(point.getArgs());
        }
        return o;
    }
}

业务异常类

public class BusinessException extends RuntimeException{

    private String message;

    private Integer code;

    public BusinessException(String message){
        super(message);
        message = "业务异常"+message;
        this.code = -4000;
        this.message = message;
    }
}

全局异常处理

@RestControllerAdvice
public class ExceptionAdvice {

    @ExceptionHandler(Exception.class)
    public TransDTO handleException(HttpServletRequest request,Exception e){
        e.printStackTrace();
        return new TransDTO().withCode(500).withMessage(e.getMessage()).withSuccess(false);
    }
}

与前端交互标准对象

package com.lq.annotationspringboot.dto;
public class TransDTO<T> {
    private String message;
    private Integer code;
    private T date;
    private Boolean success;
    public TransDTO withMessage(String message){
        this.message = message;
        return this;
    }
    public TransDTO withCode(Integer code){
        this.code = code;
        return this;
    }
    public TransDTO withDate(T date){
        this.date = date;
        return this;
    }
    public TransDTO withSuccess(Boolean success){
        this.success = success;
        return this;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    public T getDate() {
        return date;
    }
    public void setDate(T date) {
        this.date = date;
    }
    public Boolean getSuccess() {
        return success;
    }
    public void setSuccess(Boolean success) {
        this.success = success;
    }
}

用户访问用户页面结果

管理员访问用户页面

用户访问管理员页面

管理员访问管理员页面

这样我们就实现了使用自定义注解+AOP实现权限的校验了,只需要在需要进行校验的接口上添加注解即可!

posted @ 2021-06-27 01:15  熬夜总冠军  阅读(1972)  评论(1编辑  收藏  举报