1.登录凭证

面试官若问登录凭证一般如何设计,不要只说用token。登录的本质是服务端识别用户身份并证明其已登录。无论使用Session 、Redis 还是JWT ,核心都是登录凭证 。用户登录成功后,服务端会发给客户端一个凭证,后续每次请求客户端都带上该凭证,服务端根据凭证判断用户是否已登录及具体身份。常见方案有三种:Session+Cookie 、Redis Token 、JWT。
第一种:Session+Cookie的方式。
cookie存储在浏览器,session存储在服务器。用户登录成功后,服务端会创建一个session,将用户信息存入,并将session id写入cookie,返回给浏览器。之后浏览器每次请求都会自动带上cookie,服务端拿到cookie中的session id,在自己的session中查找用户信息,若查到则说明已登录,否则说明未登录或登录已过期。
验证码登录类似,服务端生成验证码存入session,浏览器提交手机号和验证码时会带上cookie,服务端根据cookie中的session id找到对应session,再拿里面的验证码做校验,验证码正确则将用户信息写入session。后续用户访问需登录的接口时,拦截器只需检查session中是否有用户信息即可,这是最传统的登录方案。
但session+cookie有明显问题,在多服务器场景下session不共享。比如有三台服务器a、b、c均部署同一套代码,用户第一次登录请求打到a服务器,a服务器有其session,但下一次请求被负载均衡转发到b服务器,b服务器没有该session,会误判用户未登录。
常见解决方案是将Session状态统一放到Redis,即用户登录成功后,不再将用户信息存在某一台机器的session中,而是存入redis,redis是独立的公共存储,所有服务器都能访问。这样无论请求打到哪台服务器,只要拿着登录凭证去redis查,都能查到用户信息。
Redis token流程一般是:用户登录成功后,服务端生成一个随机字符串作为token,以token为key,用户信息为value存入redis,再将token返回给前端。后续前端每次请求都在请求头带上token,服务端拦截器拿到token后去redis查,查到用户信息则说明已登录,查不到则说明未登录或登录已过期。
这里有个面试高频点,token要不要设置过期时间?肯定要。否则用户永不退出,风险极大。
但若设置一小时过期,用户持续操作系统时,一小时后突然被踢下线,体验极差。因此,一般会刷新过期时间,只要用户持续访问系统,服务端可顺手刷新Redis中token的过期时间,这样用户长时间不操作才会过期,持续操作则不会频繁掉线。
接下来看JWT ,JWT与前面两种方案的最大区别是无状态 。什么叫无状态?Session和Redis token本质上都是服务端存储用户登录状态,用户拿凭证过来,服务端需去session或Redis里查。但JWT不同,JWT本身就携带用户信息,一般由三部分组成:Header、Payload、Signature。header表示这是一个JWT,以及用了什么签名算法;payload里面会放一些业务信息,比如用户ID、过期时间;signature是签名,用来防止JWT被篡改。用户登录成功后,服务端生成一个JWT返回给前端,后续前端每次请求带上JWT,服务端只需校验签名、检查过期时间、解析出用户ID,就知道是谁了。它不需要查Redis,也不需要查session,这就是JWT的优势:服务端压力大减,扩展简单,天然适合分布式。
但JWT也有一个大问题,签发出去之后服务端很难主动让它失效。比如,一个JWT有效期是两小时,如果JWT被别人偷走了,只要它还没过期,别人就可以拿着它冒充你访问系统。如果是Redis token,服务端可以直接把Redis里的token删除,但JWT是无状态的,服务端本来就没存它,所以很难直接废掉它。那怎么办?常见有两种补救方案:
第一种是Redis黑名单,比如用户退出登录或发现风险,就把这个JWT加入Redis黑名单,之后每次请求除了校验JWT,还要查一下它是否在黑名单里。但这样一来,每次请求又要查Redis,JWT的无状态优势就被削弱了。
第二种是双token方案 ,也就是access token 加refresh token 。access token有效期短,比如五分钟,用来访问接口;refresh token有效期长,用来刷新access token。平时请求只带access token,只有access token过期时才拿refresh token去换新的access token。这样即使access token泄露,攻击窗口也很短。而refresh token因为传输频率低,泄露概率相对更小,同时,refresh token通常会存在服务如Redis里,比如用户退出登录时,服务端可以删除refresh token,达到控制登录状态的目的。所以最后怎么选?
没有绝对最好的登录方案,只有适合场景的方案。
若传统后台系统安全要求高,需强控制用户登录状态,优先考虑session或redis token。若是前后端分离分布式系统、微服务系统,希望服务端少存状态,可考虑jwt。但若jwt需实现退出登录、踢人、下线、封控、封禁等功能,通常需配合redis黑名单或双token。面试回答时,不要说jwt一定比session高级,更好的回答是**session和redis token有状态,登录服务端掌握用户状态,安全控制更强。jwt无状态,登录扩展性更好,但主动失效能力弱。真正落地时需结合安全性、扩展性、用户体验和系统复杂度来权衡。**这样回答面试官才会觉得真正理解登录体系,而非只会token。
2.Cookie、Session、Token区别
现在请大家回忆第一次使用B站的情形,那时我还不是B站用户,需要注册一个账户。众所周知,注册账户需要用户名和密码,若用户名已被使用,则需想一个新的名字。毕竟每个用户名都代表B站里的唯一ID。注册成功后,我们可以凭借用户名和密码开启B站之旅。
一切看似顺理成章,但大家有没有发现,自从登录一次后,很长时间都不需要再输入用户名和密码了,这是怎么回事?学过HTTP的同学都知道,HTTP是无状态的。也就是说,你这次访问服务器、关闭网页,再次访问服务器时,服务器没有意识到又是你来访问。有同学可能会反驳,说老师肯定在胡说八道,不知道是我访问怎么保持登录状态呢?
这里有好几种技术可以实现,但核心的概念就是存储。在B站注册后,B站会把用户名和密码保存起来,通常保存在数据库里。下次登录时,B站会根据输入的用户名、密码与数据库做对比,对比成功就返回相应的页面。
现在重点来了,如果B站不想让已登录过的用户再次输入账号密码,这里假设一天后需要重新输入账号密码,根据我刚刚说的核心,就得使用到存储了。最简单的就是设置让用户可以选择记住用户名和密码,或者让浏览器记住用户名和密码。如果是你,你愿意把你的密码交给浏览器吗?
如果你的电脑被黑了,那么浏览器里记录的信息其实也很危险 。就算浏览器能够完美保存你的账号密码不被破解,我们还得解决HTTP无状态的问题。因为即使我们自己不需要亲自输入用户名和密码,浏览器还得想办法帮我们在每一次请求里加入用户名密码。这样才能做到保持登录,实现每一个HTTP请求自动带数据给服务器的技术,可能很多同学都知道了,就是cookie。
cookie的基本流程是这样的:浏览器发起HTTP请求,服务器会进行cookie设置,即set-cookie。里有名和值两个重要属性,服务器会把名和值属性里的内容填充完整,cookie发给浏览器后,浏览器会保存起来。这样,浏览器以后发送的每一个请求都会自动附上这个cookie。

