权限
Cookie
Cookie是客户端技术,程序把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。这样,web资源处理的就是用户各自的数据了。
但是 cookie的用户信息是保存在客户端本地的 用户可以通过浏览器的设置里面的cookie找到cookie的相关值 也可以删除修改值 所以cookie是相对不安全的 所以我们还有一种session方案
Session
session是基于cookie的 只不过信息存储在服务端 客户端这边只有一个随机字符串 没有任何的可读性。
Session 的前后端操作流程通常包括以下几个步骤:
- 前端发送请求:客户端向服务端发送 HTTP 请求,例如通过浏览器访问网页、发送 AJAX 请求等。
- 服务端创建 Session:服务端在接收到客户端请求后,会为该客户端创建一个 Session。Session 是服务端存储用户会话信息的一种机制,通常以键值对的形式存储在服务端的内存、数据库或者缓存中。
- 服务端设置 Session ID:服务端在响应客户端请求时,会将 Session ID 保存在客户端的 set-Cookie 中,并将该 Cookie 发送给客户端。通常情况下,服务端会将 Cookie 名称设置为 JSESSIONID
Session 是存储在服务端的
- 客户端保存 Session ID:客户端接收到服务端的响应后,会将 Cookie 中的 Session ID 保存在本地存储中,通常是浏览器的 Cookie 存储中或者通过 JavaScript 代码将 Session ID 存储在本地存储中。
- 客户端发送请求:当客户端再次向服务端发送 HTTP 请求时,会将本地存储的 Session ID 自动添加到请求头中的 Cookie 字段中
- 服务端读取 Session ID:服务端接收到客户端请求后,会从请求头中的 Cookie 字段中读取客户端发送的 Session ID。然后,服务端可以通过该 Session ID 来查找存储在服务端的对应的 Session,并读取、修改、删除其中存储的会话信息。服务端可以根据 Session 中存储的会话信息来判断客户端的身份、会话状态等信息,从而提供不同的服务。
但是 session也有缺点 如果服务端的用户量大 每个用户都需要构建一条session数据存储用户信息 会占用服务器端资源。
OAuth
OAuth 不是一个API或者服务,而是一个验证授权(Authorization)的开放标准,所有人都有基于这个标准实现自己的OAuth。
更具体来说,OAuth是一个标准,app可以用来实现secure delegated access
. OAuth基于HTTPS,以及APIs,Service应用使用access token
来进行身份验证。
OAuth主要有OAuth 1.0a和OAuth 2.0两个版本,并且二者完全不同,且不兼容。OAuth2.0 是目前广泛使用的版本,我们多数谈论OAuth时,为OAuth2.0。
OAuth 中心组件
OAuth 主要下面中心组件构成 (Central Components), 接下来会依次介绍如下这些组件。
- Scopes and Consent
- Actors
- Clients
- Tokens
- Authorization Server
- Flows
OAuth Scopes
Scopes即Authorizaion时的一些请求权限,即与access token绑定在一起的一组权限。OAuth Scopes将授权策略(Authorization policy decision)与授权执行分离开来。并会很明确的表示OAuth Scopes将会获得的权限范围。
OAuth Actors
OAuth的流程中,主要有如下四个角色。其关系如下图所示:
- Resource Owner: 用户拥有资源服务器上面的数据。例如:我是一名Facebook的用户,我拥有我的Facebook 个人简介的信息。
- Resource Server: 存储用户信息的API Service
- Client: 想要访问用户的客户端
- Authorization Server: OAuth的主要引擎,授权服务器,获取token。
OAuth Tokens
- Access token: 即客户端用来请求Resource Server(API). Access tokens通常是short-lived短暂的。access token是short-lived, 因此没有必要对它做revoke, 只需要等待access token过期即可。
- Refresh token: 当access token过期之后refresh token可以用来获取新的access token。refresh token是long-lived。refresh token可以被revoke。
Token从Authorization server上的不同的endpoint获取。主要两个endpoint为authorize endpoint
和token endpoint
. authorize endpoint主要用来获得来自用户的许可和授权(consent and authorization),并将用户的授权信息传递给token endpoint
。token endpoint对用户的授权信息,处理之后返回access token
和refresh token
。 当access token过期之后,可以使用refresh token去请求token endpoint获取新的token。(开发者在开发endpoint时,需要维护token的状态,refresh token rotate)。
OAuth有两个流程,1.获取Authorization,2. 获取Token。这两个流程发送在不同的channel,Authorization发生在Front Channel(发生在用户浏览器)而Token发生在Back Channel。
- Front Channel: 客户端通过浏览器发送Authorization请求,由浏览器重定向到Authorization Server上的Authorization Endpoint,由Authorization Server返回对话框,并询问“是否允许这个应用获取如下权限”。Authorization通过结束后通过浏览器重定向到回调URL(Callback URL)。
- Back Channel: 获取Token之后,token应有由客户端应用程序使用,并与资源服务器(Resource Service)进行交互。
OAuth Flows
- implicit flow: 也称之为 2 Legged OAuth 所有OAuth的过程都在浏览器中完成,且access token通过authorization request (front channel only) 直接返回。不支持refresh token。安全性不高。
- Authorization code: 也称之为 3 Legged OAuth。使用front channel和back channel。front channel负责authorization code grant。back channel负责将authorization code换成(exchange)access token以及refresh token。
- Client Credential flow: 对于server-to-server的场景。通常使用这种模式。在这种模式下要保证client secret不会被泄露。
- Resource Owner Password Flow:类似于直接用户名,密码的模式,不推荐使用。
安全性建议
- 使用CSRF token。state参数保证整个流程的完整性
- 重定向URL(redirect URIs)要在白名单内
- 通过client ID将authorization grant和token request确保在同一个client上发生
- 对于保密的client(confidential client),确保client secret不被泄露。不要将secret随代码一起发布
OpenID Connect
OpenID Connect 是在OAuth2.0 协议基础上增加了身份验证层 (identity layer)。OAuth 2.0 定义了通过access token去获取请求资源的机制,但是没有定义提供用户身份信息的标准方法。OpenID Connect作为OAuth2.0的扩展,实现了Authentication的流程。OpenID Connect根据用户的 id_token
来验证用户,并获取用户的基本信息。
id_token
通常是JWT(Json Web Token),JWT有三部分组成,header,body,signature。header主要用来声明使用的算法,声明claim在body中,并且签名在signature中。OpenID Connection 在OAuth2.0 的基础上额外增加了UserInfo的Endpoint。id_token
作为访问UserInfo Endpoint的凭证来获取用户的基本信息(profile,email,phone),并验证用户。
OpenID Connect流程主要涉及如下几个步骤:
- 发现获取OIDC metadata
- 执行OAuth流程,获取
id_token
和access_token
。例如:在Authorization code
模式下即为通过code来换取id_token
和access_token
。 - 获取JWT签名(signature key)并且可选的动态的注册客户端应用
- 基于日期签名来本地验证JWT
id_token
,或者将id_token
发给后端backend进行验证 - 根据
id_token
通过UserInfo Endpoint获取用户信息,根据access_token
获取用户其他资源信息
SSO
单点登录(SingleSignOn,SSO),就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后,即可获得访问单点登录系统中其他关联系统和应用软件的权限,同时这种实现是不需要管理员对用户的登录状态或其他信息进行修改的,这意味着在多个应用系统中,用户只需一次登录就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,辅助了用户管理,是比较流行的。
系统共享
统一的认证系统是SSO的前提之一。认证系统的主要功能是将用户的登录信息和用户信息库相比较,对用户进行登录认证;认证成功后,认证系统应该生成统一的认证标志(ticket),返还给用户。另外,认证系统还应该对ticket进行校验,判断其有效性。
信息识别
要实现SSO的功能,让用户只登录一次,就必须让应用系统能够识别已经登录过的用户。应用系统应该能对ticket进行识别和提取,通过与认证系统的通讯,能自动判断当前用户是否登录过,从而完成单点登录的功能。
另外:
- 单一的用户信息数据库并不是必须的,有许多系统不能将所有的用户信息都集中存储,应该允许用户信息放置在不同的存储中,事实上,只要统一认证系统,统一ticket的产生和校验,无论用户信息存储在什么地方,都能实现单点登录。
- 统一的认证系统并不是说只有单个的认证服务器。
- 当用户在访问应用系统1时,由第一个认证服务器进行认证后,得到由此服务器产生的ticket。当他访问应用系统2的时候,认证服务器2能够识别此ticket是由第一个服务器产生的,通过认证服务器之间标准的通讯协议(例如SAML)来交换认证信息,仍然能够完成SSO的功能。
JWT
实现登录功能,最核心的技术是 JWT(也就是 token)。我们通过 token 来记录用户的登录状态。并把 token 存储到 vuex 中。
- 首先,我们调用登录的 API 接口,把用户填写的表单信息发送给后端(在真正发起请求之前,为了让用户体验更好,可以在对表单的数据进行合法性校验)。
- 后端校验通过之后,会返回一个 token 字符串,为了在项目中能够方便地获取和使用 token,我们会把 token 存储到 vuex 中。
- 由于 vuex 中的数据都是存储在内存中的,页面一刷新,状态就丢失了。因此,我们通过 vuex-persistedstate 这个插件,可以把 vuex 中的所有数据,持久化存储到本地。
- 拿到token 之后,在
axios
请求拦截器里注入token。 - 如果 token 过期了,会触发 axios 的响应拦截器。通过 err.response.status 可以判断响应状态码是否为 401,如果是,则证明 token 过期了,我们需要清空 vuex 中的数据。跳转到 login 登录页面,让用户重新进行登录。