当前位置: 首页 > 后端技术 > Java

面试官:小伙子,你可以学学单点登录

时间:2023-04-01 19:07:07 Java

我试着用八幅漫画让大家明白如何设计一个普通的用户认证系统,然后再扩展到单点登录系统。JWT简介JSONWebToken(JWT)是一个非常轻量级的规范。该规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。让我们想象一个场景。当用户A关注用户B时,系统会向用户B发送一封邮件,其中包含“点击这里关注用户A”的链接。链接的地址可以是这样的https://your.awesome-app.com/make-friend/?from_user=B&target_user=A上面的URL主要是通过URL来描述这个。当然,这样做有一个缺点,就是要求用户B用户必须先登录。能不能把这个过程简化一下,让用户B不用登录就可以完成这个操作。JWT让我们可以做到这一点。JWT的组成一个JWT其实就是一个字符串,它由三部分组成:header、payload和signature。载荷(Payload)先把上面添加好友的操作描述成一个JSON对象。添加一些其他信息,以帮助将来接收此JWT的服务器了解此JWT。{"iss":"JohnWuJWT","iat":1441593502,"exp":1441594722,"aud":"www.example.com","sub":"jrocket@example.com","from_user":"B","target_user":"A"}这里的前五个字段是JWT标准定义的。iss:JWT的发行者sub:JWT的用户aud:接收JWT的一方exp(expires):当它过期时,这是一个Unix时间戳iat(issuedat):可以找到这些定义的发布时间在标准中。[Base64编码]上面的JSON对象可以得到如下字符串。我们将此字符串称为JWT的有效负载。eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjoiQSJ9如果你使用Node.js,可以用Node.js的包base64url来得到这个字符串。varbase64url=require('base64url')varheader={"from_user":"B","target_user":"A"}console.log(base64url(JSON.stringify(header)))//输出:eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjoiQSJ9头部(Header)JWTalsoneedsaheader,whichisusedtodescribethemostbasicinformationabouttheJWT,suchasitstypeandthe用于签名的算法。这也可以表示为JSON对象。{"typ":"JWT","alg":"HS256"}在这里,我们表示这是一个JWT,我们使用的签名算法(后面会提到)是HS256算法。也对其进行base64编码,后面的字符串就成为了JWT的Header(头部)。eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9签名(签名)将上面的两个编码后的字符串都用句号.连接在一起(头部在前),就形成了eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0这一部分的过程在node-jws的源码中有体现最后,Weencryptthestringspliced??abovewiththeHS256algorithm.Whenencrypting,wealsoneedtoprovideakey(secret).Ifweusemystarasthekey,thenwecangetourencryptedcontentrSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM,whichisalsocalledsignature.最后将这一部分签名也拼接在被签名的字符串后面,我们就得到了完整的JWTeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0.rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM于是,我们就可以将邮件中的URL改成https://your.awesome-app.com/make-friend/?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0.rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViMisnowsafetoaddfriends!Theso-calleduserauthentication(Authentication)isamechanismthatallowsuserstologin,andallowsuserstousetheiraccountswhenvisitingwebsitesinthenextperiodoftimewithouthavingtologinagain.Littleknowledge:Don'tconfuseuserauthenticationwithuserauthorization(Authorization).Userauthorizationreferstospecifyingandallowinguserstousetheirownpermissions,suchaspublishingposts,managingsites,andsoon.First,theserverapplication(hereinafterreferredtoas"application")allowsuserstosendtheirusernamesandpasswordstotheinterfaceoftheserverthroughawebform.ThisprocessisgenerallyanHTTPPOSTrequest.TherecommendedwayistouseSSLencryptedtransmission(httpsprotocol)toavoidsniffingofsensitiveinformation.接下来,应用程序和数据库检查用户名和密码。成功验证用户名和密码后,应用程序将用户的id(图中的user\_id)作为JWTPayload的属性,与header进行Base64编码拼接,然后进行签名,形成JWT。这里的JWT是类似于lll.zzz.xxx的字符串。应用程序将JWT字符串作为请求cookie的一部分返回给用户。注意这里必须使用HttpOnly属性,防止cookies被JavaScript读取,从而避免跨站脚本攻击(XSS攻击)。在cookie过期或被删除之前,用户每次访问应用程序时,应用程序都会收到包含jwt的cookie。然后应用程序可以从请求中提取JWT。应用程序通过一系列任务检查JWT的有效性。比如检查签名是否正确;检查Token是否过期;检查令牌的接收者是否是您自己(可选)。应用程序确认JWT有效后,JWT进行Base64解码(可能在上一步已经完成),然后读取Payload中用户的id值,即user\_id属性。这里用户的id为1025,应用程序从数据库中取出id为1025的用户的信息,加载到内存中,进行ORM等一系列底层逻辑初始化。应用程序响应用户请求。单点登录会话方法用于存储用户标识。一开始,用户的session只会保存在一台服务器上。对于有多个子域的站点,每个子域至少对应一个不同的服务器,例如:www.taobao.comnv.taobao.comnz.taobao.comlogin.taobao.com所以如果你想在login.taobao.com登录后也就是说,在其他子域名下仍然可以获取到Session,这就需要我们在多台服务器上同步Session。使用JWT的方式就不存在这个问题,因为用户的状态已经传到客户端了。因此,我们只需要将包含JWT的cookie的域名设置为顶级域名即可,例如Set-Cookie:jwt=lll.zzz.xxx;仅限HTTP;最大年龄=980000;domain=.taobao.com注意domain一定要加上一个.taobao.com的顶级域名。这样taobao.com和*.taobao.com都可以接受这个cookie并获得JWT。作者:JohnWublog.leapoahead.com/2015/09/07/user-authentication-with-jwt/