AOP&JWT&全局异常处理

1. AOP

1.1 介绍

  1. Spring AOP => 底层是基于动态代理的
  2. AOP不是Spring独有的,AOP是Spring的核心之一
  3. 功能 : 在不修改原有类的基础上去对目标方法进行增强
  4. AOP实现必须要有接口,动态代理就是为了对功能进行扩展

1.2 AOP核心关键词

  原始对象   target       要被增强的方法的对象
  代理对象   proxy        方法被增强后的对象
  连接点    joinpoint      可能被增强的方法
  切点      pointcut      一定被增强的方法
  增强/通知  advice        具体增强的逻辑
  切面       aspect         切点+通知

1.3 入门

// 1.导入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

// 2. 
// 接口
public interface Person {
    void eat(int a);

    void shit();
}
//切面类
@Aspect
@Component
public class Advice {
    //切点 => 指向的 Person的eat方法
    @Pointcut("execution(* com.itheima.bootaop.aop.Person.shit(..))")
    public void pt(){}
    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp){
        Object result = null;
        try {
            //前置通知
            System.out.println("环绕通知===前置通知");
            result = pjp.proceed(); // 对标的是jdk动态代理的 method.invoke(对象,args) 对象.方法()
            //后置通知
            System.out.println("环绕通知===后置通知");
        }catch (Throwable e) {
            System.out.println("环绕通知===异常通知");
            //异常通知的逻辑
            throw new RuntimeException(e);
        } finally {
            System.out.println("环绕通知===最终通知");
            //最终通知的逻辑
        }
        return result;
    }
     // 前置通知
    @Before("pt()")
    public void before(){
       System.out.println("吃饭前洗手");
   }

    //后置通知
//    @AfterReturning("pt()")
//    public void afterReturning(){
//        System.out.println("拉完屎忘带纸了");
//    }


    //异常通知
    // 出了异常才会执行,异常和后置是互斥的!!!
//    @AfterThrowing("pt()")
//    public void afterThrowing(){
//        System.out.println("看看有水么");
//    }

    //最终通知
//    @After("pt()")
//    public void after(){
//        System.out.println("看看周围有人么?直接跑");
//    }
   
}
// target实现类
@Component
public class ZhangSan implements Person {
    @Override
    public void eat(int a) {
        System.out.println("张三在吃饭");
    }

    @Override
    public void shit() {
        System.out.println("张三拉屎");
        //int i = 1 / 0;
    }
}

注意
jdk的动态代理和cglib的区别

  1. jdk => target和proxy是兄弟关系,jdk是通过接口来创建的代理对象
  2. cglib => target 和 proxy是父子关系,这种情况下我们是不需要去使用接口来接收的

1.4 通知类型

Spring中AOP的通知类型:

  • @Around:环绕通知,此注解标注的通知方法在目标方法前、后都被执行
  • @Before:前置通知,此注解标注的通知方法在目标方法前被执行
  • @After :最终通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
  • @AfterReturning : 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行,与异常通知互斥
  • @AfterThrowing : 异常后通知,此注解标注的通知方法发生异常后执行

1.5 切入点表达式

形式 : “execution(* 切点方法名全路径(…))”

@Before("execution(* com.itheima.service.impl.DeptServiceImpl.delete(..))")
//delete方法中参数变成..代表任意参数

1.6 切入点表达式的语法规则:

    1. 方法的访问修饰符可以省略
    1. 返回值可以使用*号代替(任意返回值类型)
    1. 包名可以使用号代替,代表任意包(一层包使用一个
    1. 使用…配置包名,标识此包以及此包下的所有子包
    1. 类名可以使用*号代替,标识任意类
    1. 方法名可以使用*号代替,表示任意方法
    1. 可以使用 * 配置参数,一个任意类型的参数
    1. 可以使用… 配置参数,任意个任意类型的参数

2. JWT

JWT全称:JSON Web Token )

  • 定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。

简洁:是指jwt就是一个简单的字符串。可以在请求参数或者是请求头当中直接传递。
自包含:指的是jwt令牌,看似是一个随机的字符串,但是我们是可以根据自身的需求在jwt令牌中存储自定义的数据内容。如:可以直接在jwt令牌中存储用户的相关信息。
简单来讲,jwt就是将原始的json数据格式进行了安全的封装,这样就可以直接基于jwt在通信双方安全的进行信息传输了。

