昨日回顾

拦截器

只需要记住,在SpringBoot中如何配置一个拦截器。

1、拦截器的作用(项目中有没有用到过拦截器,用它来解决什么问题)?

在调用接口之前被触发。在调用接口之后被触发。

tokne校验,权限校验,登录拦截

全局异常管理器

只需要记住,在SpringBoot中如何配置一个全局异常管理器。

它的作用什么?项目中用它来解决什么问题?

把项目中遇到的异常可以全部的抛出,到全局异常管理器来统一的处理。

资源相关的资源需要就地解决,不能抛出。

全局异常管理器是如何处理异常的?

先做异常的判断,判断它是系统异常还是业务异常,如果是系统异常记录错误的日志,然后给用户响应系统正在维护,请稍后再试。

如果是业务异常就直接把异常信息抛给用户就可以了。

restful风格

资源定位(url),资源的操作(method)。

查询:get

添加:post

修改:Put

删除:DLETEE

接受和响应的数据都是JSON格式。

优势在于风格非常的简洁,可读性更高。

Session登录的原理

1、为什么会有Cookie和Session的处理?

因为HTTP是无状态的。

2、Session的底层是通过Cookie来实现的。

3、在集群的情况下Seesion等会出现无法共享的问题。

JWT

JSON Web Token

它可以帮助我们生成一个Token,而且这个token可以有过期时间,可以揉入一些数据进去,最终生成一个字符串。

JWT石油三部分组成,头,载荷,签名。

之前的Session是如何校验的?

登录成功后再服务端创建一个Session,每个Session会有一个JSESSIONID,session是保存在服务端的,然后把JSESSIONid给客户端,下次访问的时候在Cookie中携带,在服务端就可以获取到JESSIONid然后和服务端自己保存的做一个比对。如果是正确就说明合法的,如果不正确就说明这个被改过没有登录。

JWT可以自校验,它是怎么做到的?

认证成功后使用JWT生成一个token,给token中揉入了一些用户的数据,直接把tokne返回给客户端了,服务端没有保存。客户端下次请求的时候在氢气头中携带token,服务端获取到token后,因为服务端没有保存token,此时它是如何校验的token合法性的?

JWT生成token的过程?

1、先把头通过base64编码,得到一个字符串

2、再把载荷通过base64编码,得到一个字符串

3、使用用户签名把把头和载荷(base64后的字符串)进行一个签名,得到一个字符串。

header.payload.签名

如何自校验的?

校验的时候需要传递token字符串。

1、解析出头部

2、解析出载荷

3、使用用户签名把把头和载荷(base64后的字符串)进行一个签名,得到一个字符串。

header.payload.签名

今日内容

画图

https://www.processon.com/view/link/6538d35ffc67b11c9006d1e8

base64编码

package com.qf.application;

import org.junit.jupiter.api.Test;
import org.springframework.util.Base64Utils;
import org.springframework.util.FileCopyUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.util.Base64;

public class Base64Test {

    // 解码
    private static Base64.Decoder decoder = Base64.getDecoder();

    // 编码
    private static Base64.Encoder encoder = Base64.getEncoder();

    @Test
    public void test1() throws Exception {

        // 1.定义一个字符串
        String msg = "Hello 你好";

        // 2.把这个字符串给我使用Base64编码
        byte[] decode1 = encoder.encode(msg.getBytes("utf8"));

        // 3.把字节数组转成一个字符串
        String encoderString = new String(decode1, 0, decode1.length, "utf-8");

        System.out.println("base64编码后的内容:" + encoderString);

        // 4.解码
        byte[] decode = decoder.decode(encoderString);

        // 5.把字节数组转成字符串
        String decodeString = new String(decode, 0, decode.length, "utf-8");
        System.out.println("解码后的内容:" + decodeString);

    }

    @Test
    public void test2() throws Exception {

        // 二进制文件
        String png = "C:\\Users\\dashixin\\Pictures\\4k\\7d7c48801531ede23f3011207b3000a4--850566657.jpg";

        // 把二级制文件读取到一个字节数组中
        byte[] bytes = FileCopyUtils.copyToByteArray(new File(png));

        // 把图片的字节数组进行base64的编码
        byte[] decode1 = encoder.encode(bytes);

        String str = new String(decode1,0,decode1.length);
        System.out.println("图片进行base64编码后的内容:"+str);


        byte[] decode = decoder.decode(str);
        new FileOutputStream("./demo.png").write(decode);

    }
}

加密算法

        <dependency>
            <groupId>org.mindrot</groupId>
            <artifactId>jbcrypt</artifactId>
            <version>0.4</version>
        </dependency>

该加密算法的优点

1、自带盐值,盐值的作用就是解决明文一样,加密算法一样,加入颜值,最终得到一个密文不一样。

2、防止暴力破解

服务端四个框架都干了什么事情?

​ 用户在前端点击了删除按钮,发送一个请求,在请求中携带的需要删除数据的id。服务端接收到这个请求后进行删除。

服务端用的技术:SpringBoot,Spring,MyBaits,SpringMVC,MySQL,MP。

