搞懂Session就是现在!
小序
第二篇文章是前后端的交互出发,讲述了服务间的通信:
而本篇文章将会告诉您:
- session到底是个啥?
- session 与 jwt的区别?
正文
Session
以登录功能为例(详细可以看第一篇文章,文章中有git地址,是一个最简单的企业级项目——用户管理系统)
当然,并不会影响您阅读本篇文章的体验!
controller层
@PostMapping("/login")
public BaseResponse<User> userLogin(@RequestBody UserLoginRequest userLoginRequest, HttpServletRequest request) {
if (userLoginRequest == null) {
return ResultUtils.error(ErrorCode.PARAMS_ERROR);
}
String userAccount = userLoginRequest.getUserAccount();
String userPassword = userLoginRequest.getUserPassword();
if (StringUtils.isAnyBlank(userAccount, userPassword)) {
return ResultUtils.error(ErrorCode.PARAMS_ERROR);
}
User user = userService.userLogin(userAccount, userPassword, request);
return ResultUtils.success(user);
}
service接口
/**
* 用户登录
*
* @param userAccount 用户账户
* @param userPassword 用户密码
* @param request
* @return 脱敏后的用户信息
*/
User userLogin(String userAccount, String userPassword, HttpServletRequest request);
service实现层
@Override
public User userLogin(String userAccount, String userPassword, HttpServletRequest request) {
// 1. 校验
if (StringUtils.isAnyBlank(userAccount, userPassword)) {
return null;
}
if (userAccount.length() < 4) {
return null;
}
if (userPassword.length() < 8) {
return null;
}
// 账户不能包含特殊字符
String validPattern = "[`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]";
Matcher matcher = Pattern.compile(validPattern).matcher(userAccount);
if (matcher.find()) {
return null;
}
// 2. 加密
String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
// 查询用户是否存在
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userAccount", userAccount);
queryWrapper.eq("userPassword", encryptPassword);
User user = userMapper.selectOne(queryWrapper);
// 用户不存在
if (user == null) {
log.info("user login failed, userAccount cannot match userPassword");
return null;
}
// 3. 用户脱敏
User safetyUser = getSafetyUser(user);
// 4. 记录用户的登录态
request.getSession().setAttribute(USER_LOGIN_STATE, safetyUser);
return safetyUser;
}
最重要的也就是这句话:
request.getSession().setAttribute(USER_LOGIN_STATE, safetyUser);
它的意思就是将其保存在,session服务器中
而后续前端所携带的cookie中就有这个 通过session的key我们就能获取到用户信息!
以上 就是 刚刚的login功能,通过 Cookie 中 JSESSIONID 获取到的用户信息
而所谓的注销则是移除session中的数据
/**
* 用户注销
* @param request
*/
@Override
public int userLogout(HttpServletRequest request) {
// 移除登录态
request.getSession().removeAttribute(USER_LOGIN_STATE);
return 1;
}
Session的问题
细心的小伙伴应该就会发现,session应该也有问题吧,没错 我们看到session就是保存在服务器中的,而session代表的是一次会话,无论是在分布式的应用场景中又或者服务器重启后都会面临session丢失的问题!
分布式场景如下:
保存在不同的服务中无法共享session!
也就是说A服务器存储一个用户的服务,当我们网购的时候打开订单时(此时在B服务器) 但是B服务器 并不认识 A服务器中记载的session
不知道读者能不能理解!!!
Redis+Session
没错通过引入的redis,我们改变了session的存储模式
由以前的存储在服务器上的模式,更改为存储在数据库中
那么你看哈,引入redis 我们肯定需要下载redis 对吧
下载也很简单,我们如果没有服务器的话可以下载redis Windows 版本 地址如下:
当然,如果您电脑中有reids 那么可以忽略这一步!
下面我们已经下载好了,接下来该怎么办呢? 别急、如果我们想要在spring项目中使用redis 只需要引入一下依赖就好了!
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.6.4</version>
</dependency>
不知道大家还记不记的starter的命名格式呢?
- spring-boot-starter-xx 表示这是一个spring官方的starter
- xx-spring-boot-starter 这是指非spring官方的starter
也就是说明,上一个依赖是spring官方封装的starter
除此之外还有一些第三方的封装的redis 比如 redssion(适合分布式)jedis (速度快)
等等……
我们引入了redis 的依赖,表面我们已经可以将数据写入session了对吧。
这里,如果大家学过redis的话,知道读写命令 可能会将session 自己手动写入redis ~
但是,这么简单的事情,肯定会有别人帮我们考虑到!
所以我们还需要引入一个依赖:
<!-- https://mvnrepository.com/artifact/org.springframework.session/spring-session-data-redis -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>2.6.3</version>
</dependency>
这个依赖的作用就是,我们不需要执行任何操作,当我们保存在session中的数据,会经过上面的这个依赖,它会自动帮我们写入redis中,我们只需要做的就是在yaml中配置redis:
spring:
redis:
port: 6379
host: localhost
database: 1
jwt与session的区别
其实jwt与session最大的区别就是jwt的无状态性
那么什么是无状态性呢?就是说我们使用它来进行存储更加的轻量化,几乎不占用内存只是存储在前端的东西多点!,你可以将jwt理解成一个session整体(key+用户信息)
没错 前后端的交互都会携带着jwt 我们可以通过jwt 直接获取到用户数据
当然只有key+用户信息是不安全的,所以完整的jwt还会包含着加密的数据~
感兴趣可以看下jwt由哪三部分组成~ 我就不讲解了、因为这些都能够在网上搜到、讲解的意义不大!
想到这里你应该就能明白jwt的使用场景了
- 轻量化、不占用内存
- 无序、构造简单
- 还有一点 jwt 更多的是跨域的作用!
但是在实际的应用场景中还是session+redis更加适用!
因为虽然session相比jwt失去了简单性、但是session 的方式更加的灵活(jwt的生成无法修改)即使这样会占用部分内存。
但这点内存相比起灵活性,显得微不足道!
结尾
本篇完整就到此结束了~
我是荔枝程序员、一名大三的在校生~
作者继续找实习中求内推!
转载自:https://juejin.cn/post/7224097010633424952