cookie就是一种存储在浏览器的数据 ,我们打开浏览器是可以看到保存了哪些cookie的。也就是说,如果把用户名和密码放在cookie里是很不安全的,只要电脑被黑,cookie里的重要信息就会被泄露。于是后来就有了新的概念,即Session ,也就是会话。
既然浏览器和服务器是在进行会话的,浏览器访问服务器是会话的开始,而会话结束的时间择比较模糊,因为你关掉网页也有可能只是按错了而已,并非想结束会话。因此不同网站为每位用户的会话设定了时间和唯一ID,这里的ID即Section id,时间则是结束会话的时间,由服务器自定义,通常保存在数据库中。
这也是大家疑惑的开始,有了用户名,为何还需要section id?我们来回顾一下访问B站的过程。

1.首先,我们使用用户名和密码发送给B站服务器,B站服务器核对后确认用户名和密码正确,身份认证成功,于是在服务器端创建一个session id + 会话结束时间。(这个session id通常是一串无规律的字符串,当然还会创建其他参数。)
2.服务器需要把session id + 会话结束时间 发送给浏览器,这需要通过cookie设置,并将session id放入cookie ,将会话结束时间设置为cookie的有效期。
3.浏览器拿到cookie后进行保存,浏览器保存cookie后,利用cookie的核心特点,即每个请求都会自动发送cookie到相应服务器。(注意此时浏览器未保存用户名和密码,只保存了无规律的字符串session id,cookie中仅有session id,无其他重要信息。此时大家可能会疑惑,如果黑客入侵拿到session id怎么办?黑客拿到这个session id虽不能说毫无意义,但意义已不大。首先,这个session id是无规律的字符串,不能依据session id推断出用户的密码;其次,服务器在发送cookie之前,会对含有session id的cookie进行签名,如果黑客修改了session id,session id就会变成服务器无法识别的字符串。)
换句话说,浏览器的下次访问都会自动发送这个session id给服务器,直到cookie的有效期失效后,浏览器一般就会自行删除这个cookie,这就是会话结束。
4.在cookie失效之后,用户就得重新输入用户名和密码了。
现在用代码来验证我们刚刚说的理论。
1.首先这里创建了一个前端登录页面

