cookie, session

因为这四者都有一些关联。session 的 实现 依赖于 cookie,前后端分离引起跨域问题,导致 session 失效。

1.会话跟踪技术
试想,我正在开发一个 java应用,设置权限,对有些页面只有登录的人才允许访问,我们又知道 http 请求是无状态的,不会保留客户端信息,那我们如何实现这个功能呢,既然 请求是无状态的,也就是不能保留用户的登录信息,岂不是每次访问有权限的页面都要进行一次登录?显示这是不合适 也 不合理的,所以我们需要有一种技术,可以用来保存 用户的信息,于是就出现了会话跟踪技术。

简单点说:会话跟踪技术,就是让 “http 有状态”,能记录用户相关信息等。

1,什么是会话

会话是一个终端用户(服务器)与交互系统(客户端)的通讯过程

2,什么是会话跟踪

对同一用户对服务器的连续的请求和接受响应的监视。(将用户与同一用户发出的不同的请求之间关联,为了数据共享)

3,为什么需要会话跟踪技术

服务器与客户端之间是用HTTP协议进行通信的。而HTTP协议是无状态的协议,无法储存客户的信息,即一次响应完成之后连接就断开了,下一次的请求需要重新连接。这就需要判断是否是同一用户

2.如何实现会话跟踪呢?
假如要你来实现 会话跟踪,你会怎么去实现呢?

我的大致思路是这样的:既然我们需要保存用户信息,也就是要辨别这次请求时哪个用户发过来的,然后再把 ta 对应的内容给 ta。这像不像 一个 map 结构。key 是辨别每个用户(同一个ip请求)的唯一标识,value是一个集合,用来保存信息的信息。当用户请求过来时,我们可以获取这个 唯一标识,根据 key 获取 value,取出里面的 一个 isLogin 值,判断是否为 true,就可以辨别 用户是否登录了。

其实这就是 cookie 和 session,别人早已经帮我们实现了。

3.cookie
1,什么是 cookie?

HTTP协议本身是无状态的。什么是无状态呢,即服务器无法判断用户身份。Cookie实际上是一小段的文本信息(key-value格式)。客户端向服务器发起请求,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。

打个比方,我们去银行办理储蓄业务,第一次给你办了张银行卡,里面存放了身份证、密码、手机等个人信息。当你下次再来这个银行时,银行机器能识别你的卡,从而能够直接办理业务。

2,cookie机制
当用户第一次访问并登陆一个网站的时候,cookie的设置以及发送会经历以下4个步骤:

客户端发送一个请求到服务器 --> 服务器发送一个HttpResponse响应到客户端,其中包含Set-Cookie的头部 --> 客户端保存cookie,之后向服务器发送请求时,HttpRequest请求中会包含一个Cookie的头部 -->服务器返回响应数据

3,cookie属性项

属性项 属性项介绍
NAME=VALUE 键值对,可以设置要保存的 Key/Value,注意这里的 NAME 不能和其他属性项的名字一样
Expires 过期时间,在设置的某个时间点后该 Cookie 就会失效
Domain 生成该 Cookie 的域名,如 domain=“www.baidu.com”
Path 该 Cookie 是在当前的哪个路径下生成的,如 path=/wp-admin/
Secure 如果设置了这个属性,那么只会在 SSH 连接时才会回传该 Cookie
Expires
该属性用来设置Cookie的有效期。Cookie中的maxAge用来表示该属性,单位为秒。Cookie中通过getMaxAge()和setMaxAge(int maxAge)来读写该属性。maxAge有3种值,分别为正数,负数和0。

如果maxAge属性为正数,则表示该Cookie会在maxAge秒之后自动失效。浏览器会将maxAge为正数的Cookie持久化,即写到对应的Cookie文件中(每个浏览器存储的位置不一致)。无论客户关闭了浏览器还是电脑,只要还在maxAge秒之前,登录网站时该Cookie仍然有效。下面代码中的Cookie信息将永远有效。

