基于Token的认证
通过上一篇你大体已经了解session和cookie认证了,session认证需要服务端做大量的工作来保证session信息的一致性以及session的存储,所以现代的web应用在认证的解决方案上更倾向于客户端方向,cookie认证是基于客户端方式的,但是cookie缺点也很明显,到底有哪些缺点可以跳转上一次的文章。那有没有一种比较折中的方案呢?有的
把认证信息保存在客户端,关键点就是安全的验证,如果能解决认证信息的安全性问题,完全可以把认证信息保存在客户端,服务端完全无认证状态,这样的话服务端扩展起来要方便很多。关于信息的安全解决方案,现在普遍的做法就是签名机制,像微信公众接口的验证方式就基于签名机制。
签名,就是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。
当用户成功登陆系统并成功验证有效之后,服务器会利用某种机制产生一个token字符串,这个token中可以包含很多信息,例如来源IP,过期时间,用户信息等, 把这个字符串下发给客户端,客户端在之后的每次请求中都携带着这个token,携带方式其实很自由,无论是cookie方式还是其他方式都可以,但是必须和服务端协商一致才可以。当然这里我不推荐cookie。当服务端收到请求,取出token进行验证(可以验证来源ip,过期时间等信息),如果合法则允许进行操作。
基于token的验证方式也是现代互联网普通使用的认证方式,那它有什么优点吗?
1. 支持跨域访问,Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输.
2. 无状态:Token机制在服务端不需要存储session信息,因为Token自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息.
3. 解耦 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你可以进行Token生成调用即可.
4. 适用性更广:只要是支持http协议的客户端,就可以使用token认证。
5. 服务端只需要验证token的安全,不必再去获取登录用户信息,因为用户的登录信息已经在token信息中。
6. 基于标准化:你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在多个后端库(.NET, Ruby, Java,Python,PHP)和多家公司的支持(如:Firebase,Google, Microsoft).
那基于token的认证方式有哪些缺点呢?
1. 网络传输的数据量增大:由于token中存储了大量的用户和安全相关的信息,所以比单纯的cookie信息要大很多,传输过程中需要消耗更多流量,占用更多带宽,
2. 和所有的客户端认证方式一样,如果想要在服务端控制token的注销有难度,而且也很难解决客户端的劫持问题。
3. 由于token信息在服务端增加了一次验证数据完整性的操作,所以比session的认证方式增加了cpu的开销。
但是整体来看,基于token的认证方式还是比session和cookie方式要有很大优势。在所知的token认证中,jwt是一种优秀的解决方案
jwtJSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。
一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。
头部
header典型的由两部分组成:token的类型(“JWT”)和算法名称(比如:HMAC SHA256或者RSA等等)。
{
"alg": "HS256",
"typ": "JWT"
}
Payload
Payload 部分也是一个JSON对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
除了以上字段之外,你完全可以添加自己想要的任何字段,这里还是提醒一下,由于jwt的标准,信息是不加密的,所以一些敏感信息最好不要添加到json里面
{
"Name":"菜菜",
"Age":18
}
Signature
为了得到签名部分,你必须有编码过的header、编码过的payload、一个秘钥(这个秘钥只有服务端知道),签名算法是header中指定的那个,然对它们签名即可。
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。需要提醒一下:base64是一种编码方式,并非加密方式。
写在最后基于token的认证方式,大体流程为:
1. 客户端携带用户的登录凭证(一般为用户名密码)提交请求
2. 服务端收到登录请求,验证凭证正确性,如果正确则按照协议规定生成token信息,经过签名并返回给客户端
3. 客户端收到token信息,可以保存在cookie或者其他地方,以后每次请求的时候都携带上token信息
4. 业务服务器收到请求,验证token的正确性,如果正确则进行下一步操作
这里再重复一次,无论是token认证,cookie认证,还是session认证,一旦别人拿到客户端的标识,还是可以伪造操作。所以采用任何一种认证方式的时候请考虑加入来源ip或者白名单,过期时间,另外有条件的情况下一定要使用https。