分布式会话和基于TOKEN的分布式会话

本文探讨了在Java应用中如何实现单机会话管理和基于Redis的分布式会话管理。介绍了基于cookie的sessionId管理和基于token的会话实现,并详细阐述了将这些机制迁移到Redis的过程。此外,还讨论了会话的有效期管理、安全性问题,包括强登录态和弱登录态的处理,以及SSO单点登录的实现。最后,提到了跨域会话共享和安全性策略,如httpOnly标志和自定义协议的应用。
摘要由CSDN通过智能技术生成

单机会话管理

1.基于cookie传输的sessionId:在java tomcat容器session实现

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。

2.基于token传输类似sessionId:java代码session实现

基于token传输用java代码实现是因为移动端APP客户可能会禁止掉cookie。

分布式会话管理

对于以上两种方案在分布式环境内都不可以生效,因为单体的会话管理都是基于tomcat的内存去实现的。每台服务器都是互相分开的,一旦nginx映射到另一台机器上,session信息就不会存在了。所以就需要分布式会话管理机制。

redis是一个集中式的缓存中间件,可以把它认为是Nosql的KV数据库。在分布式会话场景上面,我们无需开启持久化磁盘的功能,只需要它内存数据库存放缓存功能即可。

1.基于cookie传输sessionid:java tomcat容器session实现迁移到redis

导入pom坐标:

第二个包的作用就是redis管理session对象。

<!--    springboot对Redis的依赖-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
<!--    spring对session的管理存在redis里面-->
    <dependency>
      <groupId>org.springframework.session</groupId>
      <artifactId>spring-session-data-redis</artifactId>
      <version>2.0.5.RELEASE</version>
    </dependency>

 配置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

2.基于token传输类似sessionid:java代码session实现迁移到redis

    //用户登录接口
    @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,在顶级域名下都可以共享。

域名都不相同:两个不同的公司合作,太复杂,需要专业架构设计。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值