计算机系统应用教程网站

网站首页 > 技术文章 正文

泰涨知识 | 请求跨域 Session不共享?你遇到过这种情形吗?

btikc 2024-10-17 08:35:49 技术文章 18 ℃ 0 评论

在JavaEE开发中,我们经常使用SpringMVC或SpringBoot框架进行后端开发,在传统的J2EE项目中,我们经常采用MVC的设计模式完成项目框架的搭建,那我们从MVC模式进入今天的问题。


01 / MVC模式是如何的?


MVC设计模式是经典的程序项目设计模式,M代表的是业务模型,V代表的是用户视图,C代表的是控制器,其使用MVC的设计模式目的是为了分离项目中会耦合的代码部分,让我们的后端代码各司其职,程序员只需要了解模块间的协调关系,注重业务逻辑的代码实现就可以完成后端程序的开发了。

MVC模式流程图如下:



02 / 那Session在项目中是干啥呢?


Session是服务端会话存储技术,我们在后端开发中我们会将登录用户的信息保存到session中,如果用户长时间或规定时间内没有操作,session就会过期从而让系统回到登录页面。

Session主要存储的内容:

  • 登录用户的信息作用:主页展示登录人、CURD操作携带用户鉴权
  • 系统相关信息:在多租户项目中,可能需要复杂的数据权限鉴权,也会在登录的时候携带


03 / 什么是跨域?


这个原因来自于浏览器的同源策略安全限制, 同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。当我们使用ajax或者axios进行http访问时,非同源内容就会出现跨域请求问题。

错误图如下:



04 / 在开发中为何会跨域?


在传统的开发中,在MVC框架中,View层技术一般会使用jsp、html、freemarker、thymeleaf技术完成,但是他们的源码文件必须和项目框架文件在同一个目录下才能实现视图的解析,从而会出现耦合的效果。

从而MVVM框架出现,实现了前后端的完全分离,以vue.js为例的技术,实现前端是一个服务通过axios访问服务端,从而达到前后端物理层面的分离,但是在这种情况下就会出现不同源的情况。

vue与服务端交互:


当前端服务与后端服务不同源,但是在axios请求中会出现脚本的代理请求,就会出现非同源跨域问题。


05 / 跨域的简单判断


一个域名的组成由:协议、子域名、主域名、端口和资源地址组成;

当协议、子域名、主域名、端口号中任意一个不相同,都算作不同域,当他们相互出现请求就会“跨域”。


06 / 跨域的解决


在Vue项目中config配置文件,配置域名解析代理;

module.exports = {
  dev: {


    // Paths
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    proxyTable: {
    '/api': {
        target:'http://localhost:8020/GDones/',
        changeOrigin:true, 
        pathRewrite:{
          '^/api': ''
        }
      }
  },


配置完成后在请求代码处,配置完整路径就通过映射路径解析即可;

axios.put("/api"+url, params,{
        headers:{'Content-Type':'application/json;charset=UTF-8'}
      })


这样我们前端的请求就再也不会跨域了,因为我们通过代理完成。


07 / 跨域解决了,那Session怎么办?


session是保存在服务器端,理论上是没有是没有限制,只要内存够大。浏览器第一次访问服务器时会创建一个session对象并返回一个JSESSIONID=ID的值,创建一个Cookie对象key为JSSIONID,value为ID的值,将这个Cookie写回浏览器,浏览器第二次访问服务器时携带Cookie信息JSESSIONID=ID的值,如果该JSESSIONID的session已经销毁,那么会重新创建一个新的session再返回一个新的JSESSIONID通过Cookie返回到浏览器针对一个web项目,一个浏览器是共享一个session,就算有两个web项目部署在同一个服务器上,针对两个项目的session是不同的。


但是在跨域的请求中,Session无法判别来自客户端的cookie和SessionID,他们每次个不相同,所服务端数据存储就得换一个思路实现,使用
Redis缓存实现。


那我们就开始吧!

安装Redis后,在服务中配置Redis相关内容,此处以Springboot为例,集成Redis缓存。


spring:
  redis:
    host: 127.0.0.1   #redis的ip,本机
    port: 6379  #redis默认端口
    password:  #默认无密码


ok,完成之后我们设计一下资源验证问题:

  • 每个客户端都有一个唯一的id描述我是一个独立的客户端;
  • 客户端id可以作为存储Redis的key,value存储客户端的用户数据;
  • 需要取出验证必须要客户端提供唯一id;

那接下来就是代码环节,以登录案例为主:


(1)登录需要验证码,验证码与客户端用户界面也是一一对应的,没有session我们就必须模拟session比较的过程,在获取验证码阶段就把客户端id对应的验证码一一对应,否则验证无法通过;

@GetMapping("/captcha")
    @ApiOperation("获取验证码图片")
    public MyResult getRegCode(HttpSession session, HttpServletResponse response,String localID){
        log.info("--- 系统操作:获取验证码");


        MyResult result = new MyResult();
        //长,宽,位数
        GifCaptcha gifCaptcha = new GifCaptcha(100, 42, 5);
        //设置类型
        gifCaptcha.setCharType(Captcha.TYPE_NUM_AND_UPPER);
        //生成验证码
        String code = gifCaptcha.text();


        log.info("--- 验证码【"+code+"】");


        //在redis中存储一套,key开头要区分一下业务
        redisUtil.set("regCode#"+localID,code);


        result.setMsg("验证码获取成功!");
        result.setData(gifCaptcha.toBase64());


        return result;
    }


(2)在登录的时候验证码的比对就可以通过登录请求中的客户端id获取验证码一一比对;

//redis获取
        String redis_regCode = (String) redisUtil.get("regCode#"+loginDto.getLocalID());



08 / 总结流程


  • 首先获取验证码发起的请求中要携带客户端唯一id;
  • 通过客户端唯一id为存储Redis的key值,value为客户端对应的验证码;
  • 在登录接口中获取客户端的唯一id,获取对应的验证码进行比较;

这样,我们就用Redis模拟了会话的方式保存验证码了。




本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表