昨日回顾
拦截器
只需要记住,在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代理
全局异常底层的实现伪代码
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/
权限管理系统(RBAC)
RBAC基于校色来控制的权限系统。
权限控制本质就是控制主后台接口的访问,它有这个权限就正常访问,没有权限提示它无权访问。
1、角色表
2、用户表
3、权限表
4、角色和权限的中间表
5、角色和用户表
作业
完成用户,角色,权限的CRUD操作。MP代码生成器用起来。