当maxAge属性为负数,则表示该Cookie只是一个临时Cookie,不会被持久化,仅在本浏览器窗口或者本窗口打开的子窗口中有效,关闭浏览器后该Cookie立即失效。

Cookie的域名
Cookie是不可以跨域名的,隐私安全机制禁止网站非法获取其他网站的Cookie。

正常情况下,同一个一级域名下的两个二级域名也不能交互使用Cookie,比如test1.mcrwayfun.com和test2.mcrwayfun.com,因为二者的域名不完全相同。如果想要mcrwayfun.com名下的二级域名都可以使用该Cookie,需要设置Cookie的domain参数为.mcrwayfun.com,这样使用test1.mcrwayfun.com和test2.mcrwayfun.com就能访问同一个cookie

Cookie的路径
path 属性决定允许访问Cookie的路径。比如,设置为/表示允许所有路径都可以使用Cookie

4.session
一般译作会话,从不同层面看待session,它有着类似但不全然相同的含义。如:从web应用的用户看来,他打开浏览器访问一个电子商务网站,登录、并完成购物直到关闭浏览器,这是一个会话。而在web应用开发者看来,用户登录时我需要创建数据结构以存储用户的登录信息,这个结构也叫作session。因此在谈论session的时候要注意上下文环境。

session一般是在web应用的背景之下,我们知道web应用是基于HTTP协议的,而HTTP协议恰恰是一种无状态协议。也就是说,用户从A页面跳转到B页面会重新发送一次HTTP请求,而服务端在返回响应的时候是无法获知该用户在请求B页面之前做了什么的。

session是有状态的,而HTTP协议是无状态的。

解决HTTP协议自身无状态的方式有cookie和session。二者都能记录状态,前者是将状态数据保存在客户端,后者则是保存在服务端。

它的基本原理是服务端为每一个session维护一份会话信息数据,而客户端和服务端依靠一个全局唯一的表示来访问会话信息数据。用户访问web应用时,服务端程序决定何时创建session,创建session可以概括为三个步骤:

生成全局唯一的标识符(sessionid)
开辟数据存储空间。一般会在内存中创建响应的数据结构,但这种情况下,系统一旦停电,所有的会话数据就会丢失,如果是电子商务网站,这种事故会造成严重的后果。不过也可以写到文件里甚至存储在数据库中,这项虽然会增加I/O开销,但是session可以实现某种程度的持久化,而且更有利于session共享
将session的全局唯一标识符发送个客户端
问题的关键就在服务端如何发送这个session的唯一标识符上。联系到HTTP协议,数据无非可以放到请求行、头域或Body里,基于此,一般来说会有两种常用的方式:利用cookie
和URL重写。
利用 cookie 方式:
服务端只要设置set-cookie头就可以将session的标识符传送到客户端,而客户端此后的每一次请求都会带上这个标识符,服务器通过这个唯一标识,就可以分辨出客户端是谁了。由于cookie可以设置失效时间,所以一般包含session信息的cookie会设置失效时间为0,即浏览器进程有效时间。至于浏览器怎么处理这个0,每个款浏览器都有自己的方案,但差别都不会太大(一般体现在新建浏览器窗口的时候)。

URL重写:
多为URL重写,顾名思义就是重写URL。试想,在返回用户请求的页面之前,将页面所有的URL后面全部以get参数的方式加上session标识符(或者加在path info部分等等),这样用户在收到响应之后,无论点击哪个链接或提交表单,都会再带上这个session标识符,从而就实现了会话的保持。

4.1 session 原理
首先浏览器请求服务器访问web站点时,程序需要为客户端的请求创建一个session的时候,服务器首先会检查这个客户端请求是否已经包含了一个session标识、称为SESSIONID,如果已经包含了一个sessionid则说明以前已经为此客户端创建过session,服务器就按照sessionid把这个session检索出来使用,如果客户端请求不包含session id,则服务器为此客户端创建一个session并且生成一个与此session相关联的session id,sessionid 的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个sessionid将在本次响应中返回到客户端保存,保存这个sessionid的方式就可以是cookie,这样在交互的过程中,浏览器可以自动的按照规则把这个标识发回给服务器,服务器根据这个sessionid就可以找得到对应的session,又回到了这段文字的开始。