2.为了验证用户和保持用户登录状态,后端使用了Node.js。

首先引入了一些库,为了验证session id,这里使用了express-session库,接着就使用常见的中间件。
下面创建了用户名和密码两个变量,因未使用数据库,假设这两个变量是用户注册后保存在数据库中的值;再往下创建了一个session db变量,用于保存即将生成的session。
接下来可以设置服务器的session,secret值为session id,cookie的签名(通常是很长的字符串,并保存为环境变量);name属性是cookie的name,默认为connect.sid;maxAge属性是cookie的有效期。receive、saveUninitialized属性不是重点,有机会再详解。
然后设置主页面,每次用户访问时,将用户发送的session保存到session_DB变量中,如果用户已登录,if条件判断为true,则发送欢迎界面;否则发送登录页面。
如果用户请求中的用户名、密码与服务器匹配,则生成新的session,并为其添加username属性,发送欢迎页面;否则提醒错误信息。
最后设置登出页面,用户登出后清除服务器的session,并重定向到主页面。
演示一下代码的执行过程:
首先,浏览器的cookie信息为空,输入用户名和密码登录后,浏览器发送post请求,将用户名和密码放在http的body中发送给服务器。服务器响应后浏览器收到cookie,name对应服务器Session的name属性值。(第一次发送请求时不附带cookie,因为服务器收到请求才会设置cookie。)

我们在服务器后台可以看到,服务器提示生成了新的cookie对象,并生成了唯一的session id,该值放在cookie的value中。

现在再次登录主页面,可以看到保持登录状态,请求头中会自动添加cookie,后续刷新登录也会自动发送该cookie。

最后点击登出,服务器的session被清除,浏览器的cookie也会随时间流逝而被删除。
随着互联网发展,用户群体增大,若服务器仍使用基于Cookie的Session ,在特定时间有大量用户访问服务器时,服务器可能需存储大量session id,在服务器集群中,一台服务器存储了session id,还需分享给其他服务器,以避免超载时分配用户到其他服务器。其他服务器需通用session id以避免用户再次输入用户名和密码,但服务器分享session id并非长久之计,因此可让Redis数据库存储session id。然而,若数据库崩溃,会影响服务器获取session id。在各种原因和需求下,出现了JWT技术,即Json Web Token。

1.用户首次登录网页后,服务器生成jwt,服务器无需保存jwt本身,只需保存jwt签名的密文
2.服务器响应并将jwt发送给浏览器,浏览器可将其存储于cookie或storage中,假设以cookie形式保存,用户每次发送请求时,都将jwt发送给服务器,无需重新输入账号密码,这与session类似。
token存储在用户端,很多人会疑惑token的安全性,jwt由三部分组成,由点分隔:header、payload和signature。header部分声明了用于生成签名的算法,payload部分包含一些特定数据,如有效期等。

header和payload两部分内容会经base64编码,注意是编码而非加密,即容易解码。虽然jwt不保存在服务器,但服务器需保存一段密码,该密码结合两段编码进行算法运算,最终得到签名信息,使用的算法是header声明的算法,签名信息即signature部分,这样一个完整的jwt可发送给客户端。

来看jwt例子,若修改三部分中的任何一个字符,整个jwt都会出错。因为三部分相关联,因此jwt有一定安全性,但并非绝对安全。
由于刚才Session的代码已让大家有基本代码概念,下面Token代码会更简洁。此次使用postman代替前端和浏览器部分,后端代码与刚才类似,Token代码演示:

