聊一聊数据接口的登录态校验以及JWT

原创
2019/12/02 23:40
阅读数 5.5K

最近和群里网友聊天,发现他在数据接口中校验登录状态用的还是session,在我及时劝说和科普之后,他最终决定改用JWT。那么接下来我们就聊一聊数据接口应该怎么管理登录状态以及什么是JWT

混合开发的时候是怎么做的

前后端混合开发的时候,用户登录状态的管理一般都是通过session来实现的,原理很简单:用户登录后,服务端将登录用户信息存储到服务器上的特定位置,并生成对应的session id存储到浏览器的cookie中。需要校验的时候先读取cookie中的session id,找到服务器中对应的存储内容,完成校验。

很显然,这个机制是建立在cookie基础上的,cookie又依赖于浏览器,而且有域名限制。是不适合app、小程序、以及前后端时数据接口采用其他域名等情况的。

app、小程序、前后端分离的时候要怎么做

app、小程序、前后端分离时的数据接口一般采用token来做登录信息校验。原理是用户登录后,服务端生成对应用户的一个token(一般都是一段无意义的唯一字符串)后返回,app、小程序、前端(以下统称为前端)拿到token后保存,在需要校验用户登录的接口请求中加入token(可以是get、post参数或者http header的形式),服务端拿到token后校验真实性、有效性等信息后完成登录校验。一般为了防止盗用,还会设置一套签名校验的过程。

其实token和session的原理是差不多的,都是服务端将对应用户的一个key(session的时候是session id,token的时候就是token)交给前端,前端通过token请求服务端,服务端再去反查用户,获取用户登录状态。

现在一般微信、微博等接口都是采用的这种方式。但是这种方式也有弊端,主要是:

  1. 服务端必须保存token,以及有效期,校验的时候必须要有数据读取的过程;
  2. 校验签名的时候一般需要一个secret做为加密签名的附加字符,前端必须也要同时保存这个secret,这样显然不适合代码会暴露的网页前端。

这时候,就轮到我们这次的主角JWT出场了。

什么是JWT

JWT是JSON Web Token的简称,有官网详细介绍,大家可以看一看,这里简单说一下。

JWT其实就是一种特殊的token,原理和使用方法自然和token一样。

JWT是由三部分组成的字符串,结构是:头部+主体内容(官方称之为Payload)+签名,三部分用“.”连接。头部和主体内容都是json格式的字符串再经过base64编码,为了方便放在get请求中,还需要把类似“=”、“/”等特殊字符替换掉。

头部内容是固定的,原始json就是下面这样

{
	"alg": "HS256",
	"typ": "JWT"
}

主要是说明了最后签名部分的加密算法。

重点是中间的主体内容,原始json一般是类似下面这样的

{
	"user": "John Doe",
	"exp": "2020-01-01 12:24:30"
}

主体内容一个是当前登录的用户,可以是用户id,也可以是用户名等可以检索定位到用户的信息;还有一个就是过期时间。还可以加入一些其他不私密的信息。

服务端拿到JWT之后可以在不读取数据的情况下,仅通过解码这部分信息就可以完成获取登录用户以及判断是否过期等初期工作。

最后的签名一般是把头部、主体内容再加上secret拼接成字符串再加密,这一步在用户登录生成JWT的时候就完成了。服务端拿到JWT之后只需要把前两部分加上secret再计算一次签名加以比对就可以完成校验签名,前端不需要同时保存secret。

JWT官网提供了各种服务端语言的生成代码,这里我提供一个我自己用PHP写的相对简化的方法,供大家参考

private function _jwt($payload){
	$header['alg']='HS256';
	$header['typ']='JWT';
	$jwt_header=$this->_base64url($header);
	$jwt_payload=$this->_base64url($payload);
	$jwt_sign=hash_hmac('sha256', $jwt_header.'.'.$jwt_payload, $this->secret);
	$jwt['token']=$jwt_header.'.'.$jwt_payload.'.'.$jwt_sign;
	$jwt['sign']=$jwt_sign;
	return $jwt;
}

private function _base64url($a){
	$c=base64_encode(json_encode($a));
	$c=str_replace('=', '', $c);
	$c=str_replace('+', '-', $c);
	$c=str_replace('/', '_', $c);
	return $c;
}

我这个方法里需要把主题内容以数组形式的参数传入,最终返回了生成的JWT和签名,方便接收时校验签名。

最后再说一下缺点

