草庐IT

一文搞懂前后端常见登录态方案

轻风博客 2023-03-28 原文

 

 

正文开始之前,我们先要了解一个概念,就是什么是 登录态

主流Web应用比如浏览器是基于http协议的,而http协议是 无状态 的。什么是 无状态?就是服务器不知道是谁发送了这个http请求,无法识别区分用户身份。

所以登录态就是服务端用来区分用户身份,同时对用户进行记录的技术方案。

那怎么实现用户的登录态呢?常见的实现流程如下:

  1. 客户端用户输入登录凭据(如账户和密码),发送登录请求。
  2. 服务端校验用户是否合法(如认证和鉴权),合法后返回登录态,不合法返回第1步。
  3. 合法后携带登录态访问用户数据。

流程有了,如何实现呢?

常见的方案有HTTP基本认证、Cookie和Session认证、Token认证、单点登录认证等,下面一一介绍。

1. HTTP基本认证

HTTP基本认证是HTTP协议本身提供了一种服务端对客户端进行用户身份验证的方法。 流程如下:

sequenceDiagram
autonumber
客户端->>服务端:Get / HTTP/1.1 Host:www.qq.com
服务端->>客户端:HTTP/1.1 401 Unauthorised WWW-Authenticate: Basic realm="qq.com"
客户端->>客户端:弹出登录窗口
客户端->>服务端:Get / HTTP/1.1 Host:www.qq.com Authorization: Basic xxxxxx
服务端->>客户端:验证成功,返回用户数据
  1. 客户端向服务端请求需要登录态的数据
  2. 服务端向客户端返回401状态码,要求客户端验证
  3. 客户端根据返回的 WWW-Authenticate: Basic realm="qq.com",弹出用户名和密码输入框要求用户进行验证。
  4. 用户输入用户名和密码后,客户端将用户名及密码以 Base64 格式发送给服务端。
  5. 服务端验证通过后返回用户数据。

这是一种比较简单的验证用户身份的方式,甚至不需要写代码,只要后端服务器配置一下即可。

优点:兼容性好,主流浏览器都支持

缺点:

  • 不安全,账号密码是Base64编码,很容易解码。
  • 无法主动注销,除非关闭标签或浏览器。

2. Cookie和Session认证

先了解两个概念,Cookie和Session是什么呢? 上面说到HTTP是一种无状态协议,而Cookie和Session可以弥补 HTTP 的无状态特性。

2.1 什么是Cookie

Cookie是客户端请求服务端时,由服务端创建并由客户端存储和管理的小文本文件。具体流程如下:

  • 客户端首次发起请求。
  • 服务端通过HTTP响应头里Set-Cookie字段返回Cookie信息。
  • 客户端再发起请求时会通过HTTP请求头里Cookie字段携带Cookie信息。

2.2 什么是Session

Session是客户端请求服务端时服务端会为这次请求创建一个数据结构,这个结构可以通过内存、文件、数据库等方式保存。具体流程如下:

  • 客户端首次发起请求。
  • 服务端收到请求并自动为该客户端创建特定的Session并返回SessionID,用来标识该客户端。
  • 客户端通过服务端响应获取SessionID,并在后续请求携带SessionID。
  • 服务端根据收到的SessionID,在服务端找对应的Session,进而获取到客户端信息。

2.3 Cookie和Session认证流程

  1. 客户端向服务端发送认证信息(例如账号密码)
  2. 服务端根据客户端提供的认证信息执行验证逻辑,如果验证成功则生成Session并保存,同时通过响应头Set-Cookie字段返回对应的SessionID
  3. 客户端再次请求并在Cookie里携带SessionID。
  4. 服务端根据SessionID查找对应的Session,并根据业务逻辑返回相应的数据。

2.4 Cookie和Session认证优点

  • Cookie由客户端管理,支持设定有效期、安全加密、防篡改、请求路径等属性。
  • Session由服务端管理,支持有效期,可以存储各类数据。