1.首先引入需要的库,因为需要创建jwt,此处用jsonwebtoken库做示范。
2.接下来有两个变量还是假设存在于数据库中的值,jwtScret变量就是等会会用于生成签名的字符串。
3.这里有个login路径,当识别到http body中的用户名密码与数据库一致时,会生成一个token变量,使用jwt.sign()生成,需要3个参数:payload、服务器jwt密码和header,然后将token发送给客户端。
4.下面是vip路径,假设用户已登录,可使用token登录vip路径。使用JWT的verified方法验证body中的token是否正确,此处使用指定的服务器密码,即前面创建的jwtScrect变量。若成功,则提示用户。
接下来,我们使用Postman进行验证,首先在body中以JSON格式输入用户名和密码,然后发送POST请求。我们可以看到返回了一串由点分隔的字符串,即token。

我们复制此token,将其放在body中,并再次发送请求至vip路径。此时,我们可以直接获取vip页面的内容,实现了无需用户重复输入用户名和密码的操作。

- Session在服务器上诞生并保存,由服务器主导;
- cookie是一种数据载体,用于将session发送到客户端,Cookie随每个HTTP请求发送。
- Token在服务器上诞生,但保存在浏览器端,由客户端主导,可放在cookie或storage中,持有token就像持有令牌一样,可以访问服务器。
3.一个案例Kiro登录

这是一个IDE账号的卡密,直接导入账号管理器即可完成账号的登录,这个Json字段包括:accessToken、refreshToken、profileArn、expiresAt、authMethod、provider键值对。
这其实就是在第一节讲的**"双Token认证手段",** 这个"卡密"实际上是一个已经通过官方认证成功的 Session 凭证 JSON 对象。商家通过技术手段,把自己设备上登录成功后获取的身份令牌导出来卖给你。下面为你逐一拆解这些字段的含义和具体作用:
🔑 核心凭证字段
1. accessToken(访问令牌)
-
具体作用 :这是你最核心的通行证 。当你在 Kiro 中编写代码、调用 AI 模型时,KiroIDE 每次向服务器发请求,都必须在请求头里带上这串超长的
accessToken。服务器看到它,就知道"哦,这是一个买了 Pro+ 服务的合法用户",然后放行。 -
特点 :它的权限很高,但为了安全,它的有效期非常短(通常只有 1 小时左右)。
2. refreshToken(刷新令牌)
-
具体作用 :专门用来给
accessToken续命 。因为accessToken一个小时就过期了,如果让你每小时重新去浏览器登录一次,体验会极差。有了refreshToken,当 Kiro 发现访问令牌过期时,它会在后台悄悄把这串refreshToken发给服务器,服务器验证无误后,会返回一个新的、有效的accessToken。 -
特点:有效期很长(几天到几十天不等)。只要它没失效,你就能一直保持登录状态。
🛡️ 身份与安全字段
3. profileArn(配置文件资源名称)
-
具体作用 :这是 AWS(亚马逊云) 体系中用来唯一标识用户配置文件的"身份证号"。
-
从你的数据
arn:aws:codewhisperer:...可以看出,Kiro 底层白嫖或使用了 AWS CodeWhisperer(现统称为 Amazon Q Developer)的代码助手服务。这串代码代表了该账号在 AWS 里的具体权限配置文件(Profile)。
4. expiresAt(过期时间)
-
具体作用 :标记当前这串
accessToken的法定死亡时间。 -
你数据里写的是
"2026-05-17T19:37:05.805Z"(注:末尾的Z代表协调世界时 UTC)。只要当前系统时间超过了这个时间,Kiro 就会知道当前的accessToken已经废了,必须用refreshToken去刷新。
🌐 登录来源字段
5. authMethod(认证方式)
- 具体作用 :记录这个账号是用什么大类方式登录的。这里的值是
"social"(社交账号登录),意味着它不是用传统的"用户名+密码"注册的,而是通过第三方平台授权进来的。
6. provider(服务提供商)
- 具体作用 :指明具体的第三方授权平台。这里是
"Google",说明这个账号最初是商家用一个 Google 谷歌邮箱账号 去绑定并登录 Kiro/AWS 得到的。
⚠️ 关键风险提示:
注意看你的 expiresAt 字段显示的是 2026年5月17日 ,如果这串凭证是商家在过期后卖给你的,KiroIDE 必须依赖 refreshToken 去 AWS 服务器换取新令牌。如果商家把这个 Google 账号的密码改了,或者在 Google 后台断开了该应用的授权,你的 refreshToken 就会瞬间失效,导致再次出现 "Access denied" 错误。