前后端分离之session问题

夙愿已清 提交于 2019-11-27 19:22:35

【session问题---跳坑+爬坑】

session基础点:
    1、session是会话控制。session对象储存特定用户会话所需的属性或者配置信息。当用户在该应用程序的web页面之间跳转时,session对象便会在一直存在。
    2、session对象在客户端向服务端发起请求时,由服务端创建。
    3、会话状态仅在支持cookie的浏览器中保留。
    【重点】
        (1)在同一个浏览器下,session是唯一的。
        (2)session由服务端创建,不同的服务端(ip、域名、端口任意不同)之间的session不相同,它是不共享session的。
        (3)服务端在创建session对象的同时,会生成一个session的唯一标识,这就是JSessionID。所以不同的服务端之间的JSessionID不同。
        (4)JSessionID作为session的唯一标识,以cookie的形式返回给客户端,而在服务端以内存的形式保存。
    【问题】
        (1)session覆盖:当在浏览器上同时访问两个或者多个服务端时,由于不同的服务端之间不共享session,并且浏览器只有一个session,所以会出现session覆盖的问题。
            表现:轮流访问服务端A、B时,JsessoinID每次都是不一样的。
                第一次进入浏览器访问serverA,A服务端中并没有session存在,则由A生成JSessionID_A,并将其以cookie的形式保存在
                当切换到ServerB时,B服务端也是没有Session存在,就会由B生成JSessionID_B,以cookie的形式返回给客户端,这个时候JSessionID_B就会将JSessionID_A覆盖。
                由此引发每次都是新的JSessionID,也就是每次都会生成新的session对象,旧对象的内容便被覆盖。
        
        (2)session不共享:正如上述,客户端访问不同的服务端时,session是不同的。也就是session在服务端之间是不能共享的,各自拥有自己独特的session。
            表现:在分布式系统中,将业务拆分时,不同的业务单独作为一个服务,业务之间相互连接变成了一个系统。用户在该系统的不同业务之间跳转时,访问的便事不同的服务端。
                由于session存放着用户的身份信息和权限信息,所以如果session不能共享,整个系统的功能便失去了意义。例如:系统可能会重复的提醒用户登录。
                
    【跳坑】
        作为一名后端小白,时常也是要接触前端一些逻辑性的东西。我经常在前后端交互中穿梭!
        这次为了测试session,我找了前端队友的代码,做了些修改,形成前后端分离的小demo
        测试背景:前后端分离 与 axios+restful API交互
        
        坑1:使用前端使用axios,默认是不携带cookie的。
        坑2:由于是前后端分离,那就必须要跨域的,但是cookie默认是不跨域的,需要在后端设置。
        
        解决:以上两个问题都好解决,博客一大堆,全是解决以上问题的!
        步骤:
            1、前端引入axios时,在main.js中做一个全局设置,设置允许携带cookie发起请求。
              

import axios from 'axios'    //导入axios                
axios.defaults.withCredentials = true    //全局设置axios允许携带cookie进行访问                
Vue.prototype.$axios = axios    //设置axios全局标量,之后就可以以this.$axios的形式访问了


                
                或者将axios允许携带cookie发起请求作为一个局部设置,只需要在需要携带cookie的请求中加上
                    

xhrFields: {                        
    withCredentials: true                    
}


                例如:
                  

this.$axios({                        
    xhrFields: {                            
        withCredentials: true                        
    },                        
    method: 'get',                        
    url: 'http://localhost:8080/...',                    
}).then(resposnse=>{                        
    console.log("验证")                    
})


            2、后端协助:在配置跨域的同时配置允许cookie跨域访问
                (1)springboot注解法:在启动类上添加如下注解:
                

@CrossOrigin(origins = "http://localhost:8082", maxAge = 3600,allowCredentials = "true")


                    注意:
                        allowCredentials="true"表示允许cookie跨域访问。
                        orgins属性的值为前端 ip+端口 ,表示允许该源的请求访问本服务端,由于配置了允许cookie跨域访问,所以这里千万不能将orgin设置为"*"(允许任意源访问)
                
                (2)自定义过滤器法:(原理如上)
                
                