JWT的组成: (JWT令牌由三个部分组成,三个部分之间使用英文的点来分割)

  • 第一部分:Header(头), 记录令牌类型、签名算法等。 例如:{“alg”:“HS256”,“type”:“JWT”}
  • 第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。 例如:{“id”:“1”,“username”:“Tom”}
  • 第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。

签名的目的就是为了防jwt令牌被篡改,而正是因为jwt令牌最后一个部分数字签名的存在,所以整个jwt 令牌是非常安全可靠的。一旦jwt令牌当中任何一个部分、任何一个字符被篡改了,整个令牌在校验的时候都会失败,所以它是非常安全可靠的。

JWT是如何将原始的JSON格式数据,转变为字符串的呢?

其实在生成JWT令牌时,会对JSON格式的数据进行一次编码:进行base64编码

Base64:是一种基于64个可打印的字符来表示二进制数据的编码方式。既然能编码,那也就意味着也能解码。所使用的64个字符分别是A到Z、a到z、 0- 9,一个加号,一个斜杠,加起来就是64个字符。任何数据经过base64编码之后,最终就会通过这64个字符来表示。当然还有一个符号,那就是等号。等号它是一个补位的符号

需要注意的是Base64是编码方式,而不是加密方式。

2.1 生成和校验

1.引入依赖

<!-- JWT依赖-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

2. 生成

public void genJwt(){
    Map<String,Object> claims = new HashMap<>();
    claims.put("id",1);
    claims.put("username","Tom");
    
    String jwt = Jwts.builder()
        .setClaims(claims) //自定义内容(载荷)          
        .signWith(SignatureAlgorithm.HS256, "itheima") //签名算法        
        .setExpiration(new Date(System.currentTimeMillis() + 24*3600*1000)) //有效期   
        .compact();
    
    System.out.println(jwt);
}

3. 解析

public void parseJwt(){
    Claims claims = Jwts.parser()
        .setSigningKey("itheima")//指定签名密钥(必须保证和生成令牌时使用相同的签名密钥)  
	    .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjcyNzI5NzMwfQ.fHi0Ub8npbyt71UqLXDdLyipptLgxBUg_mSuGJtXtBk")
        .getBody();

    System.out.println(claims);
}

3. 全局异常处理

3.1 引入

三层架构中出现异常该如何处理?
全局异常处理器

3.2 定义

我们该怎么样定义全局异常处理器?

  • 定义全局异常处理器非常简单,就是定义一个类,在类上加上一个注解@RestControllerAdvice,加上这个注解就代表我们定义了一个全局异常处理器。
  • 在全局异常处理器当中,需要定义一个方法来捕获异常,在这个方法上需要加上注解@ExceptionHandler。通过@ExceptionHandler注解当中的value属性来指定我们要捕获的是哪一类型的异常。
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class) //指定能够处理的异常类型
    public Result handleException(Exception ex){
        return Result.ERR(ex.getMesssage());
    }
}

@RestControllerAdvice = @ControllerAdvice + @ResponseBody
处理异常的方法返回值会转换为json后再响应给前端

3.3 自定义异常

@RestControllerAdvice
public class GlobalExceptionProcessor {

    //===================下面2个异常输入自定义异常===================
    //业务异常 => 程序不是真的报错,我们是用过手动控制的方式来控制程序的走向和返回结果
    //这里的 @ExceptionHandler 注解需要指定要处理的是什么异常
    @ExceptionHandler(UserNotExistsException.class)
    public Result handleException(UserNotExistsException ex){
        return Result.ERR(ex.getMessage());
    }
    @ExceptionHandler(PasswordErrException.class)
    public Result handleException(PasswordErrException ex){
        return Result.ERR(ex.getMessage());
    }

    //===================下面这个异常是系统异常===================
    // 数据出现了重复,在sql直接报错,我们也是需要把结果处理成对用户比较友好的格式才能返回
    //系统异常处理起来一定要慎重!!!
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public Result handleException(SQLIntegrityConstraintViolationException ex){
        String message = ex.getMessage();
        //Duplicate entry 'tom' for key 'username'
        String errMsg = message.split(" ")[2] + "已存在";
        return Result.ERR(errMsg);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值