集训总结2

一些重要的

JWT

简介

全称: JSON Web Token(https://jwt.io/)

定义了一种简洁的、自包含的格式,用于通信双方以 json 数据格式安全的传输信息。

组成:

第一部分: Header (头) ,记录令牌类型、签名算法等。例如: {"alg":"HS256","type":"JWT"}

第二部分: Payload (有效载荷) ,携带一些自定义信息、默认信息等。例如: {"id":"1","username":"Tom"}

第三部分: Signature (签名) ,防止 Token 被篡改、确保安全性。将 header、payload,并加入指定秘钥,通过指定签名算法计算而来。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.JTdCJTIybmFtZSUyMiUzQSUyMlRvcSUyMiUyQyUyMm1hdCUyMiUzQTE1MTYyMzkwMjIlNQ=.Sf1KxwRJ5MeKKF2QT4fwpMeJf...

Base64: 是一种基于 64 个可打印字符(A-Z a-z 0-9 + /)来表示二进制数据的编码方式。

如果篡改了头部和载荷部分的数据,那么验证失败

如果秘钥改了,验证失败

注意事项

JWT 校验时使用的签名秘钥,必须和生成 JWT 令牌时使用的秘钥是配套的。

如果 JWT 令牌解析校验时报错,则说明 JWT 令牌被篡改 或 失效了,令牌非法。

Spring Validation对注册接口的参数校验

  1. 引入Spring Validation 起步依赖
  1. 在参数前面添加@Pattern注解

  2. 在Controller类上添加@Validated注解

  1. 在全局异常处理器中处理参数校验失败的异常

ThreadLocal

提供线程局部变量

用来存取数据: set()/get()

使用ThreadLocal存储的数据,线程安全

用完记得调用remove方法释放

public class ThreadLocalTest {

@Test

public void testThreadLocalSetAndGet() {

//同一个ThreadLocal对象为两个线程提供供存储线程的空间,达到线程隔离

//提供一个ThreadLocal对象

ThreadLocal tl = new ThreadLocal();

//开启两个线程

new Thread(() -> {

tl.set("萧炎");

System.out.println(Thread.currentThread().getName() + ": " + tl.get());

System.out.println(Thread.currentThread().getName() + ": " + tl.get());

System.out.println(Thread.currentThread().getName() + ": " + tl.get());

}, "蓝色").start();

new Thread(() -> {

tl.set("药尘");

System.out.println(Thread.currentThread().getName() + ": " + tl.get());

System.out.println(Thread.currentThread().getName() + ": " + tl.get());

System.out.println(Thread.currentThread().getName() + ": " + tl.get());

}, "绿色").start();

}

}

分组校验

更新文章分类和添加文章分类都@Validated了同一个规则

但添加时不需要传入id,更新时需要传入id,导致id校验规则矛盾

分组校验

把校验项进行归类分组,在完成不同的功能的时候,校验指定组中的校验项

  1. 定义分组

  2. 定义校验项时指定归属的分组

  3. 校验时指定要校验的分组

public class Category {

public interface Add {

}

public interface Update {

}

}

public class Category {

@NotNull(groups = Update.class)

private Integer id;//主键ID

@NotEmpty(groups = {Add.class, Update.class})

@Pattern(regexp = "^\\S{1,10}$", groups = {Add.class, Update.class})

private String categoryName;//分类名称

@NotEmpty(groups = {Add.class, Update.class})

@Pattern(regexp = "^\\S{1,10}$", groups = {Add.class, Update.class})

private String categoryAlias;//分类别名

}

@PutMapping

public Result update(@RequestBody @Validated(Category.Update.class) Category category) {

categoryService.update(category);

return Result.success();

}

注意事项

定义校验项时如果没有指定分组,则属于Default分组,分组可以继承

public class Category {

public interface Add extends Default{

}

public interface Update extends Default{

}

}

封装到实体类对象中的参数如何校验

注解 作用

NotNull 值不能为 null

NotEmpty 值不能为 null, 并且内容不为空

Email 满足邮箱格式

参数校验

1.在实体类中添加注解

@Data

public class User {

@NotNull

private Integer id;//主键ID

private String username;//用户名

private String password;//密码

@NotEmpty

@Pattern(regexp = "^\\S{1,10}$")

private String nickname;//昵称

@NotEmpty

@Email

private String email;//邮箱

private String userPic;//用户头像地址

private LocalDateTime createTime;//创建时间

private LocalDateTime updateTime;//更新时间

}

2.在具体使用实体类的前面加上@Validated注解

@PutMapping("/update")

public Result update(@RequestBody @Validated User user){

userService.update(user);

return Result.success();

}

自定义校验

已有的注解不能满足所有的校验需求,特殊的情况需要自定义校验 (自定义校验注解)

1.自定义注解 State

2.创建自定义校验数据的类 StateValidation

实现 ConstraintValidator 接口

3.在需要校验的地方使用自定义注解

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

@Constraint(validatedBy = StateValidation.class)

public @interface State {

String message() default "文章状态只能是:已发布或者草稿";

Class[] groups() default {};

Class<? extends Payload>[] payload() default {};

}

public class StateValidation implements ConstraintValidator<State, String> {

@Override

public boolean isValid(String value, ConstraintValidatorContext context) {

if (value == null) {

return false;

}

if (value.equals("已发布") || value.equals("草稿")) {

return true;

}

return false;

}

}

@Data

public class Article {

private Integer id;//主键ID

private String title;//文章标题

private String content;//文章内容

private String coverImg;//封面图像

@State

private String state;//发布状态 已发布|草稿

private Integer categoryId;//文章分类id

private Integer createUser;//创建人ID

private LocalDateTime createTime;//创建时间

private LocalDateTime updateTime;//更新时间

}

令牌主动失效机制

登录的问题:

用户两次登录后会生成新旧两个令牌,此时旧的不应该生效

要使旧的失效:

令牌主动失效机制

登录成功后,给浏览器响应令牌的同时,把该令牌存储到redis中

LoginInterceptor拦截器中,需要验证浏览器携带的令牌,并同时需要获取到redis中存储的与之相同的令牌

当用户修改密码成功后,删除redis中存储的旧令牌

SpringBoot 集成 redis

1导入 spring-boot-starter-data-redis 起步依赖

2.在 yml 配置文件中,配置 redis 连接信息

3.调用 API (StringRedisTemplate) 完成字符串的存取操作

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-redis</artifactId>

</dependency>

application.yml

spring:

data:

redis:

host: localhost

port: 6379

@Test

public void testStringSet(){

stringRedisTemplate.opsForValue().set("username","如花");

}

@Test

public void testStringGet(){

stringRedisTemplate.opsForValue().get("username");

}

SpringBoot多环境开发

开发、测试、生产三种环境下配置信息是不同的

多环境开发 - Profiles

SpringBoot 提供的 Profiles 可以用来隔离应用程序配置的各个部分,并在特定环境下指定部分配置生效

如何分隔不同环境的配置?

使用三个横杠 ---

如何指定哪些配置属于哪个环境?

spring:

config:

activate:

on-profile: 环境名称

如何指定哪个环境的配置生效?

spring:

profiles:

active: 环境名称

相关推荐
丶小鱼丶27 分钟前
栈算法之【有效括号】
java·算法
慢慢沉29 分钟前
Lua(数据库访问)
开发语言·数据库·lua
郝学胜-神的一滴1 小时前
SpringBoot实战指南:从快速入门到生产级部署(2025最新版)
java·spring boot·后端·程序人生
鼠鼠我捏,要死了捏4 小时前
Java 虚拟线程在高并发微服务中的实战经验分享
java·microservices·virtualthreads
武子康4 小时前
Java-82 深入浅出 MySQL 内部架构:服务层、存储引擎与文件系统全覆盖
java·开发语言·数据库·学习·mysql·spring·微服务
Rancemy4 小时前
rabbitmq 03
java·分布式·rabbitmq
Dcs6 小时前
“SQL注入即服务”:一个10年历史系统的奇幻演变
java
秃了也弱了。6 小时前
reflections:Java非常好用的反射工具包
java·开发语言
vdoi7 小时前
【Mysql】 Mysql zip解压版 Win11 安装备忘
数据库·mysql
TDengine (老段)7 小时前
TDengine 转化类函数 TO_CHAR 用户手册
大数据·数据库·物联网·时序数据库·tdengine·涛思数据