JWT在实际使用中也是存在问题的,目前想到以下几点:

  1. 安全性:签名包含在token中,一旦token整体被盗用,将没有办法区分,所以有网友称之为“裸奔”;
  2. 过期时间放在token中而不是服务端保存处理,一旦token生成并签发出去,将无法灵活的控制有效期;
  3. 最后一个是用户体验,其实可以算是token方式的通病。这个问题我也和群友讨论过,大家在访问部分社区的时候应该会遇到过,还在访问过程中突然变成未登录。我觉得这主要时因为服务端在token过期后就即时判断为用户登录失败,不管你在网页上处于什么状态。这个问题在session方式中是不存在的,前面说过session依赖于cookie,而存储session id的cookie是会保存在整个浏览器进程,就是说只要浏览器不关闭,用户就可以一直保持登录状态。
展开阅读全文
JWt
打赏
3
23 收藏
分享
加载中
现在一般微信、微博等接口都是采用的这种方式。但是这种方式也有弊端,主要是:

服务端必须保存token,以及有效期,校验的时候必须要有数据读取的过程;
校验签名的时候一般需要一个secret做为加密签名的附加字符,前端必须也要同时保存这个secret,这样显然不适合代码会暴露的网页前端。
这两个原因太牵强了。
第一点数据读取的过程,也算弊端?多大的弊端?登录查下库,看看这个身份分是否合法也叫弊端?
第二点前端也要保存secret?听谁说要保存?用户登录的时候后端发一个token,一个sig给前端,前端每次请求的时候带着这个去。跟secret有什么关系?
jwt,最应该质疑的就是过期时间,携带的base64编码数据也类似明文。发放的时候把过期时间,一些明文信息都暴露出去了,这是哪门子设计?就想着一劳永逸,不想查库?查一次库这么难?
03/27 14:25
回复
举报
第一点,对于你来说,可能觉得查一次库没什么,没什么好讨论的,我不强求你觉得查一次库很难,你也没必要觉得别人查一次库很难有什么不对 第二点,微信也好、微博也好,你做他们服务端接口开发不要保存secret吗?当然你自己用服务端语言做个中转是不会把secret暴露出来,本文说的是纯前端的情况 最后说下你说的明文信息的问题,jwt确实是会暴露一些明文信息,但是也不是说要你把所有信息都放明文,放到明文里的都是可以暴露的非必要信息,你自己非要放一些不能暴露的信息怎么能怪jwt呢?
03/30 11:20
回复
举报
刷新本页Ctrl+F输入 “相关文章”能找到一个列表,读读别人的文章,多了解了解应用场景。
04/09 19:07
回复
举报
如果你觉得我哪里说得不对,可以指出来,我们继续讨论。我不是什么大牛,所有的东西肯定都是在看过各种前辈牛人的文章基础上,再加上自己实际应用后才整理出来的。不是凭空捏造的。如果你只是想为人师,动动嘴皮子教育他人多看书,那你可以移步首页动弹,谢谢,不送
04/17 10:50
回复
举报
我的refresh方案是 在请求头传入token 然后校验token 如果在允许时间范围内 兑换一个新的token 放到response的header中。
2019/12/16 13:02
回复
举报
不知道你这个是解决了什么问题。我的建议是,既然到期了就让他重新登录,而不是一直继续用refresh去换新的token。
2019/12/16 14:45
回复
举报
jwt 像HTTP无状态一样,只需要验证 载荷解析处理而不需要存储,也有的文章说不建议使用jwt说安全性不可靠,毕竟通过bash64可以解析出来,但是我个人觉得jwt还是一个实用性非常高的技术
2019/12/04 08:34
回复
举报
你说的缺点一个refresh token都解决了
2019/12/03 20:15
回复
举报
服务端调接口的时候,可以用refresh token,但是小程序、网页等前端就不适合了 还有不知道你这个“都”是指哪几个缺点,我列出的全部?不行吧
2019/12/03 23:10
回复
举报
安全性,只要是web应用就有这个问题,说的是后边两个
2019/12/04 09:07
回复
举报
麻烦分享一下解决方案,谢谢
2019/12/04 13:24
回复
举报
是的,refresh token都可以解决
2019/12/04 10:09
回复
举报
麻烦分享一下解决方案,谢谢
2019/12/04 13:25
回复
举报
refresh token,就是续签吧。 session是自动续签,永远以最后一次访问的时间做为起始计时时间,refresh token则是间断续签。 session能剔账户下线,jwt无法。 各有优劣吧。
2019/12/04 14:30
回复
举报
一般token有效期短 refresh有效期长 这样就能踢了 只不过有个延迟,需要特殊处理的 应该会在程序里直接屏蔽账户,
2019/12/04 19:47
回复
举报
有效期长短和能不能踢好像没有关系啊
2019/12/04 23:26
回复
举报
更多评论
打赏
16 评论
23 收藏
3
分享
返回顶部
顶部