整体来看服务端处理了删除的请求,问的事服务端处理这个请求的过程中,以上框架都干了那些事情?

SpringBoot:自动装配(以前需要在XML文件中配置很多的Bean),它干的事情更多在项目初始化的时候。

SpringMVC:根据请求地址找到对应的Controller,并且做些数据的封装(以前是Servlet干的)。

Spring:Bean的创建和管理,代理,依赖注入(之前自己new的)。

MyBatis:操作数据库(之前是JDBC)

MP:通用的CRUD接口,分页操作,条件查询(开发人员自己写的)

MySQL:做数据的持久化,保存在一个物理的设备上面(硬盘)

@ControllerAdvice是啥意思?

它是SpringMVC提供的一个注解,用该注解修饰的类表示是一个全局异常管理器。

一般该注解和@ExceptionHandler配合使用的。

全局异常管理器的原理?出现一个异常后是怎么处理的。

1、Controller或者service抛出一个异常

2、如果有全局异常管理器存在,该异常就会被捕获,如果没有就直接抛给用户了。

3、在全局异常管理器中通过@ExceptionHandler来区分异常的类型

4、在对应的方法中进行异常的处理。

它底层就是AOP代理

image-20231025143137823

全局异常底层的实现伪代码

try{
   doController(); // 最终所有的异常都会在这里进捕获
}catch(EmpException e){
    // 调用全局异常管理器的业务异常的方法
}catch(Exception e){
    // 调用全局异常管理器的系统异常的方法
}

   public void doController(){
       empController.save("xxx");
   };

使用token登录后如何在controller或者service中获取当前登录用户

以前在用户信息放在session中,直接从session中获取即可。

现在用户的信息是放在请求头中的,可以从请求头中取出来,但是取出来是一个token字符串,还是需要解析的。

public class UserTokenThreadLocal {

    private static ThreadLocal<UserToken> threadLocal = new ThreadLocal<>();

    public static void set(UserToken token) {
        threadLocal.set(token);
    }

    public static UserToken get() {
        return threadLocal.get();
    }
}

在拦截器中解析token,然后保存到ThreadLocal中

package com.qf.application.interceptor;

import com.qf.application.core.exception.TokenNullException;
import com.qf.application.core.security.UserTokenThreadLocal;
import com.qf.application.entity.UserToken;
import com.qf.application.core.utils.JWTUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class TokenInterceptor implements HandlerInterceptor {

    @Autowired
    private JWTUtils jwtUtils;

    /**
     * 调用后台接口之前判断token是否合法,合法就调用
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // 1.获取用户携带的token
        String token = request.getHeader("token");

        // 2.非空判断
        if (StringUtils.isEmpty(token)) {
            throw new TokenNullException(1001, "token为空");
        }

        // 3.校验token的合法性
        UserToken userToken = jwtUtils.check(token);

        // 4.把userToken保存在ThreadLocal中
        UserTokenThreadLocal.set(userToken);
        return true;
    }
}

使用的时候直接去THreadLocal获取即可

    @PostMapping("/page")
    public R empPage(@RequestBody Page<Emp> page) {
        // 获取当前登录用户
        UserToken userToken = UserTokenThreadLocal.get();
        log.debug("userToken:{}",userToken);

        return R.success(empService.page(page));
    }

controller统一的包装


@Slf4j
@ControllerAdvice
// ResponseBodyAdvice<Object> 这里的泛型指的是Controller的返回值
public class ResponceResultAdvices implements ResponseBodyAdvice<Object> {

    // 判断条件,它返回true才能执行beforeBodyWrite的方法
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    /**
     * @param body
     * @param returnType
     * @param selectedContentType
     * @param selectedConverterType
     * @param request
     * @param response
     * @return
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
//        log.debug("Controller执行完了:{}", body);

        // controller中没有返回值
        if (body == null) {
            return null;
        }
        // Controller返回的内容已经进行R的包装
        if (body instanceof R) {
            return body;
        }
        return R.success(body);
    }
}
    @GetMapping
    public List<Emp> empList() {
        return empService.list();
    }

    @GetMapping("/{id}")
    public Emp info(@PathVariable Integer id) {
        return empService.getById(id);
    }

    @PostMapping("/page")
    public Page<Emp> empPage(@RequestBody Page<Emp> page) {
        // 获取当前登录用户
        UserToken userToken = UserTokenThreadLocal.get();
        log.debug("userToken:{}", userToken);

        return empService.page(page);
    }

Jemeter

它是压力测试工具

https://jmeter.apache.org/

image-20231025161719781

image-20231025161737048

权限管理系统(RBAC)

RBAC基于校色来控制的权限系统。

权限控制本质就是控制主后台接口的访问,它有这个权限就正常访问,没有权限提示它无权访问。

1、角色表

image-20231025162459801

2、用户表

image-20231025162514119

3、权限表

image-20231025162805828

4、角色和权限的中间表

image-20231025163359821

5、角色和用户表

image-20231025163639712

image-20231025163701184

作业

完成用户,角色,权限的CRUD操作。MP代码生成器用起来。