HTTP
协议是无状态的,意味着需要验证每一次请求,从而辨别客户端的身份。程序都是通过在服务端存储的登录信息来辨别请求的。
基于 Cookie/Session 的认证方式
Cookie是一种记录客户状态的机制,
Cookie
实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用
response
向客户端浏览器颁发一个
Cookie
。客户端浏览器会把
Cookie
保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该
Cookie
一同提交给服务器。服务器检查该
Cookie
,以此来辨认用户状态。服务器还可以根据需要修改
Cookie
的内容。
Cookie
功能需要浏览器的支持。如果浏览器不支持
Cookie
(如大部分手机中的浏览器)或者把
Cookie
禁用了,
Cookie
功能就会失效。
不同的浏览器采用不同的方式保存
Cookie
。
IE
浏览器会以文本文件形式保存,一个文本文件保存一个
Cookie
。
Cookie
具有不可跨域名性。根据
Cookie
规范,只能携带当前域名的cookie。
cookie
是浏览器实现的一种数据存储功能。
cookie
可以自行设置保存时间。若没有设置,关闭浏览器,
cookie
就自动消失。
Session
是另一种记录客户状态的机制,不同的是
Cookie
保存在客户端浏览器中,而
Session
保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是
Session
。客户端浏览器再次访问时只需要从该
Session
中查找该客户的状态就可以了。
如果说
Cookie
机制是通过检查客户身上的“通行证”来确定客户身份的话,那么
Session
机制就是通过检查服务器上的“客户明细表”来确认客户身份。
Cookie与Session的区别和联系
-
数据存放在客户的浏览器上,cookie
数据放在服务器上;session
-
不是很安全,别人可以分析存放在本地的cookie
并进行COOKIE
欺骗,考虑到安全应当使用COOKIE
;session
-
会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能。考虑到减轻服务器性能方面,应当使用session
;COOKIE
- 单个cookie在客户端的限制是3K,就是说一个站点在客户端存放的COOKIE不能超过3K;
Cookie
和
Session
的方案虽然分别属于客户端和服务端,但是服务端的
session
的实现对客户端的
cookie
有依赖关系的,服务端执行
session
机制时候会生成
session
的id值,这个
id
值会发送给客户端,客户端每次请求都会把这个
id
值放到
http
请求的头部发送给服务端,而这个
id
值在客户端会保存下来,保存的容器就是
cookie
,因此当我们完全禁掉浏览器的
cookie
的时候,服务端的
session
也会不能正常使用。
服务器使用
session
把用户的信息临时保存在了服务器上,用户离开网站后
session
会被销毁。这种用户信息存储方式相对
cookie
来说更安全。
这种方式一般存在一些问题:
1.
Seesion
:每次认证用户发起请求时,服务器需要去创建一个记录来存储信息。当越来越多的用户发请求时,内存的开销也会不断增加。
2. 可扩展性:在服务端的内存中使用
Seesion
存储登录信息,伴随而来的是可扩展性问题。如果
web
服务器做了负载均衡,那么下一个操作请求到了另一台服务器的时候
session
会丢失。
3.
CORS
(跨域资源共享):当我们需要让数据跨多台移动设备上使用时,跨域资源的共享会是一个让人头疼的问题。在使用
Ajax
抓取另一个域的资源,就可以会出现禁止请求的情况。
4.
CSRF
(跨站请求伪造):用户在访问银行网站时,他们很容易受到跨站请求伪造的攻击,并且能够被利用其访问其他的网站。
基于 token 的认证方式
基于 Token 的身份验证的特点是:
1. 无状态、可扩展(跨程序,跨端调用);在客户端存储的
Tokens
是无状态的,并且能够被扩展。基于这种无状态和不存储
Session
信息,负载均衡器能够将用户信息从一个服务传到其他服务器上。而不用去担心用户是否登录。
tokens
自己保存了用户的验证信息。
4. 安全;请求中发送
token
而不再是发送
cookie
能够防止
CSRF
(跨站请求伪造)。即使在客户端使用
cookie
存储
token
,
cookie
也仅仅是一个存储机制而不是用于认证。不将信息存储在
Session
中,让我们少了对
session
操作。
token
是有时效的,一段时间之后用户需要重新验证。
使用基于
Token
的身份验证方法,在服务端不需要存储用户的登录记录。基于Token的身份验证的过程:
1. 用户通过用户名和密码发送请求。
2. 服务器端程序验证。
3. 服务器端程序根据用户
id
、用户名、定义好的秘钥、过期时间返回一个带签名的
token
给客户端。
4. 客户端储存
token,
比如放在
Cookie
里或者
Local Storage
里,并且每次访问
API
判断
localStroage
有无
token,
没有则跳转到登录页,有则在请求头里携带
Token
到服务器端,请求获取用户信息,改变登录状态。
5. 服务端验证
token,
校验成功则返回请求数据;没有或者
token
过期校验失败则返回错误码
401,
重定向到登录页面。
export interface User {
token: string;
userInfo: UserInfo | any;
companyInfo: CompanyInfo | any;
resources?: string[];
}
save(key: string, value: any, storageType ?: StorageType) {
return this.storageService.put(
{
pool: key,
key: 'chris-app',
storageType: StorageType.localStorage
},
value
);
}
this.storageService.save(CACHE_USER_KEY, user);
HttpInterceptor => headers = headers.set('token', this.authService.getToken());
HttpInterceptor =>
401: '用户登陆状态失效,请重新登陆。'
有效期如何设置?
无论是从安全的角度考虑,还是从吊销的角度考虑,
Token
都需要设有效期,处于安全考虑,尽可能短。
Token
过期失效,就需要用户重新登录,体验就不太好,才有了
Refresh Token的方案。
服务端不需要刷新
Token
的过期时间,一旦
Token
过期,就反馈给前端,前端使用
Refresh Token
申请一个全新
Token
继续使用。这种方案中,服务端只需要在客户端请求更新
Token
的时候对
Refresh Token
的有效性进行一次检查,大大减少了更新有效期的操作,也就避免了频繁读写。当然
Refresh Token
也是有效期的,但是这个有效期就可以长点,比如,以天为单位的时间。
使用
Token
和
Refresh Token
的时序图如下:
1)登录
2)业务请求
3)
Token
过期,通过 refresh Token 申请刷新
Token
上面的时序图中并未提到
Refresh Token
过期怎么办。
Refresh Token
既然已经过期,无可厚非该要求用户重新登录了。