springboot通过自定义注解实现AOP角色权限校验

通过SpringBoot自定义注解实现AOP角色权限校验之前,首先先要了解一下注解的基本知识:

Annotation是Java重要的组成部分,从J2SE 5.0时代就已经存在了。在我们的代码中,我们随处可以看到许多注解,例如@Autowired、@Override、@Service。这些注解我们可能非常熟悉,但是注解的作用、工作原理、工作方式以及我们如何自定义注解,我们可能并不熟悉。下面将逐步介绍:

1、什么是注解

注解是一种特殊的元数据,元数据是关于数据的数据,所以,注解就是代码的元数据。我们看下面这个例子:

@Override
public void process(SjscData sjscData) {
		String token = sjscData.getTokenID();
		String  rwmxid = sjscData.getNo();
}

我们用@Override注解process()方法,说明我们要重写process()方法。这里,即使我们不用@Override注解,这个方法任然可以正常执行。那注解的好处是什么呢?@Override告诉编译器,子类要重写这个方法,需要按照自己定义的格式输出内容,也就是覆盖了父类的方法,如果父类中没有这个方法,编译器就会报错。

所以,注解是一种特殊的Java构造器,它可以用于装饰类,方法,字段,参数,变量,构造函数或包。

2、为什么要使用注解

在注释之前(甚至之后),XML被广泛用于元数据。但是随着项目中代码量的增大,XML维护变得越来越麻烦。开发人员希望元数据与代码是紧密耦合,而XML与代码之间的耦合非常松散(在某些情况下,几乎是分开的)。

关于使用注解还是基于XML进行标记一直是一个具有争议的话题,对于开发人员来说,记住以下两个原则即可:

1、假设您要设置一些应用程序范围内的常量。在这种情况下,XML是一个更好的选择,因为它与任何特定的代码段都不相关。2、如果要将某个方法作为服务公开,则注解将是一个更好的选择,因为它需要与该方法紧密结合。另一个重要因素是,注解定义了在代码中定义元数据的标准方式。在注解之前,开发人员使用自己的方式定义元数据,这造成元数据定义混乱。但要注解是标准化的东西,更容易维护。

现在,大多数框架都将XML和注解结合使用,以充分利用两者的积极方面。

3、注解的工作方式

注解仅仅是元数据,不包括任何业务逻辑。那它的使用者是谁呢?像@Override这种注解,JVM是它的使用者,它在字节码级别工作。如果我们自己编写了一个注解,那我们就要去实现它的消费者,否则,我们自定义的注解是没有任何作用的。

4、编写自定义注解

在编写自定义注解的时候,我们首先需要搞清楚几个默认的注解,这几个注解仅仅作用与另一个注解之上。

@Documented

@Documented注解表明这个注解要被javadoc记录。注解默认状态下是不被javadoc记录的。

@Retention

注解表明该注解保留到那个阶段,主要有三个值:

SOURCE —— 这种注解保留在源代码级别,编译时就会被忽略

CLASS —— 这种注解编译时被保留,在class文件中存在,但JVM将会忽略

RUNTIME —— 这种注解将被JVM保留,利用反射机制可以获取并使用。

@Target

@Target注解表明该注解作用的范围。包括package、method、field、构造方法、成员变量、枚举值等属性。

@Inherited

@Inherited注解表明该注解是否影响子类。如果定义的注解上使用了@Inherited标记,则使用该注解的某个父类,它的子类默认继承所有的属性

了解完自定义注解需要使用到的注解,我们开始自定义注解。

SpringBoot工程  

POM.xml 中引入以下依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

自定义注解

package com.zhanglf.aop.annotation;

import java.lang.annotation.*;

/**
 * 使用注解统一校验角色权限
 * @author zhanglf
 * @date 2019-04-29
 */

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PermissionCheck {
    //自定义角色值,如果是多个角色,用逗号分割。
    public String role() default "";
}

aop绑定自定义注解。并在aop中实现处理逻辑

package com.jd.mee.catalog.aop;


import com.zhanglf.aop.annotation.PermissionCheck;
import com.zhanglf.common.result.CodeMsg;
import com.zhanglf.common.util.JwtUtil;
import com.zhanglf.entity.UserInfo;
import com.zhanglf.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/**
 * 角色权限校验-AOP
 * @author  zhanglf
 * @date 2019-04-29
 */