session的有效期:

由于会有越来越多的用户访问服务器,因此Session也会越来越多。为防止内存溢出,服务器会把长时间内没有活跃的Session从内存删除。这个时间就是Session的超时时间。如果超过了超时时间没访问过服务器,Session就自动失效了。

  1. 跨域
    5.1 为什么会出现跨域问题
    出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)

同源策略的目的:

保证用户的信息安全,防止恶意的网站盗取数据。如果缺少了同源策略,浏览器很容易受到xss、csrf的攻击。
设置同源策略的主要目的是为了安全,如果没有同源策略,在浏览器中的cookie等其他数据可以任意读取,不同域下的DOM任意操作,ajax任意请求其他网站的隐私数据。
设想这样一个场景:一个恶意网站的页面通过iframe嵌入了银行的登陆页面(两者不同源)如果没有同源策略的限制,恶意网站上的js脚本就可以在用户登录银行的时候获取用户名和密码

(1)做一个假网站,里面用iframe嵌套一个银行网站 http://mybank.com。
(2)把iframe宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别。
(3)这时如果用户输入账号密码,我们的主网站可以跨域访问到http://mybank.com的dom节点,就可以拿到用户的输入了,那么就完成了一次攻击。

5.2 什么是跨域
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

5.3 非同源限制
无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
无法接触非同源网页的 DOM
无法向非同源地址发送 AJAX 请求
5.4 跨域解决方法

  1. 设置document.domain解决无法读取非同源网页的 Cookie问题
    因为浏览器是通过document.domain属性来检查两个页面是否同源,因此只要通过设置相同的document.domain,两个页面就可以共享Cookie(此方案仅限主域相同,子域不同的跨域应用场景。)

// 两个页面都设置
document.domain = ‘test.com’;
1
2
2.跨文档通信 API:window.postMessage()
调用postMessage方法实现父窗口http://test1.com向子窗口http://test2.com发消息(子窗口同样可以通过该方法发送消息给父窗口)

它可用于解决以下方面的问题:

