day31 Redis实现Session共享与MyBatis-Plus基础

1. 今天做了什么

  • 基于 Redis 实现 session 共享,替代原来的 Session 方案

  • 存验证码、校验验证码、存用户数据到 Redis

  • 修改拦截器逻辑:获取 user、存储到 ThreadLocal、刷新失效时间

  • 学习 MyBatis-Plus 的基本用法

2. 核心知识点

基于 Redis 实现 session 共享

存验证码:

1
2
3
//将验证码存入Redis
stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY+phone, code,
LOGIN_CODE_TTL, TimeUnit.MINUTES);//2分钟过期

校验验证码:

1
String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);

存用户数据:

1
2
3
4
5
6
7
8
9
10
11
12
//1. 生成token
String token = UUID.randomUUID(true).toString();
String tokenKey = LOGIN_USER_KEY + token;

//2.user转为map
UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
Map<String, Object> userDtoMap = BeanUtil.beanToMap(userDTO);

//3. 存储到Redis
stringRedisTemplate.opsForHash().putAll(tokenKey, userDtoMap);
//设置过期时间
stringRedisTemplate.expire(tokenKey,LOGIN_USER_TTL,TimeUnit.SECONDS);

怎样获取 map:.entries(token)

1
Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(token);

Redis 实现要考虑:

  • 合适数据结构

  • 合适 key

  • 存储粒度(去除敏感信息)

字符串判空为啥不用 ==null

1
2
3
4
String token = request.getHeader("authorization");
if(StrUtil.isBlank(token)){
return false;
}
  1. ==null 只能判断为 null 的情况
  2. StrUtil.isBlank()(Hutool 工具类)能判断:null、空串、以及仅包含空白字符(空格、制表符等)的字符串,统统返回 true
  3. isEmpty() 只判断长度是否为 0,对空格字符串返回 false

MyBatis-Plus 的基本用法

  1. 引入依赖
  2. 数据库配置
  3. 数据表准备,实体类准备
  4. 使数据表的字段与实体类的属性对应
  5. Mapper 接口继承 BaseMapper<User>,泛型里是该接口操作的数据表对应的实体类,即可使用 Mapper 层方法
1
2
3
4
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 只需继承 BaseMapper,无需编写任何 CRUD 方法!
}
  1. 除了 Mapper 层,MyBatis-Plus 还提供了通用的 Service 层接口 IService<T>。创建 Service 接口和实现类,即可使用 Service 层方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Service 接口
import com.baomidou.mybatisplus.extension.service.IService;

public interface UserService extends IService<User> {
// 可以扩展自定义方法
}

// Service 实现类
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
// 自定义方法实现
}

3. 深度思考

验证码登录后并未使验证码失效

  • 生成验证码,时间 2 分钟

  • 用户使用验证码登录后,并未使验证码失效

  • 解决:登录成功时移出验证码

刷新失效时间的操作放在一个拦截器里面存在问题

  • 用户访问不需要拦截的页面时不会刷新拦截器

  • 解决:

    • 拦截器 1 拦截所有请求,并查询信息,保存到 ThreadLocal,刷新

    • 拦截器 2 拦截需要拦截的请求,从 ThreadLocal 中查询数据

    • 配置拦截器,设置执行顺序

    • 注意 .order(0) 数字越小,优先级越高

4. 踩过的坑

运行报错:StringRedisTemplate 存 Long 类型报错

  • 原因:StringRedisTemplate 的 key、value 都必须是 String,而存的时候,Map<String, Object> userDtoMap = BeanUtil.beanToMap(userDTO); Map 里面存的 id 是 Long

  • 解决:

1
2
3
4
5
6
7
8
//2.user转为map
UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
Map<String, Object> userDtoMap = BeanUtil.beanToMap(
userDTO,new HashMap<>(),
CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor(
(fieldName, value) -> value.toString()
)
);

5. 遗留问题 / 想不通