文章目录
1 登录和授权的区别
-
登录:身份认证,即确认[你是你]的过程。比如你输入用户名和密码请求,向服务器确认[你是你]
-
授权:由身份或持有的令牌确认享有某些权限(例如获取用户信息)【比如古代皇上有权利杀任何人,其他人要有权利杀人需要有尚方宝剑,皇上给我尚方宝剑是授权方,尚方宝剑就是一个令牌,我就是令牌的持有方】。
登录过程实质上的目的也是为了确认权限,直接把我的权限授予我自己,相比授权就少了令牌这个概念。
因此,在实际的应用中,多数场景下的[登录]和[授权]界限是模糊的。
目前,登录和授权有两种方式:Cookie
和 Autorization
,它们都是添加在header中的属性。
2 Cookie
2.1 Cookie的工作机制
- 服务器需要客户端保存的内容,放在
set-cookie
headers里返回,客户端会自动保存(客户端会将服务器返回给我的set-cookie
原封不动的保存,并且也会将访问的host
地址作为键也存到cookie中,下次访问同一个地址时就会携带存储在客户端的cookie一起发送请求)
-
客户端保存的cookies,会在之后的所有请求里都携带进
cookie
header里发回给服务器 -
客户端保存cookie是按照服务器域名来分类的,例如
shop.com
发会的cookie保存下来以后,在之后向games.com
的请求中并不会携带 -
客户端保存的cookie在超时后会被删除、没有设置超时时间的cookie(称作
session cookie
)在浏览器关闭后就会自动删除;另外,服务器也可以主动删除还未过期的客户端cookies
简单来说,客户端只处理对cookie的保存,在访问同一个服务器的时候将保存到本地的cookie给发过去就行了;cookie的计算和修改是有服务器处理。
cookie就是在客户端帮助服务端记录保存状态的工作机制。
cookie在移动端目前基本只处理登录记录,在往后的时间可能会慢慢的被抛弃。
2.2 Cookie的作用
- 会话管理:登录状态、购物车等
为什么需要 sessionid
?
假如你输入用户名密码登录了账户,服务器记录你已经登录了,然后又有其他人请求服务器想查询你的用户信息,因为请求是HTTP,HTTP是无状态的,此时没有 sessionid
服务器又查询到你当前已经登录了,然后就会处理请求结果返回信息。
没有 sessionid
是不安全的,sessionid
能记录哪个客户端登录了,客户端通过在cookie携带 sessionid
让服务器知道你是哪个客户端,让HTTP请求是有状态的。
客户端访问同一个服务器可以存储多个cookie,比如用户登录后存储了服务器在cookie返回的 sessionid
,然后向服务器发送其他请求也返回cookie。
- 个性化:用户偏好、主题
比如客户端请求了蓝色主题,在下次访问其他页面时,服务器记录了客户端的主题就会给页面提供蓝色主题。
- Tracking:分析用户行为
上面是客户端访问 shop.com
时展示一个html页面。这个页面有一个图片 http://3rd-party.com/image.jpg
,这张图片由第三方 3rd-party.com
提供。
你在这个图片的访问被记录了 from=shop.com
外链给第三方,这能让第三方公司 3rd-party.com
知道你是在 shop.com
查看了这张图片,第三方公司能跟踪到你的行为,并给你返回cookie让客户端保存。
下次你在其他网站展示图片,而图片同样来自 3rd-party.com
,你就会携带当时第三方给你的cookie,并且让第三方再记录你在哪个网站的访问,第三方就会跟踪完善对你的分析,推荐给你需要符合的信息。
2.3 Cookie存在的问题(了解即可)
- XSS(Cross-site scripting):跨站脚本攻击。即使用javascript拿到浏览器的cookie之后,发送到自己网站,以这种方式来盗取用户cookie
应对方式:HttpOnly。这个cookie只能用于HTTP请求,不能被javascript调用。它可以防止本地代码滥用cookie。客户端在HTTP请求时在cookie加上这个属性 Cookie:HttpOnly
- XSRF(Cross-site request forgery):跨站请求伪造。即在用户不知情的情况下访问已经保存了cookie的网站,以此来越权操作用户账户(例如盗取用户资金)
应对方式:Referer校验,主要从服务器安全角度考虑。
3 Authorization
Authorization
授权有两种主流方式:
-
Basic
-
Bearer
3.1 Basic
Basic
用得比较少,但也是很有用。格式如下:
// HTTP header中添加
// 对username:password进行Base64作为header的授权
Authorization:Basic<username:password(Base64ed)>
GET /user HTTP/1.1
Host:xxx.com
Authorization:Basic xxxx
虽然Basic简单,但是有风险(不过是可避免的):
-
使用的Base64(Base64是可逆的解码的),但是这可以通过HTTPS来解决,不完全是Basic的问题
-
token需要反复使用,那就要存在本地文件,如果手机被root了,有些恶意软件伪装成工具软件就有可能把token窃走,只能改密码
3.2 Bearer
Bearer
的授权流程还是要从OAuth2流程来说明。
格式如下:
// HTTP header中添加
Authrization:Bearer<barer token>
3.2.1 OAuth2流程(第三方授权)
OAuth2授权操作流程(以掘金申请授权github账户为例子,第三方网站:掘金,授权方网站:github):
- 第三方网站向授权方网站申请第三方授权合作,拿到
client id
和client secret
- 用户在使用第三方网站时,点击授权按钮[通过github授权],第三方网站将页面跳转到授权方网站,并传入
client id
作为自己的身份标识
- 授权方网站根据
client id
,将第三方网站的信息和第三方网站需要的用户权限展示给用户,并询问用户是否同意授权
- 用户点击[同意授权]按钮后,授权方网站将页面跳转回第三方网站,并传入
Authorization code
作为用户认可的凭证
- 第三方网站将
Authorization code
发送回自己的服务器
- 服务器将
Authorization code
和自己的client secret
一并发送给授权方的服务器,授权方服务器在验证通过后,返回access token
。OAuth流程结束
// HTTP请求带上access token从github获取用户信息处理注册登录
GET /user HTTP/1.1
Host: github.com
Authorization: Bearer xxxx
- 在上面的过程结束之后,第三方网站的服务器(或者有时客户端也会)就可以使用
access token
作为用户授权的令牌,向授权方网站发送请求来获取用户信息或操作用户账户。但这已经在OAuth流程之外
为什么用户点击授权按钮时要先拿到 Authorization code
,然后把 Authorization code
发送给服务器后,再通过 Authorization code
获取到 access token
,而不是用户点击授权按钮后直接返回 access token
?
其实是为了安全,Authorization code
不是token,而是用来获取token的。OAuth不强制授权流程必须使用HTTPS,通过 Authorization code
授权方github能判断是哪个第三方跟它获取 access token
。因此需要保证当通信路径中存在窃听者时,依然具有足够高的安全性。
3.2.2 微信登录(第三方登录)
第三方App微信登录其实也是一个OAuth2流程:
-
第三方App向腾讯申请第三方授权合作,拿到
client id
和client secret
-
用户在使用第三方App时,点击[微信登录],第三方App将使用微信sdk跳转到微信,并传入自己的
client id
作为自己的身份标识 -
微信通过和服务器交互,拿到第三方App的信息,并限制在界面中,然后询问用户是否同意该App使用微信登录
-
用户点击[使用微信登录]后,微信和服务器交互将授权信息提交,然后跳转回第三方App,并传入
Authorization code
作为用户认可的凭证 -
第三方App调用自己服务器的[微信登录]API,并传入
Authorization code
,然后等待服务器响应 -
服务器在收到登录请求后,拿收到的
Authorization code
去向微信的第三方授权接口发送请求,将Authorization code
和自己的client secret
一起作为参数发送,微信在验证通过后,返回access token
-
服务器在收到
access token
后,立即拿着access token
去向微信的用户信息接口发送请求,微信验证通过后,返回用户信息 -
服务器在收到用户信息后,在自己的数据库中为用户创建一个账户,并使用从微信服务器拿来的用户信息填入自己的数据库,以及将用户的ID和用户的微信ID做关联
-
用户创建完成后,服务器向客户端的请求发送响应,传送会刚创建好的用户信息
-
客户端收到服务器响应,用户登录成功
上面是第三方App微信登录的流程。
在实际开发中,发现了一种错误的做法,服务器在拿到 access token
后,就把 access token
发给客户端,让客户端自己去授权方获取用户数据。这就把OAuth2的优势浪费了,Authorization code
就没用了,这样还是会泄露 access token
。这是错误的:
3.2.3 OAuth2流程简单总结
OAuth2流程简单说明:
-
1、第三方网站持有授权网站给的
client id
和client secret
-
2、用户在第三方网站授权登录,跳转到授权网站提供
client id
-
3、用户允许授权拿到
Authrization code
给第三方网站服务器 -
4、第三方网站服务器把
Authrization code
和client secret
给授权网站拿到access token
上面的流程客户端只需要做第2、3步即可,剩下的都是服务器处理。
上面虽然列出了第三方授权和第三方登录,但是要区分好两者:
在技术上,其实只有第三方授权,没有第三方登录。因为第三方登录是github授予权限给掘金,掘金就可以通过token拿到github的用户信息,通过这些信息在掘金自己的服务器处理注册登录账号。
第三方登录是基于第三方授权的,所以掘金在登录过程中它不是第三方,因为它是实际给你注册登录的;而在授权过程中它是第三方,因为它是被github授权的。
3.2.4 自家App中使用Bearer token
有的App会在API设计中,将登录和授权设计成类似OAuth2的流程,但简化掉 Authrization code
概念。即:登录接口请求成功时,会返回 access token
,然后客户端在之后的请求中,就可以使用这个 access token
来当作 bearer token
进行用户操作了。
3.2.5 Refresh token
{
"token_type": "Bearer"
"access_token": "xxxxx"
"refresh_token": "xxxxx"
"expires_time": "xxxxx"
}
access_token
有失效时间,在它失效后,调用 refresh_token
接口,传入 refresh_token
来获取新的 access_token
。
提供 refresh token
主要是为了安全,可以做到以下处理:
-
当
access token
失窃,由于它有失效时间,因此坏人只有较短的时间来[做坏事] -
由于(在标准的OAuth2流程中)
refresh token
永远只存在于第三方服务的服务器中,因此refresh token
几乎没有失窃的风险