页面和其打开的新窗口的数据传递
多窗口之间消息传递
页面与嵌套的iframe消息传递
上面三个场景的跨域数据传递
// 父窗口打开一个子窗口
var openWindow = window.open(‘http://test2.com’, ‘title’);

// 父窗口向子窗口发消息(第一个参数代表发送的内容,第二个参数代表接收消息窗口的url)
openWindow.postMessage(‘Nice to meet you!’, ‘http://test2.com’);
1
2
3
4
5
调用message事件,监听对方发送的消息

// 监听 message 消息
window.addEventListener(‘message’, function (e) {
console.log(e.source); // e.source 发送消息的窗口
console.log(e.origin); // e.origin 消息发向的网址
console.log(e.data); // e.data 发送的消息
},false);
1
2
3
4
5
6
3.JSONP
JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好(兼容低版本IE),缺点是只支持get请求,不支持post请求。

核心思想:网页通过添加一个

原生实现:

// 向服务器test.com发出请求,该请求的查询字符串有一个callback参数,用来指定回调函数的名字

// 处理服务器返回回调函数的数据

1
2
3
4
5
6
7
8
9
10
Vue.js
this.$http.jsonp(‘http://www.domain2.com:8080/login’, {
params: {},
jsonp: ‘handleCallback’
}).then((res) => {
console.log(res);
})
1
2
3
4
5
6
PS:服务端也要做相应得设置,返回 handleCallback 函数 ,例如

return HttpResponse(“handleCallback(”+result+")");
1
5.4 CORS
CORS 是跨域资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。

普通跨域请求:只需服务器端设置Access-Control-Allow-Origin
带cookie跨域请求:前后端都需要进行设置
【前端设置】根据xhr.withCredentials字段判断是否带有cookie
原生ajax
var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容

// 前端设置是否带cookie
xhr.withCredentials = true;

xhr.open(‘post’, ‘http://www.domain2.com:8080/login’, true);
xhr.setRequestHeader(‘Content-Type’, ‘application/x-www-form-urlencoded’);
xhr.send(‘user=admin’);

xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
};

vue-resource
Vue.http.options.credentials = true
1
axios
axios.defaults.withCredentials = true
1
【服务端设置】
服务器端对于CORS的支持,主要是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。

java 原生
/*

  • 导入包:import javax.servlet.http.HttpServletResponse;
  • 接口参数中定义:HttpServletResponse response
    */

// 允许跨域访问的域名:若有端口需写全(协议+域名+端口),若没有端口末尾不用加’/’
response.setHeader(“Access-Control-Allow-Origin”, “http://www.domain1.com”);

// 允许前端带认证cookie:启用此项后,上面的域名不能为’*’,必须指定具体的域名,否则浏览器会提示
response.setHeader(“Access-Control-Allow-Credentials”, “true”);

// 提示OPTIONS预检时,后端需要设置的两个常用自定义头
response.setHeader(“Access-Control-Allow-Headers”, “Content-Type,X-Requested-With”);

springboot
第一种方式:(精细配置)在需要跨域的整个Controller或者单个方法上添加@CrossOrigin注解
第二种方式:全局配置
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods(“POST”, “GET”, “PUT”, “OPTIONS”, “DELETE”)
.maxAge(3600)
.allowCredentials(true);
}
}

第三种方式:通过filter
@Component
@WebFilter(urlPatterns = "/", filterName = “authFilter”) //这里的“/” 表示的是需要拦截的请求路径
public class PassHttpFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException { }

@Override
 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    HttpServletResponse httpResponse = (HttpServletResponse)servletResponse;
    httpResponse.setHeader("Access-Control-Allow-Headers","Origin, X-Requested-With, Content-Type, Accept");
    httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
    httpResponse.addHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8080");
     filterChain.doFilter(servletRequest, httpResponse);
 }
 
@Override
public void destroy() { }

}

  1. 前后端分离
    6.1 什么是前后端分离
    一般传统上的开发协作模式有两种:

一种是前端先写一个静态页面,写好后,让后端去套模板。静态页面可以本地开发,也无需考虑业务逻辑只需要实现View即可。不足是还需要后端套模板,这些前端代码后端需要浏览一遍,以免出错。
另一种协作模式是,前端直接去写模板,这样做的问题在于,前端编写过程中很依赖与后端环境,如果当后端没写完的情况下,前端几乎没法干活。
前后端分离:

首先要知道所有的程序都是一数据为基础的,没有数据的程序没有实际意义,程序的本质就是对程序的增删改查。
前后端分离就是把数据操作和显示分离出来。前端专注做数据显示,通过文字,图片或者图标等方式让数据形象直观的显示出来。后端专注做数据的操作。前端把数据发给后端,有后端对数据进行修改。
后端一般用java,c#等语言。后端来进行数据库的链接,并对数据进行操作。
后端提供接口给前端调用,来触发后端对数据的操作。
6.2 为什么前后端分离会引起的跨域问题
由于我们使用前后端分离的开发方式,那必然会导致 前端的页面代码 和 后端的代码 需要分别部署到不同的服务器上,即使是部署到同一个服务器,也会导致端口不一致,由此便产生了跨域问题,当然这并不是很难解决的事情,前面我们已经知道该如何解决跨域问题了。

6.3 前后端分离引起 session 无效
我们知道 由于前后端分离,出现了跨域问题,而由于跨域问题,又导致 session 失效。

分析一下为什么 session 会失效?

我们知道 cookie 默认是不支持跨域传递的(同一个一级域名下的两个二级域名),所以我们每一次发送请求给后端,并不能携带我们的 cookie,也就是不能携带 sessionid,没有sessionid,服务端就认为你是第一次访问,这就导致 session 失效。

当然前面也说过了,通过一些代码可以让 cookie 支持跨域。

记录的比较仓促,可能有些东西没想到,或者有错误,欢迎纠正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值