登录和授权的区别
- 登录:身份认证,即确认“你是你”的过程。
- 授权:由身份或持有的令牌确认享有某些权限(例如获取⽤户信息)。登录过程实质上的⽬的也是为了确认权限。
因此,在实际的应⽤中,多数场景下的“登录”和“授权”界限是模糊的。
Cookie
起源
“购物⻋”功能的需求,由 Netscape 浏览器开发团队打造。
⼯作机制
- 服务器需要客户端保存的内容,放在 Set-Cookie headers ⾥返回,客户端会⾃动保存。
- 客户端保存的 Cookies,会在之后的所有请求⾥都携带进 Cookie header ⾥发回给服务器。
- 客户端保存 Cookie 是按照服务器域名来分类的,例如 shop.com 发回的 Cookie 保存下来以后,在之后向 games.com 的请求中并不会携带。
- 客户端保存的 Cookie 在超时后会被删除、没有设置超时时间的 Cookie(称作 Session Cookie)在浏览器关闭后就会⾃动删除;另外,服务器也可以主动删除还未过期的客户端 Cookies。
图例:
如上图在购物车添加一个苹果,访问“cart”购物车的“path”,用“post”传入一个数据“apple = 1”,表示购物车增加一个苹果。
服务器处理完成,返回信息,同时附加一个 header—“Set-Cookie”(Cookie 是客户端给服务端用的,Set-Cookie是服务端给客户端用的),要求客户端保存cart = “apple = 1”
客户端按域(此处是 shop.com)保存该 Cookie ,原封不动地将 cart = “apple = 1”保存下来。当再次访问指定的域时,Cookie 内容会自动附加出去,这是由浏览器来实现的,网站开发者或者用户不必关心。
下次再向购物车添加香蕉时,只需要发送 banana = 1 ,同时将 Cookie 附加过去(浏览器执行的)。通过 psot 的 body 告诉服务器需要增加一个香蕉 ,通过 Cookie 自动告诉服务器已经增加了一个苹果。
服务器这边已经确定,现在购物车里面一个苹果一个香蕉。
客户端自动更新 Cookie 。整个过程服务器没有记录任何数据。
作用
会话管理:登录状态、购物车等
使用 Cookie 管理登录状态
向服务器发送登录请求,将用户名和密码添加。
服务器确认 a 登录了,并创建一个会话,表示服务端在和某个用户代理(User-Agent)在通信,对方可能是一个登录或未登录状态。例如服务器记录下会话信息:session:id = 123。并将会话信息返回给客户端,表示和客户端在交互。
客户端将 sessionid = 123 记录下来。
客户端再去访问时,会将 sessionid = 123 带过去。
服务器收到后,会对照是否有该会话,查到有 session:id = 123 ,并且处于登录状态,于是将用户信息返回。
Cookie 是可以有多个的,像这样客户端有两个 Cookie :sessionid = 123 和 cart = “apple = 1”,二者互不干扰,分别管理登录和购物车。但管理登录服务器还是需要保存会话的。
⽤ Cookie 管理⽤户偏好
假设 shop.com 有多个主题,客户端发出请求,服务端返回 client_id=123 。客户端特意发送请求改变主题为蓝色,服务器正常返回,并且记下了 client_id=123 的客户端的主题为蓝色。HTTP 是无状态的,下次客户端再访问另外一个页面 other_page ,client_id=123 自动附加,服务器根据 client_id=123 便知道需要蓝色主题了。
Tracking:分析用户行为
客户端向服务器发送信息。
之前的例子中,将服务器返回的 body 省略掉了,此处不能省略,因为 body 是做追踪的主要部分。可以看到图片链接中,增加了和图片地址本身无关的信息:“from=shop.com”,“3rd-party.com”这个网站就是帮助记录用户行踪的网站。追踪用户行为会有一个统一的网站,该网站记录用户去各个站点的信息。该图片链接在用户打开网页之后,会自动显示一张图片,图片会附加“from=shop.com”信息,该信息与图片显示无关。假设如果不是在“shop.com”打开该图片,而是在“taobao.com”,那么图片后面追加的信息则是“from=taobao.com”,二者显示的是同样的图片,但是对于第三方记录网站来说当前用户的来源不一样(“shop.com”还是“taobao.com”)。
客户端记录下 Cookie :client_id=123 ,向第三方(3rd-party.com)发送请求获取图片,第三方会记录 client_id=123 的用户来自 shop.com 。
第三方将图片显示给用户。
“client_id=abc” 是针对第三方网站的 id ,与 “client_id=123”没有关系,名字(client_id)是可以不一样的。“shop.com:client_id=123” 是用来管理用户的,而“3rd-party.com:client_id=abc”是用来记录用户行踪的。如果该用户再去“taobao.com”,该网站同样做了相同的第三方统计,在显示图片时,第三方网站会知道该用户之前去过“shop.com”,现在来了“taobao.com”,便会记录下这两个网站。这个时候对用户就有一定的画像了,用户去了两个网站。随着用户去的网站多,画像越详细,便有利于推广告。
利用 Cookie 机制“做坏事”与防范
XSS (Cross-site scripting)
跨站脚本攻击。即使⽤ JavaScript 拿到浏览器的
Cookie 之后,发送到⾃⼰的⽹站,以这种⽅式来盗取⽤户 Cookie。
应对⽅式: Server 在发送
Cookie 时,敏感的 Cookie 加上 HttpOnly ,这个 Cookie 只能⽤于 HTTP 请求,不能被 JavaScript 调⽤。它可以防⽌本地代码滥⽤ Cookie。
XSRF (Cross-site request forgery)
跨站请求伪造。即在⽤户不知情的情况下访问已经保存了 Cookie 的⽹站,以此来越权操作⽤户账户(例如盗取⽤户资⾦)。
应对⽅式主要是从服务器安全⻆度考虑,如 Referer 校验。
Authorization
两种主流⽅式: Basic 和 Bearer 。
Basic
格式: Authorization: Basic <username:password(Base64ed)>
示例如下(比如用户名和密码分别为 wy521angel:123456,Base64 转换后为 d3k1MjFhbmdlbDoxMjM0NTY= ):
1 | get /user http:/1.1 |
“Authorization: Basic d3k1MjFhbmdlbDoxMjM0NTY=”这个就是认证信息,用在 header 中,如果数据对了,就可以获取到用户信息。为了安全,需要使用 Https 来传输。
Bearer
格式: Authorization: Bearer <bearer token>
bearer token 的获取⽅式:通过 OAuth2 的授权流程。
OAuth2 的流程图例:
- 第三⽅⽹站向授权⽅⽹站申请第三⽅授权合作,拿到 client id 和 client secret ;
- ⽤户在使⽤第三⽅⽹站时,点击「通过 XX (如 GitHub) 授权」按钮,第三⽅⽹站将⻚⾯跳转到授权⽅⽹站,并传⼊ client id 作为⾃⼰的身份标识;
- 授权⽅⽹站根据 client id ,将第三⽅⽹站的信息和第三⽅⽹站需要的⽤户权限展示给⽤户,并询问⽤户是否同意授权;
- ⽤户点击「同意授权」按钮后,授权⽅⽹站将⻚⾯跳转回第三⽅⽹站,并传⼊ Authorization code 作为⽤户认可的凭证;
- 第三⽅⽹站将 Authorization code 发送回⾃⼰的服务器;
- 服务器将 Authorization code 和⾃⼰的 client secret ⼀并发送给授权⽅的服务器,授权⽅
服务器在验证通过后,返回 access token, OAuth 流程结束; - 在上⾯的过程结束之后,第三⽅⽹站的服务器(或者有时客户端也会)就可以使⽤ access
token 作为⽤户授权的令牌,向授权⽅⽹站发送请求来获取⽤户信息或操作⽤户账户。但这
已经在 OAuth 流程之外。
如下图所示,Server 有时会把 access token 发还给客户端,这个不是不允许的,只不过这会对安全有一定的影响:
如下图所示,这样请求也是允许的,很多公司都这样使用,只是它可能不太具有 OAuth2 的安全性:
1 | 为什么 OAuth 要引⼊ Authorization code,并需要申请授权的第三⽅将 Authorization code 发送回⾃⼰的服务器,再从服务器来获取 access token,⽽不是直接返回 access token ?这样复杂的流程意义何在? |
1 | 第三方登录与第三方授权中的“第三方”: |
微信登录:
第三⽅ 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 中使⽤ Bearer token
有的 App 会在 Api 的设计中,将登录和授权设计成类似 OAuth2 的过程,但简化掉 Authorization code 概念。即:登录接⼝请求成功时,会返回 access token,然后客户端在之后的请求中,就可以使⽤这个 access token 来当做 bearer token 进⾏⽤户操作了。
Refresh token
1 | { |
- ⽤法: access token 有失效时间,在它失效后,调⽤ refresh token 接⼝,传⼊ refresh_token 来获取新的 access token。
- ⽬的:安全。当 access token 失窃,由于它有失效时间,因此坏⼈只有较短的时间来“做坏
事”;同时,由于(在标准的 OAuth2 流程中) refresh token 永远只存在与第三⽅服务的服务
器中,因此 refresh token ⼏乎没有失窃的⻛险。
参考资料:
腾讯课堂 HenCoder