Http Basic Auth 是每次请求API时都提供用户的username和password,简言之, Basic Auth 是配合Restful API使用的最简单的认证方式,只需要提供用户名和密码即可。但由于有把用户名和密码暴露给第三方客户端的风险,在生产环境下被使用的越来越少。因此,在开发对外开放的Restful API时,应尽量避免采用 Http Basic Auth认证方式。
Cookie认证机制是为一次请求认证在服务端创建一个Session对象,同时在客户端的浏览器端创建了一个Cookie对象,通过客户端携带的Cookie对象与服务器端的Session对象匹配来实现状态管理。默认情况下,关闭浏览器时Cookie会被删除,可以通过设置Cookie的超时时间使Cookie在一定时间内有效。
OAuth(开放授权,Open Authorization)是一个开放的授权标准,为用户资源的授权提供了一个安全、开放而又简易的标准。与以往的授权方式不同之处是OAuth的授权不会使第三方触及到用户的账号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此OAuth是安全的。
OAuth 在 "客户端" 与 "服务提供商" 之间,设置了一个授权层(authorization layer)。"客户端" 不能直接登录 "服务提供商",只能登录授权层,以此将用户与客户端区分开来。"客户端" 登录授权层所用的令牌(token),与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期。"客户端" 登录授权层以后,"服务提供商" 根据令牌的权限范围和有效期,向 "客户端" 开放用户储存的资料。
这种基于OAuth的认证机制是用于个人消费类的互联网产品,如社交类、商超类App等应用,但不太适合拥有自有认证权限管理的企业应用
使用基于Token的身份验证方法,在服务端不需要存储用户的登陆信息。流程如下:
客户端使用用户名和密码请求登陆。
服务端收到请求,去验证用户名和密码。
验证成功后,服务端会签发一个Token,再把这个Token发送给客户端。
客户端收到Token以后可以把它存储在Cookie本地。
客户端每次向服务端请求资源时需要携带Cookie中该Token。
服务端收到请求后,验证客户端携带的Token,如果验证成功则返回数据。
Token认证方式比Http Basic Auth安全,比Cookie Auth更节约服务器资源,比OAuth更加轻量。Token Auth具体有以下优点:
支持跨域访问:Cookie是不允许跨域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过Http头传输
无状态(服务端可扩展行):Token机制在服务端不需要存储session信息,因为Token自身包含了登陆用户的部分信息,只需要在客户端的cookie或本地介质存储状态信息。
更适用CDN:可以通过内容分发网络请求服务端的所有资料(如js,html,图片等),而服务端只需要提供API即可。
去耦合:不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在API被调用时,生成Token即可。
更适用于移动应用:当移动设备不支持Cookie验证时,采用Token验证即可。
CSRF:因为不再依赖于Cookie,就不需要考虑对CSRF(跨站请求伪造)的防范。
性能:一次网络往返时间(通过数据库查询Session信息)总比做一次SHA256计算的Token验证和解析要费事的多。
基于标准化:创建的API可以采用标准话的 JSON Web Token(JWT)。这个标准已经存在多个后端库( .net,Ruby,Java,Python,Php )和多家公司的支持(Firebase,Google,Microsoft)。
JSON Web Token (JWT)是一个开放的行业标准(RFC 7519),它定义了一种简洁的、自包含的协议格式,用于在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任。JWT可以使用HMAC算法或使用RSA的公钥/私钥对进行签名,防止被篡改。
JWT官网: https://jwt.io
JWT令牌的优点:
JWT基于json,非常方便解析。
可以在令牌中自定义丰富的内容,易扩展。
通过非对称加密算法及数字签名技术,JWT防止篡改,安全性高。
资源服务使用JWT可不依赖认证服务即完成授权。
JWT令牌的缺点:
JWT令牌较长,占存储空间比较大。
一个JWT实际上就一个字符串,它由三部分组成,头部、负载与签名。
头部用于描述关于该JWT的最基本信息,例如其类型(即JWT)以及签名所用的算法(如HMAC SHA256 或 RSA)等。这也可以被表示成一个JSON对象。
- {
- "alg":"HS256",
- "typ":"JWT"
- }
alg:签名算法
typ:类型
我们对头部的json字符串进行BASE64编码,编码后的字符串如下:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Base64是一种基于64个可打印字符串来表示二进制数据的表示方式。JDK提供了非常方便的Base64Encoder和Base64Decoder,用它们可以非常方便的完成基于Base64的编码和解码。
负载,是存放有效信息的地方,比如用户的基本信息可以存在该部分中。负载包含三个部分:
标准中注册的生命(建议但不强制使用)
iss::jwt签发者
sub:jwt所面向的用户
aud:接收jwt的一方
exp:jwt的过期时间,过期时间必须大于签发时间
nbf:定义在什么时间之前,该jwt都是不可用的
iat:jwt的签发时间
jti:jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
公共的声明
公共的声明可以添加任何信息,一般添加用户的相关信息或其他业务需要的必要信息,但不建议添加敏感信息,因为该部分在客户端可解密。
私有的声明
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
私有声明也就是自定义claim,用于存放自定义键值对。
- {
- "sub": "1234567890",
- "name": "John Doe",
- "iat": 1516239022
- }
其中sub是标准的声明,name是自定义的私有声明,编码后如下:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
jwt的第三部分是一个签证信息,由三部分组成:
Header(Base64编码后)
Payload(Base64编码后)
Secret(盐,必须保密)
这个部分需要Base64加密后的header和base4加密后的payload使用.连接组成的字符串,然后通过header重声明的加密方式进行加盐Secret组合加密,然后就构成了JWT的第三部分——使用“qfjava”作为盐:
eZqdTo1mRMB-o7co1oAiTvNvumfCkt-1H-CdfNm78Cw
从官方工具中可以看到,三个部分组合出的完整字符串:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF