https://coolshell.cn/articles/19395.html
HTTP Basic
就是使用 username和 password 来进行登录
技术原理
1 拼接username和 password, username:password(用冒号分隔)
2 Base64编码, Base64("username:password")
3 放到HTTP头中, Authorization: Basic aGFvZW86Y29vbHNoZWxsCg
4 服务端验证
但是Base64不是加密协议,而是编码协议,所以就算是有HTTPS作为安全保护,给人的感觉还是不放心
Digest Access HTTP 摘要认证
原理
-
1 调用方
发起一个普通的HTTP请求 -
2 服务端返回错误, 且在HTTP头里的 WWW-Authenticate 包含如下信息
WWW-Authenticate: Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41" nonce 为服务器端生成的随机数 -
3 客户端做加密
HASH1=MD5(MD5(username:realm:password):nonce:cnonce)cnonce 客户端生成的随机数
-
4 如果
qop中包含了auth
做 HASH2=MD5(method:digestURI)
method 就是HTTP的请求方法(GET/POST…),digestURI 是请求的URL
- 5 如果
qop中包含了auth-init
做 HASH2=MD5(method:digestURI:MD5(entityBody))
entityBody 就是HTTP请求的整个数据体
- 6 得到 response
` response = MD5(HASH1:nonce:nonceCount:cnonce:qop:HASH2)`
没有 qop则 response = MD5(HA1:nonce:HA2)
-
7 客户端向服务端请求
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"
App Secret Key + HMAC 用于给消息签名的技术
保证消息在传递的过程中没有被人修改, 对消息进行一个MAC算法,得到一个摘要字串,然后,接收方得到消息后,进行同样的计算,然后比较这个MAC字符串

-
把
HTTP的请求(方法、URI、查询字串、头、签名头,body)打个包叫CanonicalRequest,作个SHA-256的签名,然后再做一个base16的编码 -
把上面的这个签名和签名算法 AWS4-HMAC-SHA256、时间戳、Scop,再打一个包,叫
StringToSign。 -
准备签名,用
AWSSecretAccessKey来对日期签一个DataKey,再用 DataKey 对要操作的Region签一个DataRegionKey,再对相关的服务签一个DataRegionServiceKey,最后得到 SigningKey. -
用第三步的
SigningKey来对第二步的StringToSign签名。

-
HTTP Request
Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7AKIDEXAMPLE 是 AWS Access Key ID
服务器端会根据这个AppID来查相关的 Secret Access Key,然后再验证签名
JWT – JSON Web Tokens
-
用户使用
用户名和口令到认证服务器上请求认证 -
服务器端生成
JWT Token认证服务器还会生成一个 Secret Key(密钥)
对JWT Header和 JWT Payload分别求Base64。在Payload可能包括了用户的抽象ID和的过期时间。
用密钥对JWT签名 HMAC-SHA256(SecertKey, Base64UrlEncode(JWT-Header)+’.’+Base64UrlEncode(JWT-Payload))
-
把
base64(header).base64(payload).signature作为JWT token返回客户端 -
客户端使用
JWT Token向应用服务器发送相关的请求。
这个JWT Token就像一个临时用户权证一样
服务器收到请求后
检查 JWT Token,确认签名
认证服务器有这个用户的Secret Key(密钥),所以,应用服务器得把JWT Token传给认证服务器。
认证服务器通过JWT Payload 解出用户的抽象ID,然后通过抽象ID查到登录时生成的Secret Key,然后再来检查一下签名。
认证服务器检查通过后,应用服务就可以认为这是合法请求了
对称加密, 加密解密都是认证服务器(私钥)
非对称加密, 加密认证服务器(私钥) 解密应用服务器(公钥)
OAuth 1.0 委托授权协议
例子
(User 照片所有者-用户) 使用一个Consumer(第三方照片打印服务)来打印他在Service Provider(照片存储服务)某网站上的照片,
用户不想把自己的用户名和口令交给那个第三方的网络打印服务,又想让那个第三方的网络打印服务来访问自己的照片
1 [Consumer] 获取 Request Token
2 [Service Provider] 认证用户, 并授权Consumer
3 [Consumer] 获取 Access Token, 调用API访问用户的照片
详细流程
User,Consumer 和 Service Provide,又叫 3-legged flow,三脚流程
-
Consumer(第三方照片打印服务)需要先上Service Provider获得开发的Consumer Key和Consumer Secret(申请有效的开发者信息)
-
当
User访问Consumer时,Consumer向Service Provide发起请求请求Request Token(需要对HTTP请求签名)用户授权第三方平台开始
-
Service Provide验明Consumer是注册过的第三方服务商后,返回Request Token(oauth_token)和Request Token Secret (oauth_token_secret)Service Provider验证第三方开发者信息的有效性
-
Consumer收到Request Token后,使用HTTP GET 请求把 User切到 Service Provide 的认证页上(其中带上Request Token),让用户输入他的用户和口令。跳转 Service Provider 的用户登录认证
-
Service Provider认证User成功后,跳回Consumer,并返回Request Token (oauth_token)和Verification Code(oauth_verifier)回调, 获取 Verification Code
-
接下来就是
签名请求,用Request Token和Verification Code换取Access Token (oauth_token)和Access Token Secret (oauth_token_secret)请求 access_token
-
最后使用
Access Token访问用户授权访问资源

