超文本传输协议(HyperText Transfer Protocol,HTTP)一种用于分布式、协作式和超媒体信息系统的应用层协议,是基于 TCP/IP 协议的应用层协议。通常运行于TCP上,指定了客户端发送什么样的请求信息,得到来自服务端返回回来的相对应的响应。
我们知道HTTP是一种无状态的协议,无状态的意思就是说是没有记忆的,前后的HTTP请求是相互独立互不干扰的,也就是说客户端每向服务端发送请求时,后发送的请求无法得知前发送请求所包含的各种状态数据。如果还想要得到前发送请求响应的数据,需要重新发送该请求,即http的响应数据无法持久化。但是我们一般的需求是有需要保留之前请求的数据的,就如同最为常见的用户登录功能,当你用户登录后要进行其他业务的处理需要用到用户信息时,其就要重新在通过用户密码获取用户信息,这显然是不合理的,需要考虑将登录时的响应数据进行持久化。
cookie将信息以key-value的形式存储至客户端浏览器,当我们再次向服务端发起请求时,其请求会携带所有的cookie信息至服务端,以解决http无状态的问题。
浏览器版本 | Cookie的数量限制 | Cookie的总大小限制 |
---|---|---|
IE6 | 20个/每个域名 | 4095个字节 |
IE7 | 50个/每个域名 | 4095个字节 |
IE9 | 50个/每个域名 | 4095个字节 |
Chrome | 50个/每个域名 | 大于80000 |
FireFox | 50个/每个域名 | 4097个字节 |
cookie的存储时间是大部分是有限制临时的,其规定cookie信息保存在浏览器上一段指定的时间,等时间到期之后其cookie信息将自动被清楚。
属性 | 说明 |
---|---|
name | Cookie 的名称及相对应的值,必须是字符串类型,不区分大小写,一旦创建,名称便不可更改 |
value | Cookie的值,如果值为Unicode字符,需要为字符编码。如果为二进制数据,则需要使用BASE64编码 |
domain | 指定 cookie 使用的所属域名,默认是当前域名,例如设置为“.csdn.net”,则所有以“csdn.net”结尾的域名都可以访问该Cookie。 |
path | 指定 cookie 在哪个路由下生效,默认是 ‘/’。如设置“www.csdn.net/blog”则只有 ./blog 下的路由可以访问到该 cookie |
expires | 过期时间(GMT时间格式),在设置的某个时间点后该 cookie 就会失效。以客户端的时间为准,cookie一般默认存储的,当关闭浏览器时及删除cookie |
max-age | cookie 失效的时间,单位秒。该参数优先级高于优先级高于 expires ,如果值为正数,则该 cookie 在 maxAge 秒后失效。如果为负数则关闭浏览器即失效删除 。如果为 0,表示删除该 cookie 。默认为 -1。 |
HttpOnly | 设置该属性将使js脚本无法读写该 cookie 的信息,但还是能通过 Application 中手动修改 cookie,一定程度上可以防止 CSRF 攻击 |
secure | 该 cookie 是否仅被使用安全协议传输。安全协议有 HTTPS,SSL等,在网络上传输数据之前先将数据加密。默认为false。当 secure 值为 true 时,cookie 在 HTTP 中是无效的。 |
comment | 该Cookie的用处说明,浏览器显示Cookie信息的时候显示该说明。 |
version | Cookie使用的版本号。0表示遵循Netscape的Cookie规范,1表示遵循W3C的RFC 2109规范 |
cookie在JS中设置、读取、删除的常用封装方法:
<script type="text/javascript">
document.cookie='name=value;domain=.domain.com;path=/;expires=time;HttpOnly;secure'
/**
* 设置cookie
*/
function setCookie(key,value,days){
var exp = new Date();
exp.setTime(exp.getTime() + days*24*60*60*1000);
document.cookie=key+"="+value+"; expires="+exp.toGMTString();
}
/**
* 获取cookie
*/
function getCookie(key){
var arr;
var reg = new RegExp("(^| )" + key+ "=([^;]*)(;|$)");
if (arr = document.cookie.match(reg)) return decodeURI(arr[2]);
else return null;
}
/**
* removeCookie 移除cookie
*/
function removeCookie(key){
setCookie(key,"",-1); // 把cookie设置为过期
};
</script>
我们知道cookie是将信息存储在本地浏览器,对于本地浏览器存储的信息我们很容易对其获取破译这样来说信息数据相对不那么安全。而session则是将信息存储在服务器,这样对于数据信息来说就变得更加的安全,不易被窃取。
Session 代表着服务器和客户端一次会话的过程。在整个会话过程中,不管页面的进行跳转,其存储的数据信息都不会丢失,知道会话关闭或者 Session 超时失效会话结束。
session属于一种服务器端的机制,服务器使用一种类似于散列表的结构保存信息。
浏览器第一次发送请求时,服务器会自动生成了Session,并且生成了唯一标识Session ID来标识这个Session并返回给浏览器。当我们从浏览器发送要存储数据至Session中的请求给服务器时,我们的请求里会携带存放有Session ID的Cookie一并发送到服务器上,服务器就会根据我们的sessionid在服务器端开辟一个内存空间来存放数据,同时把内存空间的地址通过cookie的形式返回给浏览器。当我们下一个请求需要调用数据时,服务器就会根据我们cookie存储的内存空间地址来到内存中,找到对应session拿出数据。
一般Session ID会有时间限制,超时后毁掉这个值,默认30分钟,当我们关闭浏览器的时候,服务器端的session也会被摧毁。
通过上述我们知道session id存储在cookie中,而真正的数据是存储在服务器中,那么session的时效性是如何
在选用cookie或session时应当考虑本身的需求进行合理化选择。一般将重要信息存放到session中,其他需要保留的信息放在cookie中,大多数使用Session + Cookie来进行操作。
Token是服务器端生成的用于验证用户登录状态的加密数据,Token验证服务器端不需要存储用户会话所需的配置等数据,只需要后端将Token进行验证签名,然后再发给浏览器。每次用户请求时带上这一段token信息,服务端拿到此信息进行解密后就能判断此用户。
我们知道HTTP协议是无状态的,无状态以为着需要验证每次请求来辨别客户端的身份。一般我们通过在服务端存储的登录信息来辨别请求,而基于服务端验证的方式存在一些问题:
使用session进行验证时,当用户量越来越多,其占用服务器的内存资源将会不断的加大。
可扩展性:在服务端的内存中使用Seesion存储登录信息,伴随而来的是可扩展性问题。
CORS(跨域资源共享):当我们需要让数据跨多台移动设备上使用时,会出现跨域问题。
CSRF(跨站请求伪造):cookie和session都会受到csrf的攻击,一个典型的例子:假如用户已经登陆了银行网页,但此时点进了一个钓鱼网站新开了一个tab页,而该网站直接向银行网页发起了一个转账请求,如果银行网页未对csrf攻击进行防护,那么该请求就会携带我们银行的cookie信息骗过银行的服务器验证并最终完成转账,形成 CSRF 攻击。
无状态
不将用户信息存在服务器或session中,只需携带token信息去服务端进行签名验证即可,适用于分布式微服务
安全性
请求中发送token而不再是发送cookie能够防止CSRF(跨站请求伪造)。即使在客户端使用cookie存储token,cookie也仅仅是一个存储机制而不是用于认证。同时token是有时效的,一段时间之后用户需要重新验证。
可扩展性
Tokens能够创建与其它程序共享权限的程序。使用tokens时,可以提供可选的权限给第三方应用程序。当用户想让另一个应用程序访问它们的数据,我们可以通过建立自己的API,得出特殊权限的tokens。例如我们可以通过其他社交账户来登录CSDN
多平台跨域
CORS(跨域资源共享),对应用程序和服务进行扩展的时候,需要介入各种各种的设备和应用程序。只要用户有一个通过了验证的token,数据和资源就能够在任何域上被请求到。
客户端使用用户名跟密码向服务端发起请求登录,服务端进行用户校验
若用户校验成功,服务端会签发一个由服务端持有秘钥通过加密算法对数据进行加密的token,并将该token信息传送给客户端
客户端收到 token 信息以后,会把它存储起来,比如放在 cookie(只用于存储) 里或者 localStorage 里
客户端在之后每次向服务端请求资源的时,都会携带服务端签发的 token,携带方式将 token 放到 HTTP 的 Header中或其它方式(请求头的 Authorization 字段中使用Bearer 模式添加 token )
服务端收到请求后,会去验证客户端请求携带着的 token,如果验证成功,就向客户端返回请求的数据,失败在返回错误信息。一般采用验证中间件进行校验
JSON Web Token(简称 JWT)是token的一种实现方式,并且基本是java web领域的事实标准,是目前最流行的跨域认证解决方案。
JWT 的原理是,服务器认证以后,生成一个 JSON 对象,为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名,发回给用户。
JWT是一个很长的没有分行的字符串,中间以点(.)分隔成三个部分。
JWT 的三个部分由上到下依次为:
{ "alg": "HS256", "typ": "JWT" }
alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);
typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT。
最后,将上面的 JSON 对象使用 Base64URL 算法(Base64算法替换在 URL 里面有特殊含义的值)转成字符串。
- iss (issuer):签发人
- exp (expiration time):过期时间
- sub (subject):主题
- aud (audience):受众
- nbf (Not Before):生效时间
- iat (Issued At):签发时间
- jti (JWT ID):编号
首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
JWT的实现根据语言的不同有不同的实现方式,以下提供一些实现案例:
Token:服务端验证客户端发送过来的 Token 时,还需要查询数据库获取用户信息,然后验证 Token 是否有效。
JWT: 将 Token 和 Payload 加密后存储于客户端,服务端只需要使用密钥解密进行校验(校验也是 JWT 自己实现的)即可,不需要查询或者减少查询数据库,因为 JWT 自包含了用户信息和加密的数据。
本文为编程小白自学问题归纳,如有错误与不足敬请指正!