单机会话管理
http请求会有一个Session会话管理机制,用来标识用户会话的过程,默认使用的是springboot内嵌的httpSession的实现机制,这个tomcat协议实现是基于cookie传输sessionId的方式来完成容器的实现。
比如我们使用HttpServletRequest.getSession().getAttribute(telephone,otpCode);去存储用户获取验证码时手机和验证码对应信息,以使得用户在注册的方法可以从界面上拿到的otpCode和Session中otpCode的比较。
还有在用户登录的模块中,我们将登录凭证加入到session中:this.httpServletRequest.getSession().setAtteibute("IS_LOGIN",true);
this.httpServletRequest.getSession().setAtteibute("LOGIN_USER",userModel);
然后我们在用需要用到用户登录状态的业务方法中,会先判断当前用户对应的session中是否是login的,并且将LOGIN_USER获取到用户信息,完成对应用户的操作:
这个JSESSIONID就是 tomcat返回的内置cookie的标识,在tomcat内就是用来获取用户的session。因此客户端在每次发送到对应域名下的请求都会在RequestHeaderCookie里面带上JSESSIONID。
基于token传输用java代码实现是因为移动端APP客户可能会禁止掉cookie。
分布式会话管理
对于以上两种方案在分布式环境内都不可以生效,因为单体的会话管理都是基于tomcat的内存去实现的。每台服务器都是互相分开的,一旦nginx映射到另一台机器上,session信息就不会存在了。所以就需要分布式会话管理机制。
redis是一个集中式的缓存中间件,可以把它认为是Nosql的KV数据库。在分布式会话场景上面,我们无需开启持久化磁盘的功能,只需要它内存数据库存放缓存功能即可。
导入pom坐标:
第二个包的作用就是redis管理session对象。
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.session
spring-session-data-redis
2.0.5.RELEASE
配置application文件:
#配置springboot对redis的依赖
spring.redis.host=线上redis服务器ip地址
spring.redis.port=6379
spring.redis.database=10
#spring.redis.password=
#设置jedis连接池
#最大连接数量
spring.redis.jedis.pool.max-active=50
#最小idle连接
spring.redis.jedis.pool.min-idle=20
注意:
1.redis默认序列化方式是jdk方式,存入redis的类需要实现序列化,或者修改redis的序列化方式。
2.需要把redis部署在不和项目服务器冲突的服务器上,不然nginx没法映射
3.修改redis的配置文件:bind在redis线上服务器ip 然后指定一下:src/redis-server ./redis.conf
//用户登录接口
@RequestMapping(value = "/login", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
@ResponseBody
public CommonReturnType login(@RequestParam(name="telephone") String telephone,
@RequestParam(name = "password") String password) throws BusinessException, UnsupportedEncodingException, NoSuchAlgorithmException {
//入参校验
if(org.apache.commons.lang3.StringUtils.isEmpty(telephone) || StringUtils.isEmpty(password)){
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
}
//用户登录服务,就是检验用户登录是否合法
UserModel userModel = userService.validateLogin(telephone, EncodeByMds(password));
//将登录凭证加入到用户登录成功的session内
//修改成若用户登录验证成功后将对应的登录信息和登录凭证一起存入redis中
//生成登录凭证token,UUID
String uuidToken = UUID.randomUUID().toString();
uuidToken = uuidToken.replace("-", "");
//建立token和用户登录态之间的联系
redisTemplate.opsForValue().set(uuidToken,userModel); //只要有uuid,用户登录态就存在
redisTemplate.expire(uuidToken,1, TimeUnit.HOURS);
//下发token
return CommonReturnType.create(uuidToken);
}
//封装下单请求
@RequestMapping(value = "/createorder",method = {RequestMethod.POST},consumes = {CONTENT_TYPE_FORMED})
@ResponseBody
public CommonReturnType createOrder(@RequestParam(name="itemId")Integer itemId,@RequestParam(name = "promoId",required = false) Integer promoId,@RequestParam(name = "amount")Integer amount) throws BusinessException {
String token = httpServletRequest.getParameterMap().get("token")[0]; //也可以从参数中获取
if(StringUtils.isEmpty(token)){
throw new BusinessException(EmBusinessError.USER_NOT_LOGIN,"用户还未登录,不能下单");
}
//获取用户登录信息
UserModel userModel = (UserModel)redisTemplate.opsForValue().get(token);
if(userModel == null){ //以过期
throw new BusinessException(EmBusinessError.USER_NOT_LOGIN,"用户还未登录,不能下单");
}
OrderModel order = orderService.createOrder(userModel.getId(), itemId,promoId, amount);
return CommonReturnType.create(order);
}
分布式会话存储策略
分布式会话持久性管理:
不要redis降低处理到mysql中存储用户登录信息,一旦有高并发redis抗不住,关系型数据库一定也扛不住。所以还是要redis调优。
会话有效期时间:Tomcat默认为30min不与服务端发生交互的呆滞时间。
会话续命管理:触发操作延长声明周期,延长到30min,而不是加30min。
安全性问题:
1.url query string,get请求参数内(不安全)
2.自定义header内(不安全)
3.用安全传输的https:将http的明文字符流转用加密的方式换成数据流传输到服务端。但是发送请求时打开调试窗口仍可以看见请求格式,就可以模拟请求攻击。
4.自定义协议:使用app来进行输,app没有办法进行线上调试内在执行,只能通过wireshark等网络抓包工具去抓取http发出去的请求,这时候自定义协议就产生了效果,我们可以自己定义协议去传输报文,这样我们的请求就像https一样无法解析。
强登录态与弱登录态:
强登录态:需要登录操作才能做的行为
无需登录:只浏览公共页面
弱登录态:千人千面的智能推荐
弱登录态的续命能力:1.请求续命,2.keepalive续命
SSO单点登录:
同域名:都是www域名,则对应SSO是同一个存储即可。
根域名相同子域名不同:设置cookie时可以指定两个参数,第一个参数httpOnly=true,处理浏览器自身,javaScript是无法访问这个cookie的,第二个参数domain=/,只要拿到cookie,在顶级域名下都可以共享。
域名都不相同:两个不同的公司合作,太复杂,需要专业架构设计。