@Aspect
@Component
@Slf4j
public class PermissionCheckAspect {

    //切入点表达式决定了用注解方式的方法切还是针对某个路径下的所有类和方法进行切,方法必须是返回void类型
    @Pointcut("@annotation(com.zhanglf.aop.annotation.PermissionCheck)")
    private void permissionCheckCut(){};

    //定义了切面的处理逻辑。即方法上加了@PermissionCheck
    @Around("permissionCheckCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        log.info("==========哈哈哈,进入AOP============================");
        //1.记录日志信息
        Signature signature = pjp.getSignature();
        String className = pjp.getTarget().getClass().getSimpleName();
        String methodName = signature.getName();
        log.info("className:{},methodName:{}",className,methodName);

        //2.角色权限校验
        MethodSignature methodSignature = (MethodSignature)signature;
        Method targetMethod = methodSignature.getMethod();
        if(targetMethod.isAnnotationPresent(PermissionCheck.class)){
            //获取方法上注解中表明的权限
            PermissionCheck permission = (PermissionCheck)targetMethod.getAnnotation(PermissionCheck.class);
            String role = permission.role();
            log.info("当前接口请求的用户角色role:{}",role);
            if(StringUtils.isNotEmpty(role)){
                String[] roles = role.split(",");//接口允许的角色
                List<String> list = Arrays.asList(roles);
                //如果该接口只允许老师角色访问。则要获取当前用户是不是老师角色。
                HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
                String authorization = request.getHeader("Authorization");
                UserInfo userInfo = JwtUtil.parseAccessToken(authorization);
                String userRole = userInfo.getUserRole();//用户的角色
                if(list.contains(userRole)){
                    log.info("AOP权限角色校验通过,进入业务层处理!");
                    //3.执行业务逻辑,放行
                    return pjp.proceed();
                }else{
                    //如果没有权限,抛出异常,由Spring框架捕获,跳转到错误页面
                    throw new BizException(CodeMsg.ROLE_HAVE_NO_PERMISSION);
                }
            }
        }
        throw new BizException(CodeMsg.NO_ROLE_CONFIGER);
    }

}

aop的使用:用在接口层校验登陆的角色是否有权限使用该接口

参考资料:

(45条消息)java注解学习---元注解,注解类型,注解支持元素类型,注解基础(一)_Java_HaHa_Sir的博客-CSDN博客

Java注解的工作原理及如何自定义注解

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
校验Java中的Token有效性,可以按照以下步骤进行: 1. 获取Token:从请求中获取Token。通常,Token会包含在请求的头部(Header)或者请求参数中。 2. 解析Token:使用相应的库(例如JWT)解析Token,获取其中的信息。解析过程会验证Token的签名和有效期。 3. 验证签名:如果Token被签名,需要验证签名的合法性。这通常涉及到使用事先共享的密钥对Token进行解密和验证。 4. 验证有效期:检查Token的有效期是否过期。可以通过比较当前时间与Token中的过期时间进行判断。 以下是一个示例代码,使用Java JWT库进行Token校验的过程: ```java import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.MalformedJwtException; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.SignatureException; import io.jsonwebtoken.UnsupportedJwtException; public class TokenValidator { private static final String SECRET_KEY = "your-secret-key"; public static boolean validateToken(String token) { try { Claims claims = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody(); // 可以在这里添加其他校验逻辑,例如检查Token中的角色信息等 return true; } catch (ExpiredJwtException | MalformedJwtException | SignatureException | UnsupportedJwtException | IllegalArgumentException e) { return false; } } } ``` 在上面的示例中,`validateToken`方法接收一个Token作为参数,使用`Jwts.parser()`方法解析Token,并使用事先共享的密钥`SECRET_KEY`进行签名验证。如果Token验证通过,返回`true`;否则,返回`false`。 请注意,`SECRET_KEY`是一个关键的配置项,应该妥善保管,并且确保不被泄露。此外,示例中只是简单示例,你可以根据实际需求对Token的校验逻辑进行扩展和定制化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李晓LOVE向阳

你的鼓励是我持续的不断动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值