2.4 Cookie和Session认证缺点

  • Cookie只能存储字符串,有大小和数量限制,对移动APP端支持不好,同时有跨域限制(主域不同)。
  • Session存储在服务端,对服务端有性能开销,客户端量太大会影响性能。如果集中存储(如存储在Redis),会带来额外的部署维护成本。

3. Token认证

Token又叫令牌,是服务端生成用来验证客户端身份的凭证,客户端每次请求都携带Token。 Token一般由以下数据组成:

uid(用户唯一的身份标识)
time(当前时间的时间戳)
sign(签名,由token的前几位+盐用哈希算法压缩成一定长的十六进制字符串)

3.1 Token认证流程

  1. 客户端向服务端发送认证信息(例如账号密码)
  2. 服务端根据客户端提供的认证信息执行验证逻辑(如查询数据库),如果验证成功则生成Token并返回。
  3. 客户端存储(可以存在Cookie、LocalStorage或本地缓存里)收到的Token,再次请求时携带Token(可以通过HTTP请求头Authorization字段)。
  4. 服务端校验Token(如查询数据库),并根据业务逻辑返回相应的数据。

3.2 Token认证优点

  • 客户端可以用Cookie、LocalStorage等存储,服务端不需要存储。
  • 安全性高(有签名校验)。
  • 支持移动APP端。
  • 支持跨域。

3.3 Token认证缺点

  • 占用额外传输宽带,因为Token比较大,可能会消耗一定的流量。
  • 每次签名校验会消耗服务端性能。
  • 有效期短(避免被盗用)。

3.4 Refresh Token

3.3里说到为了避免被盗用Token一般有效期比较短。但是有效期太短会造成客户端不断重新登录,体验太差。有没有什么办法可以解决这个问题呢?

那就是再来一个Token,一个专门生成Token的Token,称为 Refresh Token

流程如下:

  1. 客户端向服务端发送认证信息(例如账号密码)
  2. 服务端根据客户端提供的认证信息执行验证逻辑(如查询数据库),如果验证成功则生成Token和Refresh Token并返回。
  3. 客户端存储(可以存在Cookie、LocalStorage或本地缓存里)收到的Token和Refresh Token,再次请求时携带Token(可以通过HTTP请求头Authorization字段)。
  4. 服务端校验Token(如查询数据库),并根据业务逻辑返回相应的数据。
  5. 服务端发现Token过期了,拒绝了请求。
  6. 客户端重新请求并携带Refresh Token。
  7. 服务端校验Refresh Token并返回新Token和新Refresh Token。
  8. 客户端再次请求并携带新Token。

3.5 JWT

3.3、3.4里服务端校验客户端发过来的Token是否有效时,可能会查询数据库来验证。如果每次请求都要查询数据库,可能会带来额外性能消耗。

那这个有没有办法优化呢?

答案是有的,那就是JWT(JSON Web Token)。JWT 是 Auth0 提出的通过 对JSON进行加密签名 来实现授权验证的方案。

JWT也是一种Token,由三部分组成: Header头部 、 Payload负载 和 Signature签名。它是一个很长的字符串,中间用点( . )分隔成三个部分,列如 :

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Header头部:
{
   "alg": "Hash算法(HMAC、SHA256或RSA)",
   "typ": "Token的类型(JWT)"
 }

Payload负载:

{
   "iss": "签发人(issuer)",
   "exp": "过期时间(expiration time)",
   "sub": "主题(subject)",
   "aud": "受众(audience)",
   "nbf": "生效时间(not before)",
   "iat": "签发时间(issued at)",
   "jti": "编号(JWT ID)",
   "uid": "自定义字段(可以存储用户ID等)",
 }

Signature 签名:

HMACSHA256(
   base64UrlEncode(header) + "." + base64UrlEncode(payload),
   secret //设置的密钥
)

 

JWT的流程和Token的基本一样,因为已经携带了客户端信息(如用户ID等),所以服务端校验Token时不需要查询数据库了。

