我们知道,HTTP 是无状态的,所以,当我们需要获得用户是否在登录的状态时,我们需要检查用户的登录状态,一般来说,用户的登录成功后,服务器会发一个登录凭证(又被叫作 Token),就像你去访问某个公司,在前台被认证过合法后,这个公司的前台会给你的一个访客卡一样,之后,你在这个公司内去到哪都用这个访客卡来开门,而不再校验你是哪一个人。在计算机的世界里,这个登录凭证的相关数据会放在两种地方,一个地方在用户端,以 Cookie 的方式(一般不会放在浏览器的 Local Storage,因为这很容易出现登录凭证被 XSS 攻击),另一个地方是放在服务器端,又叫 Session 的方式(SessonID 存于 Cookie)。
创新互联主营扬州网站建设的网络公司,主营网站建设方案,重庆APP开发,扬州h5重庆小程序开发搭建,扬州网站营销推广欢迎扬州等地区企业咨询
但是,这个世界还是比较复杂的,除了用户访问,还有用户委托的第三方的应用,还有企业和企业间的调用,这里,我想把业内常用的一些 API认证技术相对系统地总结归纳一下,这样可以让大家更为全面的了解这些技术。注意,这是一篇长文!
本篇文章会覆盖如下技术:
HTTP Basic
HTTP Basic 是一个非常传统的 API 认证技术,也是一个比较简单的技术。这个技术也就是使用 username 和 password 来进行登录。整个过程被定义在了 RFC 2617 中,也被描述在了 Wikipedia: Basic Access Authentication 词条中,同时也可以参看 MDN HTTP Authentication
其技术原理如下:
我们可以看到,使用 Base64 的目的无非就是为了把一些特殊的字符给搞掉,这样就可以放在 HTTP 协议里传输了。而这种方式的问题最大的问题就是把用户名和口令放在网络上传,所以,一般要配合 TLS/SSL 的安全加密方式来使用。我们可以看到 JIRA Cloud 的 API 认证支持HTTP Basic 这样的方式。
但我们还是要知道,这种把用户名和密码同时放在公网上传输的方式有点不太好,因为 Base64 不是加密协议,而是编码协议,所以就算是有 HTTPS 作为安全保护,给人的感觉还是不放心。
Digest Access
中文称“HTTP 摘要认证”,最初被定义在了 RFC 2069 文档中(后来被 RFC 2617 引入了一系列安全增强的选项;“保护质量”(qop)、随机数计数器由客户端增加、以及客户生成的随机数)。
其基本思路是,请求方把用户名口令和域做一个 MD5 – MD5(username:realm:password) 然后传给服务器,这样就不会在网上传用户名和口令了,但是,因为用户名和口令基本不会变,所以,这个 MD5 的字符串也是比较固定的,因此,这个认证过程在其中加入了两个事,一个是 nonce 另一个是 qop
- WWW-Authenticate: Digest realm="testrealm@host.com",
- qop="auth,auth-int",
- nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
- opaque="5ccc069c403ebaf9f0171e9517f40e41"
- GET /dir/index.html HTTP/1.0
- Host: localhost
- Authorization: Digest username="Mufasa",
- realm="testrealm@host.com",
- nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
- uri="%2Fcoolshell%2Fadmin",
- qop=auth,
- nc=00000001,
- cnonce="0a4f113b",
- response="6629fae49393a05397450978507c4ef1",
- opaque="5ccc069c403ebaf9f0171e9517f40e41"
维基百科上的 Wikipedia: Digest access authentication 词条非常详细地描述了这个细节。
摘要认证这个方式会比之前的方式要好一些,因为没有在网上传递用户的密码,而只是把密码的 MD5 传送过去,相对会比较安全,而且,其并不需要是否 TLS/SSL 的安全链接。但是,别看这个算法这么复杂,最后你可以发现,整个过程其实关键是用户的 password,这个 password 如果不够得杂,其实是可以被暴力破解的,而且,整个过程是非常容易受到中间人攻击——比如一个中间人告诉客户端需要的 Basic 的认证方式 或是 老旧签名认证方式(RFC2069)。
App Secret Key + HMAC
先说 HMAC 技术,这个东西来自于 MAC – Message Authentication Code,是一种用于给消息签名的技术,也就是说,我们怕消息在传递的过程中被人修改,所以,我们需要用对消息进行一个 MAC 算法,得到一个摘要字串,然后,接收方得到消息后,进行同样的计算,然后比较这个 MAC 字符串,如果一致,则表明没有被修改过(整个过程参看下图)。而 HMAC – Hash-based Authenticsation Code,指的是利用 Hash 技术完成这一工作,比如:SHA-256算法。
我们再来说 App ID,这个东西跟验证没有关系,只是用来区分,是谁来调用 API 的,就像我们每个人的身份证一样,只是用来标注不同的人,不是用来做身份认证的。与前面的不同之处是,这里,我们需要用 App ID 来映射一个用于加密的密钥,这样一来,我们就可以在服务器端进行相关的管理,我们可以生成若干个密钥对(AppID, AppSecret),并可以有更细粒度的操作权限管理。
最后,发出 HTTP Request 时,在 HTTP 头的 Authorization 字段中放入如下的信息:
- Authorization: AWS4-HMAC-SHA256
- Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request,
- SignedHeaders=content-type;host;x-amz-date,
- Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7
其中的 AKIDEXAMPLE 是 AWS Access Key ID, 也就是所谓的 AppID,服务器端会根据这个 AppID 来查相关的 Secret Access Key,然后再验证签名。如果,你对这个过程有点没看懂的话,你可以读一读这篇文章——《Amazon S3 Rest API with curl》这篇文章里有好些代码,代码应该是最有细节也是最准确的了。
这种认证的方式好处在于,AppID 和 AppSecretKey,是由服务器的系统开出的,所以,是可以被管理的,AWS 的 IAM 就是相关的管理,其管理了用户、权限和其对应的 AppID 和AppSecretKey。但是不好的地方在于,这个东西没有标准 ,所以,各家的实现很不一致。比如:Acquia 的 HMAC,微信的签名算法 (这里,我们需要说明一下,微信的 API 没有遵循HTTP 协议的标准,把认证信息放在 HTTP 头的 Authorization 里,而是放在 body 里)
JWT – JSON Web Tokens
JWT 是一个比较标准的认证解决方案,这个技术在 Java 圈里应该用的是非常普遍的。JWT 签名也是一种 MAC(Message Authentication Code)的方法。JWT 的签名流程一般是下面这个样子:
用户使用用户名和口令到认证服务器上请求认证。
认证服务器验证用户名和口令后,以服务器端生成 JWT Token,这个 token 的生成过程如下:
然后把 base64(header).base64(payload).signature 作为 JWT token 返回客户端。
客户端使用 JWT Token 向应用服务器发送相关的请求。这个 JWT Token 就像一个临时用户权证一样。
当应用服务器收到请求后:
我们可以看以,上面的这个过程,是在认证服务器上为用户动态生成 Secret Key 的,应用服务在验签的时候,需要到认证服务器上去签,这个过程增加了一些网络调用,所以,JWT 除了支持 HMAC-SHA256 的算法外,还支持 RSA 的非对称加密的算法。
使用 RSA 非对称算法,在认证服务器这边放一个私钥,在应用服务器那边放一个公钥,认证服务器使用私钥加密,应用服务器使用公钥解密,这样一来,就不需要应用服务器向认证服务器请求了,但是,RSA 是一个很慢的算法,所以,虽然你省了网络调用,但是却费了 CPU,尤其是Header 和 Payload 比较长的时候。所以,一种比较好的玩法是,如果我们把 header 和 payload 简单地做 SHA256,这会很快,然后,我们用 RSA 加密这个 SHA256 出来的字符串,这样一来,RSA 算法就比较快了,而我们也做到了使用 RSA 签名的目的。
最后,我们只需要使用一个机制在认证服务器和应用服务器之间定期地换一下公钥私钥对就好了。
这里强烈建议全文阅读 Anglar 大学的 《JSW:The Complete Guide to JSON Web Tokens》
OAuth 1.0
OAuth 也是一个 API 认证的协议,这个协议最初在 2006 年由 Twitter 的工程师在开发 OpenID 实现的时候和社交书签网站 Ma.gnolia 时发现,没有一种好的委托授权协议,后来在 2007 年成立了一个 OAuth 小组,知道这个消息后,Google 员工也加入进来,并完善有善了这个协议,在 2007 年底发布草案,过一年后,在 2008 年将 OAuth 放进了 IETF 作进一步的标准化工作,最后在 2010 年 4 月,正式发布 OAuth 1.0,即:RFC 5849 (这个 RFC 比起 TCP 的那些来说读起来还是很轻松的),不过,如果你想了解其前身的草案,可以读一下 OAuth Core 1.0 Revision A ,我在下面做个大概的描述。
根据RFC 5849,可以看到 OAuth 的出现,目的是为了,用户为了想使用一个第三方的网络打印服务来打印他在某网站上的照片,但是,用户不想把自己的用户名和口令交给那个第三方的网络打印服务,但又想让那个第三方的网络打印服务来访问自己的照片,为了解决这个授权的问题, OAuth 这个协议就出来了。
这个协议有三个角色:
这个协义有三个阶段:
整个授权过程是这样的:
下图附上一个 Yahoo! 的流程图可以看到整个过程的相关细节。
因为上面这个流程有三方:User,Consumer 和 Service Provide,所以,又叫 3-legged flow,三脚流程。OAuth 1.0 也有不需要用户参与的,只有 Consumer 和 Service Provider 的, 也就是 2-legged flow 两脚流程,其中省掉了用户认证的事。整个过程如下所示:
最后,再来说一说 OAuth 中的签名。
下图是整个签名的示意图:
图片还是比较直观的,我就不多解释了。
OAuth 2.0
在前面,我们可以看到,从 Digest Access, 到 AppID+HMAC,再到 JWT,再到 OAuth 1.0,这些个 API 认证都是要向 Client发一个密钥(或是用密码)然后用 HASH 或是 RSA 来签 HTTP 的请求,这其中有个主要的原因是,以前的 HTTP 是明文传输,所以,在传输过程中很容易被篡改,于是才搞出来一套的安全签名机制,所以,这些个认证的玩法是可以在 HTTP 明文协议下玩的。
这种使用签名方式大家可以看到是比较复杂的,所以,对于开发者来说,也是很不友好的,在组织签名的那些 HTTP 报文的时候,各种,URLEncode 和 Base64,还要对 Query 的参数进行排序,然后有的方法还要层层签名,非常容易出错,另外,这种认证的安全粒度比较粗,授权也比较单一,对于有终端用户参与的移动端来说也有点不够。所以,在 2012 年的时候,OAuth 2.0 的 RFC 6749 正式放出。
OAuth 2.0 依赖于 TLS/SSL 的链路加密技术(HTTPS),完全放弃了签名的方式,认证服务器再也不返回什么 token secret 的密钥了,所以,OAuth 2.0 是完全不同于 1.0 的,也是不兼容的。目前,Facebook 的 Graph API 只支持 OAuth 2.0协议,Google 和 Microsoft Azure 也支持Auth 2.0,国内的微信和支付宝也支持使用 OAuth 2.0。
下面,我们来重点看一下 OAuth 2.0 的两个主要的 Flow:
Authorization Code Flow
Authorization Code 是最常使用的 OAuth 2.0 的授权许可类型,它适用于用户给第三方应用授权访问自己信息的场景。这个 Flow 也是 OAuth 2.0 四个 Flow 中我个人觉得最完整的一个 Flow,其流程图如下所示。
下面是对这个流程的一个细节上的解释:
1)当用户(Resource Owner)访问第三方应用(Client)的时候,第三方应用会把用户带到认证服务器(Authorization Server)上去,主要请求的是 /authorize API,其中的请求方式如下所示。
- https://login.authorization-server.com/authorize?
- client_id=6731de76-14a6-49ae-97bc-6eba6914391e
- &response_type=code
- &redirect_uri=http%3A%2F%2Fexample-client.com%2Fcallback%2F
- &scope=read
- &state=xcoiv98CoolShell3kch
其中:
2)当 Authorization Server 收到这个 URL 请求后,其会通过 client_id 来检查 redirect_uri 和 scope 是否合法,如果合法,则弹出一个页面,让用户授权(如果用户没有登录,则先让用户登录,登录完成后,出现授权访问页面)。
3)当用户授权同意访问以后,Authorization Server 会跳转回 Client ,并以其中加入一个 Authorization Code。如下所示:
- https://example-client.com/callback?
- code=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG
- &state=xcoiv98CoolShell3kch
我们可以看到,
4)接下来,Client 就可以使用 Authorization Code 获得 Access Token。其需要向 Authorization Server 发出如下请求。
- POST /oauth/token HTTP/1.1
- Host: authorization-server.com
- code=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG
- &grant_type=code
- &redirect_uri=https%3A%2F%2Fexample-client.com%2Fcallback%2F
- &client_id=6731de76-14a6-49ae-97bc-6eba6914391e
- &client_secret=JqQX2PNo9bpM0uEihUPzyrh
5)如果没什么问题,Authorization 会返回如下信息。
- {
- "access_token": "iJKV1QiLCJhbGciOiJSUzI1NiI",
- "refresh_token": "1KaPlrEqdFSBzjqfTGAMxZGU",
- "token_type": "bearer",
- "expires": 3600,
- "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciO.eyJhdWQiOiIyZDRkM..."
- }
其中,
6)接下来就是用 Access Token 请求用户的资源了。
- GET /v1/user/pictures
- Host: https://example.resource.com
- Authorization: Bearer iJKV1QiLCJhbGciOiJSUzI1NiI
Client Credential Flow
Client Credential 是一个简化版的 API 认证,主要是用于认证服务器到服务器的调用,也就是没有用户参与的的认证流程。下面是相关的流程图。
这个过程非常简单,本质上就是 Client 用自己的 client_id 和 client_secret 向Authorization Server 要一个 Access Token,然后使用 Access Token 访问相关的资源。
请求示例
- POST /token HTTP/1.1
- Host: server.example.com
- Content-Type: application/x-www-form-urlencoded
- grant_type=client_credentials
- &client_id=czZCaGRSa3F0Mzpn
- &client_secret=7Fjfp0ZBr1KtDRbnfVdmIw
返回示例
- {
- "access_token":"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
- "token_type":"bearer",
- "expires_in":3600,
- "refresh_token":"IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk",
- "scope":"create"
- }
这里,容我多扯一句,微信公从平台的开发文档中,使用了 OAuth 2.0 的 Client Credentials 的方式(参看文档“微信公众号获取 access token”),我截了个图如下所谓。我们可以看到,微信公众号使用的是 GET 方式的请求,把 AppID 和 AppSecret 放在了 URL中,虽然这也符合 OAuth 2.0,但是并不好,因为大多数网关代理会把整个 URI 请求记到日志中。我们只要脑补一下腾讯的网关的 Access Log,里面的日志一定会有很多的各个用户的AppID 和 AppSecret……
小结
讲了这么多,我们来小结一下(下面的小结可能会有点散)
两个概念和三个术语
明白一些初衷
相关的注意事项
当前文章:前后端分离开发,HTTPAPI认证授权术
当前链接:http://www.mswzjz.com/qtweb/news38/178038.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联