OAuth 2.0
Digest Access,AppID+HMAC,JWT,再到OAuth 1.0,这些API认证都是要向Client发一个密钥(或是用密码)然后用HASH或是RSA来签HTTP的请求, 安全签名机制
OAuth 2.0 完全不同于1.0, 依赖于TLS/SSL的链路加密技术(HTTPS) 放弃了签名的方式
Authorization Code Flow (3 legged flow)
最常使用的OAuth 2.0的授权许可类型

流程:
-
用户(Resource Owner)访问第三方应用(Client)的时候,第三方应用会把用户带到认证服务器(Authorization Server)上去 , 请求的是/authorize APIhttps://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 client_id 为第三方应用的App ID response_type=code 为告诉认证服务器,我要走Authorization Code Flow。 redirect_uri 意思是我跳转回第三方应用的URL scope 意是相关的权限 state 是一个随机的字符串,主要用于防CSRF攻击。 -
Authorization Server收到这个URL请求后,检查client_id,redirect_uri和scope是否合法
如果合法,则弹出一用户认证个页面,让用户授权(如果用户没有登录,则先让用户登录,登录完成后,出现授权访问页面)。
认证 + 授权
-
用户授权同意访问以后,Authorization Server会跳转回Client,并以其中加入一个Authorization Code添加 auth code, 回调 (redirect_uri) client第三方应用 https://example-client.com/callback? code=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG &state=xcoiv98CoolShell3kch -
Client就使用Authorization Code获得Access Token获取token 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 -
获得
token 信息{ "access_token": "iJKV1QiLCJhbGciOiJSUzI1NiI", "refresh_token": "1KaPlrEqdFSBzjqfTGAMxZGU", "token_type": "bearer", "expires": 3600, "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciO.eyJhdWQiOiIyZDRkM..." } access_token 就是访问请求令牌了 refresh_token 用于刷新 access_token id_token 是JWT的token,其中一般会包含用户的OpenID -
请求资源数据
Client Credential Flow (2 legged flow)
简化版的API认证, 认证服务器到服务器的调用, 没有用户参与

流程
-
Client用自己的client_id和client_secret向Authorization Server要一个Access TokenPOST /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访问相关的资源{ "access_token":"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3", "token_type":"bearer", "expires_in":3600, "refresh_token":"IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk", "scope":"create" }
注意 tips
-
编码Base64Encode、签名HMAC、加密RSA编码 为了更的传输,等同于明文,
签名 为了信息不能被篡改,
加密 为了不让别人看到是什么信息
-
应该遵循HTTP的规范,把
认证信息放在Authorization HTTP头中 -
不要使用
GET的方式在URL中放入secret之类的东西,因为很多proxy或gateway的软件会把整个URL记在Access Log文件中 -
认证授权服务器
(Authorization Server)和应用服务器(App Server)最好分开 -
密钥Secret相当于Password,用来加密的,最好不要在网络上传输,如果要传输,最好使用TLS/SSL的安全链路