在一些互联网公司的面试中,面试官每每会问这样一个问题:html
“若是禁用浏览器 cookie,如何实现用户追踪和认证?”web
遗憾的是依然有大量候选人答非所问,没法搞清楚 cookie 和 session 之间的区别。而在工做中也有让人惊讶的真实案例:把 user ID 存储到 local storage 中当作 token 使用,缘由是他们声称弃用了 cookie 这种落后的东西;一个移动端项目,服务器给出的 API 中须要客户端模拟一个 cookie,从而像浏览器中 ajax 那样消费 API。面试
互联网是基于 HTTP 协议构建的,而 HTTP 协议由于简单流行开来,可是 HTTP 协议是无状态(通讯层面上虚电路比数据报昂贵太多)的,为此人们为了追踪用户想出了各类办法,包括 cookie/session 机制、token、flash 跨浏览器 cookie 甚至浏览器指纹等。ajax
把用户身份藏在每个地方(浏览器指纹技术甚至不须要存储介质)算法
讲使用 spring security 等具体技术的资料已经不少了,这篇文章不打算写框架和代码的具体实现。咱们会讨论认证和受权的区别,而后会介绍一些被业界普遍采用的技术,最后会聊聊怎么为 API 构建选择合适的认证方式。spring
首先,认证和受权是两个不一样的概念,为了让咱们的 API 更加安全和具备清晰的设计,理解认证和受权的不一样就很是有必要了,它们在英文中也是不一样的单词。数据库
认证是 authentication,指的是当前用户的身份,当用户登录事后系统便能追踪到他的身份作出符合相应业务逻辑的操做。即便用户没有登陆,大多数系统也会追踪他的身份,只是当作来宾或者匿名用户来处理。认证技术解决的是 “我是谁?”的问题。json
受权则不一样,受权是 authorization,指的是什么样的身份被容许访问某些资源,在获取到用户身份后继续检查用户的权限。单一的系统受权每每是伴随认证来完成的,可是在开放 API 的多系统结构下,受权能够由不一样的系统来完成,例如 OAuth。受权技术是解决“我能作什么?”的问题。浏览器
实现认证和受权的基础是须要一种媒介(credentials)来标记访问者的身份或权利,在现实生活中每一个人都须要一张身份证才能访问本身的银行帐户、结婚和办理养老保险等,这就是认证的凭证;在古代军事活动中,皇帝会给出战的将军颁发兵符,下级将领不关心持有兵符的人,只须要执行兵符对应的命令便可。在互联网世界中,服务器为每个访问者颁发 session ID 存放到 cookie,这就是一种凭证技术。数字凭证还表如今方方面面,SSH 登陆的密匙、JWT 令牌、一次性密码等。缓存
用户帐户也不必定是存放在数据库中的一张表,在一些企业 IT 系统中,对帐户管理和权限有了更多的要求。因此帐户技术 (accounting)能够帮助咱们使用不一样的方式管理用户帐户,同时具备不一样系统之间共享帐户的能力。例如微软的活动目录(AD),以及简单目录访问协议(LDAP),甚至区块链技术。
还有一个重要的概念是访问控制策略(AC)。若是咱们须要把资源的权限划分到一个很细的粒度,就不得不考虑用户以何种身份来访问受限的资源,选择基于访问控制列表(ACL)仍是基于用户角色的访问控制(RBAC)或者其余访问控制策略。
在流行的技术和框架中,这些概念都没法孤立的被实现,所以在现实中使用这些技术时,你们每每为一个 OAuth2 是认证仍是受权这种概念争论不休。为了容易理解,我在文末附上了一份常见技术和概念的术语表。下面我会介绍在API开发中经常使用的几种认证和受权技术:HTTP Basic AUthentication、HAMC、OAuth2,以及凭证技术JWT token。
你必定用过这种方式,但不必定知道它是什么,在不久以前,当你访问一台家用路由器的管理界面,每每会看到一个浏览器弹出表单,要求你输入用户密码。
在这背后,当用户输入完用户名密码后,浏览器帮你作了一个很是简单的操做:
组合用户名和密码而后 Base64 编码
给编码后的字符串添加 Basic 前缀,而后设置名称为 Authorization 的 header 头部
API 也能够很是简单的提供 HTTP Basic Authentication 认证方式,那么客户端能够很简单经过 Base64 传输用户名和密码便可:
这种方式实现起来很是简单,在大量场景下被采用。固然缺点也很明显,Base64 只能称为编码,而不是加密 (实际上无需配置密匙的客户端并无任何可靠地加密方式,咱们都依赖 TSL 协议)。这种方式的致命弱点是编码后的密码若是明文传输则容易在网络传输中泄露,在密码不会过时的状况下,密码一旦泄露,只能经过修改密码的方式。
在咱们对接一些 PASS 平台和支付平台时,会要求咱们预先生成一个 access key(AK) 和 secure key(SK),而后经过签名的方式完成认证请求,这种方式能够避免传输 secure key,且大多数状况下签名只容许使用一次,避免了重放攻击。
这种基于 AK/SK 的认证方式主要是利用散列的消息认证码 (Hash-based MessageAuthentication Code) 来实现的,所以有不少地方叫 HMAC 认证,实际上不是很是准确。HMAC 只是利用带有 key 值的哈希算法生成消息摘要,在设计 API 时有具体不一样的实现。
HMAC 在做为网络通讯的认证设计中做为凭证生成算法使用,避免了口令等敏感信息在网络中传输。基本过程以下:
为了让每一次请求的签名变得独一无二,从而实现重放攻击,咱们须要在签名时放入一些干扰信息。
在业界标准中有两种典型的作法,质疑/应答算法(OCRA: OATH Challenge-Response Algorithm)、基于时间的一次性密码算法(TOTP:Time-based One-time Password Algorithm)。
质疑/应答算法须要客户端先请求一次服务器,得到一个 401 未认证的返回,并获得一个随机字符串(nonce)。将 nonce 附加到按照上面说到的方法进行 HMAC 签名,服务器使用预先分配的 nonce 一样进行签名校验,这个 nonce 在服务器只会被使用一次,所以能够提供惟一的摘要。
为了不额外的请求来获取 nonce,还有一种算法是使用时间戳,而且经过同步时间的方式协商到一致,在必定的时间窗口内有效(1分钟左右)。
这里的只是利用时间戳做为验证的时间窗口,并不能严格的算做基于时间的一次性密码算法。标准的基于时间的一次性密码算法在两步验证中被大量使用,例如 Google 身份验证器不须要网络通讯也能实现验证(但依赖准确的授时服务)。原理是客户端服务器共享密钥而后根据时间窗口能经过 HMAC 算法计算出一个相同的验证码。
TOTP 基本原理和常见厂商
OAuth(开放受权)是一个开放标准,容许用户受权第三方网站访问他们存储在另外的服务提供者上的信息,而不须要将用户名和密码提供给第三方网站或分享他们数据的全部内容。
OAuth 是一个受权标准,而不是认证标准。提供资源的服务器不须要知道确切的用户身份(session),只须要验证受权服务器授予的权限(token)便可。
上图只是 OAuth 的一个简化流程,OAuth 的基本思路就是经过受权服务器获取 access token 和 refresh token(refresh token 用于从新刷新access token),而后经过 access token 从资源服务器获取数据 。在特定的场景下还有下面几种模式:
若是须要获取用户的认证信息,OAuth 自己没有定义这部份内容,若是须要识别用户信息,则须要借助另外的认证层,例如 OpenID Connect。
在一些介绍OAuth 的博客中不多讲到资源服务器是怎么验证 access token 的。OAuth core 标准并无定义这部分,不过在 OAuth 其余标准文件中提到两种验证 access token的方式。
几乎全部人刚开始了解 OAuth 时都有一个一疑问,为何已经有了 access token 还须要 refresh token 呢?
受权服务器会在第一次受权请求时一块儿返回 access token 和refresh token,在后面刷新 access token 时只须要 refresh token。 access token 和 refresh token 的设计意图是不同的,access token 被设计用来客户端和资源服务器之间交互,而 refresh token 是被设计用来客户端和受权服务器之间交互。
某些受权模式下 access token 须要暴露给浏览器,充当一个资源服务器和浏览器之间的临时会话,浏览器和资源服务器之间不存在签名机制,access token 成为惟一凭证,所以 access token 的过时时间(TTL)应该尽可能短,从而避免用户的 access token 被嗅探攻击。
因为要求 access token 时间很短,refresh token 能够帮助用户维护一个较长时间的状态,避免频繁从新受权。你们会以为让 access token 保持一个长的过时时间不就能够了吗?实际上 refresh token 和 access token 的不一样之处在于即便 refresh token 被截获,系统依然是安全的,客户端拿着 refresh token 去获取 access token 时同时须要预先配置的 secure key,客户端和受权服务器以前始终存在安全的认证。
认证方面的术语实在太多,我在搭建本身的认证服务器或接入第三方认证平台时,有时候到完成开发工做的最后一刻都没法理解这些术语。
OAuth 负责解决分布式系统之间的受权问题,即便有时候客户端和资源服务器或者认证服务器存在同一台机器上。OAuth 没有解决认证的问题,但提供了良好的设计利于和现有的认证系统对接。
Open ID 解决的问题是分布式系统之间身份认证问题,使用Open ID token 能在多个系统之间验证用户,以及返回用户信息,能够独立使用,与 OAuth 没有关联。
OpenID Connect 解决的是在 OAuth 这套体系下的用户认证问题,实现的基本原理是将用户的认证信息(ID token)当作资源处理。在 OAuth 框架下完成受权后,再经过 access token 获取用户的身份。
这三个概念之间的关系有点难以理解,用现实场景来讲,若是系统中须要一套独立的认证系统,并不须要多系统之间的受权能够直接采用 Open ID。若是使用了 OAuth 做为受权标准,能够再经过 OpenID Connect 来完成用户的认证。
在 OAuth 等分布式的认证、受权体系下,对凭证技术有了更多的要求,好比包含用户 ID、过时等信息,不须要再外部存储中关联。所以业界对 token 作了进一步优化,设计了一种自包含令牌,令牌签发后无需从服务器存储中检查是否合法,经过解析令牌就能获取令牌的过时、有效等信息,这就是JWT (JSON Web Token)。
JWT 是一种包含令牌(self-contained token),或者叫值令牌 (value token),咱们之前使用关联到 session 上的 hash 值被叫作引用令牌(reference token)。
简而言之,一个基本的JWT令牌为一段点分3段式结构。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
生成JWT 令牌的流程为
所以只须要签名的 secret key 就能校验 JWT 令牌,若是在消息体中加入用户 ID、过时信息就能够实现验证令牌是否有效、过时了,无需从数据库/缓存中读取信息。由于使用了加密算法,因此第1、二部分即便被修改(包括过时信息)也没法经过验证。JWT 优势是不只能够做为 token 使用,同时也能够承载一些必要信息,省去屡次查询。
注意:
JWT token 在微服务的系统中优点特别突出。多层调用的 API 中能够直接传递 JWT token,利用自包含的能力,能够减小用户信息查询次数;更重要的是,使用非对称的加密方式能够经过在系统中分发密匙的方式验证 JWT token。
固然 OAuth 对 access token 等凭证所选用的技术并无作出限制,OAuth 并不强制使用 JWT,在使用 JWT 自包含特性的优点时,必须考虑到 JWT 撤回困难的问题。在一些对撤回 token 要求很高的项目中不适合使用JWT,即便采用了一些方案实现(whitelist 和 blacklist)也违背了设计 JWT 的初衷。
在构建 API 时,开发者会发现咱们的认证方式和网页应用有一些不一样,除了像 ajax 这种典型的 web 技术外,若是咱们但愿 API 是无状态的,不推荐使用 Cookie。
使用 Cookie 的本质是用户第一次访问时服务器会分配一个 Session ID,后面的请求中客户端都会带上这个 ID 做为当前用户的标志,由于 HTTP 自己是无状态的,Cookie 属于一种内建于浏览器中实现状态的方式。若是咱们的 API 是用来给客户端使用的,强行要求 API 的调用者管理Cookie 也能够完成任务。
在一些遗留或者不是标准的认证明现的项目中,咱们依然能够看到这些作法,快速地实现认证。
随着微服务的发展,API 的设计不只仅是面向 WEB 或者 Mobile APP,还有BFF(Backend for Frontend)和 Domain API 的认证,以及第三方服务的集成。
客户端到服务器之间认证和服务器到服务器之间认证是不一样的。
咱们把终端用户(Human)参与的通讯,叫作 Human-to-machine (H2M),服务器与服务器之间的通讯叫作 Machine-to-machine (M2M)。
H2M 的通讯须要更高的安全性,M2M 的通讯自然比 H2M 安全,所以更多的强调性能,在不一样的场合下选择合适的认证技术就显得特别重要。例如 HTTP Basic Authentication 用来做为 H2M 认证显得有些落后,可是在 M2M 中被大量使用。
另外值得一提的是,H2M 这种通讯方式下,客户端不受控制,因为没法自主分发密匙,认证通讯的安全高度依赖 HTTPS。
从一个宏观的角度看待他们的关系,对咱们技术选型很是有帮助。
[HMAC: Keyed-Hashing for Message Authentication]( "www.ietf.org/rfc/rfc2104… ")
HOTP: An HMAC-Based One-Time Password Algorithm
OCRA: OATH Challenge-Response Algorithm
The OAuth 2.0 Authorization Framework
Internet-Draft Archive for OAuth
文/ThoughtWorks林宁
更多精彩洞见,请关注微信公众号:ThoughtWorks洞见