4. 单点登录认证

上面说的都是同一个域名(或同一主域)下,通过Cookie或Token携带凭证实现登录态管理。但是如果有很多域名,如何实现用户在一个域名下登录后,访问另一个域名也能自动登录呢?

这就是单点登录问题(Single Sign On

要实现SSO,需要有一个CAS(Central Authentication Service)中央授权服务(假设域名为cas.com)来提供统一的登录功能。

假如现在有域名要实现互相自动登录。流程如下:

先访问

再访问

先访问

  1. 客户端访问
  2. 发现没有登录(域下没有Session或Session失效),302跳转到并携带的回调地址(登录成功跳转回来的页面链接)。
  3. 发现没有登录(域下没有Session或Session失效),302跳转到登录页面并携带的回调地址。
  4. 客户端携带回调地址访问
  5. 客户端向发送认证信息(例如账号密码)。
  6. 登录成功并生成域下的Session,同时生成一个Token,根据回调地址携带此Token重定向到
  7. 客户端携带Token访问
  8. 访问验证Token的有效性,验证成功并生成域下的Session,完成登录。

再访问

  1. 客户端访问
  2. 校验失败,需要登录(域下没有Session或Session失效),302重定向到,并携带回调地址。
  3. 客户端携带回调地址访问
  4. 根据下的Session(访问时生成的)发现用户已登录,生成Token后302重定向到
  5. 访问验证Token的有效性,验证成功并生成域下的Session,完成登录。

总结:

  • HTTP基本认证:一般用于对安全要求不高或内部系统用户量极少的场景,实际应用不多。
  • Cookie和Session认证:一般应用于浏览器环境。
  • Token认证:除了浏览器环境外,还可以应用于移动端APP、小程序、PC端软件等非浏览器环境。
  • 单点登录认证:应用于大型站群系统或企业内不同业务系统间互通。

以上总结4种了常见的登录方案,还有OAuth2.0、扫码登录等方式,后续还会继续更新,敬请期待。。。

 

作者:yanweiyao

有关一文搞懂前后端常见登录态方案的更多相关文章

  1. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  2. git使用常见问题(提交代码,合并冲突) - 2

    文章目录git常用命令(简介,详细参数往下看)Git提交代码步骤gitpullgitstatusgitaddgitcommitgitpushgit代码冲突合并问题方法一:放弃本地代码方法二:合并代码常用命令以及详细参数gitadd将文件添加到仓库:gitdiff比较文件异同gitlog查看历史记录gitreset代码回滚版本库相关操作远程仓库相关操作分支相关操作创建分支查看分支:gitbranch合并分支:gitmerge删除分支:gitbranch-ddev查看分支合并图:gitlog–graph–pretty=oneline–abbrev-commit撤消某次提交git用户名密码相关配置g

  3. ruby - 将对象设置为 nil 是否很常见? - 2

    我正在构建一个应用程序,想知道是否将未使用的对象设置为nil是生产级编码中的常见做法。我知道这只是垃圾收集器的提示,并不总是处理对象。 最佳答案 根据这个thread如果您使用完一个成员对象,将其设置为nil将引发被引用对象被垃圾回收。如果它是局部变量,方法exit将做同样的事情。也就是说,如果您要求将成员显式设置为nil,我会质疑您的设计。 关于ruby-将对象设置为nil是否很常见?,我们在StackOverflow上找到一个类似的问题: https://

  4. ruby - 变量赋值后的 if 语句 - 有多常见? - 2

    我最近与一位同事讨论了以下Ruby语法:value=ifa==0"foo"elsifa>42"bar"else"fizz"end我个人并没有看到太多这种逻辑,但我的同事指出,这实际上是一种相当普遍的Rubyism。我试着用谷歌搜索这个主题,但没有找到任何文章、页面或SO问题来讨论它,这让我相信这可能是一种非常实际的技术。然而,另一位同事发现语法令人困惑,而是将上面的逻辑写成这样:ifa==0value="foo"elsifa>42value="bar"elsevalue="fizz"end缺点是value=的重复声明和隐式elsenil的丢失,如果我们想使用它的话。这也感觉它与Ruby

  5. Ruby 守护进程和 JRuby - 备选方案 - 2

    我有一个应用程序正在从Ruby迁移到JRuby(由于需要通过Java提供更好的Web服务安全支持)。我使用的gem之一是daemons创建后台作业。问题在于它使用fork+exec来创建后台进程,但这对JRuby来说是禁忌。那么-是否有用于创建后台作业的替代gem/wrapper?我目前的想法是只从shell脚本调用rake并让rake任务永远运行......提前致谢,克里斯。更新我们目前正在使用几个与Java线程相关的包装器,即https://github.com/jmettraux/rufus-scheduler和https://github.com/philostler/acts

  6. ruby - 使用 Ruby 和 Mechanize 登录网站 - 2

    我需要从站点抓取数据,但它需要我先登录。我一直在使用hpricot成功地抓取其他网站,但我是使用mechanize的新手,我真的对如何使用它感到困惑。我看到这个例子经常被引用:require'rubygems'require'mechanize'a=Mechanize.newa.get('http://rubyforge.org/')do|page|#Clicktheloginlinklogin_page=a.click(page.link_with(:text=>/LogIn/))#Submittheloginformmy_page=login_page.form_with(:act

  7. ruby - 在 ruby​​ 中的字符串前后添加空格? - 2

    我想在随机字符串前后添加一个空格。我试过使用"Random_string".center(1,"")但它不起作用。谢谢 最佳答案 我发现这是最优雅的解决方案:padded_string="#{random_string}"走简单的路没有错。 关于ruby-在ruby​​中的字符串前后添加空格?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/3357897/

  8. 常见网络安全产品汇总(私信发送思维导图) - 2

    安全产品安全网关类防火墙Firewall防火墙防火墙主要用于边界安全防护的权限控制和安全域的划分。防火墙•信息安全的防护系统,依照特定的规则,允许或是限制传输的数据通过。防火墙是一个由软件和硬件设备组合而成,在内外网之间、专网与公网之间的界面上构成的保护屏障。下一代防火墙•下一代防火墙,NextGenerationFirewall,简称NGFirewall,是一款可以全面应对应用层威胁的高性能防火墙,提供网络层应用层一体化安全防护。生产厂家•联想网御、CheckPoint、深信服、网康、天融信、华为、H3C等防火墙部署部署于内、外网编辑额,用于权限访问控制和安全域划分。UTM统一威胁管理(Un

  9. 关于Qt程序打包后运行库依赖的常见问题分析及解决方法 - 2

    目录一.大致如下常见问题:(1)找不到程序所依赖的Qt库version`Qt_5'notfound(requiredby(2)CouldnotLoadtheQtplatformplugin"xcb"in""eventhoughitwasfound(3)打包到在不同的linux系统下,或者打包到高版本的相同系统下,运行程序时,直接提示段错误即segmentationfault,或者Illegalinstruction(coredumped)非法指令(4)ldd应用程序或者库,查看运行所依赖的库时,直接报段错误二.问题逐个分析,得出解决方法:(1)找不到程序所依赖的Qt库version`Qt_5'

  10. ruby-on-rails - 使用用户或管理员模型和 Basecamp 样式子域设计登录 - 2

    我为Devise用户和管理员提供了不同的模型。我也在使用Basecamp风格的子域。除了我需要能够以用户或管理员身份进行身份验证的一些Controller和操作外,一切都运行良好。目前我有authenticate_user!在我的application_controller.rb中设置,对于那些只有管理员才能访问的Controller和操作,我使用skip_before_filter跳过它。不幸的是,我不能简单地指定每个Controller的身份验证要求,因为我仍然需要一些Controller和操作才能被用户或管理员访问。我尝试了一些方法都无济于事。看来,如果我移动authentica

随机推荐