@Slf4j
@Configuration
@WebFilter(urlPatterns = "/*", filterName = "crossDomainFilter")
@Order(1)
public class CrossDomainFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        log.info("【跨域过滤器】 CrossDomainFilter");
		/*
         *  Access-Control-Allow-Origin 响应头指定了该响应的资源是否被允许与给定的origin共享。
		 *  对于不需具备凭证(credentials)的请求,服务器会以“*”作为通配符,从而允许所有域都具有访问资源的权限。
		 *  可以指定一个可以访问资源的URI。
		 *
		 *  CORS和缓存
		 *  如果服务器未使用“*”,而是指定了一个域,那么为了向客户端表明服务器的返回会根据Origin请求头而有所不同,
		 *  必须在Vary响应头中包含Origin。
		 *  	例如:
		 *  	Access-Control-Allow-Origin: https://developer.mozilla.org
		 *  	Vary: Origin
		 */
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));

		/*
		 *  请求首部  Access-Control-Request-Method 出现于 preflight request (预检请求)中,
		 *  用于通知服务器在真正的请求中会采用哪种  HTTP 方法。
		 *  因为预检请求所使用的方法总是 OPTIONS ,与实际请求所使用的方法不一样,所以这个首部是必要的。
		 */
        response.setHeader("Access-Control-Allow-Method", "*");

		/*
		 * The Access-Control-Max-Age 这个响应首部表示 preflight request(预检请求)的返回结果
		 * (即 Access-Control-Allow-Methods 和Access-Control-Allow-Headers 提供的信息)
		 *  可以被缓存多久
		 */
        response.setHeader("Access-Control-Max-Age", "3600");


		/*
		 *  响应首部 Access-Control-Allow-Headers 用于 preflight request (预检请求)中
		 *  列出了将会在正式请求的 Access-Control-Request-Headers 字段中出现的首部信息
		 *
		 *  注意以下这些特定的首部是一直允许的:
		 *  	Accept, Accept-Language, Content-Language,Content-Type
		 *  	其中 Content-Type (只在其值属于 MIME 类型 application/x-www-form-urlencoded, multipart/form-data 或
		 *  	text/plain中的一种)。
		 *  	这些被称作simple headers,你无需特意声明它们。
		 */
       // response.setHeader("Access-Control-Allow-Headers", "Content-Type");
        response.setHeader("Access-Control-Allow-Headers", "Authorization,Origin, X-Requested-With, Content-Type, Accept,Access-Token");
        //Origin, X-Requested-With, Content-Type, Accept,Access-Token

		/*
		 * 响应首部 Access-Control-Expose-Headers 列出了哪些首部可以作为响应的一部分暴露给外部
		 *
		 * 默认情况下,只有六种 simple response headers (简单响应首部)可以暴露给外部:
		 * Cache-Control
		 * Content-Language
		 * Content-Type
		 * Expires
		 * Last-Modified
		 * Pragma
		 * 如果想要让客户端可以访问到其他的首部信息,可以将它们在 Access-Control-Expose-Headers 里面列出来
		 */
        response.setHeader("Access-Control-Expose-Headers", "checkTokenResult");
        //设置可以暴露在外的响应头
        response.setHeader("Access-Control-Expose-Headers", "message");


        /*
        * 允许cookie跨域
        * */
        // 指示的请求的响应是否可以暴露于该页面。当返回true时他可以被暴露Credentials
        response.setHeader("Access-Control-Allow-Credentials", "true");

		/*Credentials
		 * no-cache:
		 * 	在发布缓存副本之前,强制要求缓存把请求提交给原始服务器进行验证。
		 */
        response.setHeader("Cache-Control", "no-cache");

        // 把请求传回过滤链
        filterChain.doFilter(servletRequest, servletResponse);
    }
}


                
            好了,以上问题大功告成了,你以为就真的结束了?
        
        坑3(我的大boss):以上内容是绝大多数网友解决的问题的办法,但是对我来说就不是了,身为一个后端小白,解决session问题的周期就耗在了这第三步!
            我只能说,你要是向上面这么一步一步做下来,就已经可以实现前后端session访问了,那我恭喜你。
            如果连续访问同一个服务端,sessionId还是不一样,那还是恭喜你,你估计找对人了!
            
            我想问一个问题:你使用了mock了没有?
            
            如果有,好的你离成功最后一步了!
            
            作妖的东西:mock.js
                【mock将每次的请求的cookie都重新刷新了,导致后台每次获取的SessionID都不一样】
            
            当看到这句话的时候,我百感交加,我都快哭了,花了我整整一两天的时间!
            于是,我立刻将require(mock.js)以及页面中import mock的地方都注释了,注意看你有没有再main.js中引入了mock!好的,测试,发送请求,我成功了!


太罗嗦了,留下了没有技术的泪水!如何解决session共享呢,下一篇会介绍!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!