登录验证两种方案:token和cookie以及对比

前置知识

对PIN的理解:

在账号登录和身份认证的语境中,“PIN” 通常指 个人识别码(Personal Identification Number),是一种用于验证用户身份的短数字密码(常见为4-6位)。以下是具体说明:


1. 登录认证中的作用

用途:代替或补充密码,用于快速验证身份(如手机解锁、银行APP登录、设备访问等)。特点
比传统密码更简短,但安全性较低(需配合其他措施,如短信验证码或生物识别)。常用于高频次、低风险场景(如设备本地解锁)。


2. 常见应用场景

设备登录:手机/电脑的开机PIN码、智能门锁密码。金融服务:银行卡取款、支付软件(如支付宝的支付密码本质是PIN)。企业系统:部分内部系统要求“密码+PIN”双重验证。临时访问:会议Wi-Fi、共享设备的临时PIN码。


3. 与密码的区别
对比项 PIN 传统密码
长度 通常4-6位数字 8位以上,含字母/符号
安全性 依赖设备本地存储* 依赖服务器加密存储
使用场景 设备解锁、快速登录 账号全局安全认证

*注:PIN码若仅存储在本地(如手机),暴力破解可能导致数据泄露;而密码通常经服务器加密验证。


4. 安全建议

避免重复使用:不要将银行卡PIN与设备密码设为相同。启用多因素认证:如“PIN+指纹”或“PIN+短信验证码”。定期更换:尤其用于敏感服务时(如网银)。

cookie

HTTP无状态,每次请求都要携带cookie,以帮助识别用户身份;
服务端也可以向客户端set-cookie,cookie大小限制为4kb;
cookie默认有跨域限制,不跨域共享和传递,例如:

现代浏览器开始禁止第三方JS设置cookie

和跨域限制不同,这里是指禁止网页引入的第三方JS设置cookie,例如你有一个手机性能对比的网站,用户可以在你这个网站上来参考手机的性能,只访问你的网页你是没啥收入的,如果你想获得更多的收入,那么你可以植入一些JD的广告,而广告内容可能是一个图片,代码如下:


<img src="jd.com?info='华为手机'" >。

JD广告的js虽然访问不了你的cookie,因为cookie是不跨域共享的,但是可以拿到当前网页中的内容(它一看大概是和手机内容相关的),一旦你曾经登录过JD,那么上面的代码中的请求就会携带上你的cookie信息(jd就会知道你是谁),并顺路带上当前你访问的内容。

当有一天你访问JD时,你就会发现JD给你智能推荐你想要那款华为手机了。

因此,cookie新增了一个SameSite属性,用来防止CSRF攻击和用户追踪。

SameSite有三个值,其中Strict最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。

参考链接:cookie和token的区别

cookie 和 session

cookie在客户端,用于登录验证,存储用户标识 如useId;
session在服务端,存储用户详细信息,和cookie信息一一对应;
cookie+session是常见登录验证解决方案;
图片[1] - 登录验证两种方案:token和cookie以及对比 - 宋马

为什么要有session:

安全性cookie体积小,存储信息有限;cookie每次都会被携带,http携带信息少的话传输快;

cookie vs token

cookie是http规范,会自动传递;而token需要开发者手动自己传递;cookie会被浏览器默认存储;而token需要自己存储(localstorage或sessionStorage);token没有跨域限制;

cookie是官方的登录验证方案,但虽然互联网的发展,cookie暴露出很多限制,因此有了民间发起的token方式,更加灵活。

实现token的方案jwt(json web token)

前端发起登录,后端验证成功后,返回一个加密的token;前端自行存储这个token(其中包括用户信息,加密了的,非对称加密);以后访问服务端接口,都会携带这个token,作为验证信息;

token vs cookie 优缺点以及使用场景

参考链接: token vs cookie 优缺点以及使用场景

实战

使用Taro开发微信小程序进行登录

首先用户授权,授权之后通过getUserInfo() 接口获取用户的头像、昵称信息。

在 Egg.js 中,默认情况下 CSRF 中间件只会对 POST、PUT、DELETE、PATCH 等修改性请求进行 CSRF token 校验,而不会对 GET、HEAD、OPTIONS、TRACE 等非修改性请求进行校验。这是为了避免对一些不会对数据进行修改的请求增加额外的复杂性。

安装jwt和redis:


npm install egg-jwt egg-redis --save

TTL


ttl
的全称是 “Time To Live”,它表示缓存的生存时间或剩余生存时间。在缓存系统中,
ttl
用来指示一个缓存条目在多长时间内有效,通常以秒为单位。当缓存的 TTL 达到 0 时,缓存条目将被自动清除,即缓存失效。

数据库

MySQL是一个流行的开源关系型数据库管理系统,它是由瑞典公司MySQL AB开发的,并由Oracle公司持有和维护。MySQL采用了客户端-服务器模型,可以在各种操作系统上运行,包括Linux、Windows和macOS等。它支持SQL(Structured Query Language)作为查询语言,并提供了丰富的功能和工具,使用户能够管理和操作数据库。MySQL被广泛应用于Web应用程序开发、数据存储和管理等领域,因其高性能、可靠性和易用性而备受青睐。

Redis是一个开源的内存数据库(In-Memory Database),也被称为数据结构服务器。它提供了一个键值对存储系统,并支持多种数据结构,如字符串(Strings)、哈希(Hashes)、列表(Lists)、集合(Sets)、有序集合(Sorted Sets)等。Redis以其高性能、灵活性和丰富的功能而闻名,被广泛应用于缓存、会话存储、消息队列等场景。

除了作为内存数据库外,Redis也支持将数据持久化到磁盘上,以确保数据不会在重启时丢失。此外,Redis还提供了诸如发布订阅(Pub/Sub)模式、事务(Transactions)等高级功能,使其成为一种功能强大的数据存储和处理工具。

Redis使用C语言编写,可在各种操作系统上运行,并提供了多种语言的客户端库,方便开发者在不同的编程语言中使用Redis。

Redis和MySQL是两种不同类型的数据库管理系统,它们在数据存储和管理方面有一些相似之处,但也有一些明显的区别。

数据模型:

MySQL是一个关系型数据库管理系统(RDBMS),它使用表格结构来存储数据,并支持SQL查询语言。Redis是一个内存数据库,它以键值对的形式存储数据,并支持多种数据结构,如字符串、哈希、列表等。

数据持久化:

MySQL支持将数据持久化到磁盘上,以确保数据在系统重启时不会丢失。Redis也支持将数据持久化到磁盘上,但它的主要特点是数据存储在内存中,因此在性能方面通常比MySQL更快。

应用场景:

MySQL通常用于需要复杂的查询和事务处理的应用,如电子商务网站、ERP系统等。Redis通常用于需要高速读写和处理大量数据的场景,如缓存、会话存储、实时统计等。

虽然Redis和MySQL是不同类型的数据库管理系统,但在某些情况下它们可以一起使用。例如,可以将Redis用作MySQL的缓存层,提高系统的性能和响应速度。

常见登录方式与小程序登录

图片[2] - 登录验证两种方案:token和cookie以及对比 - 宋马
图片[3] - 登录验证两种方案:token和cookie以及对比 - 宋马
图片[4] - 登录验证两种方案:token和cookie以及对比 - 宋马
登录的目的:在系统中建立当用用户的唯一标识,即用户id。

小程序登录 – 2种

借助微信小程序生态能力,获取当前用户的手机号,然后通过手机号的验证码登录。通过调用wx.login来实现登录。获取openid

数据库设计

当设计一个支持用户通过手机号和微信登录的数据库结构时,你需要考虑以下几个方面:

用户表:用于存储用户的基本信息。微信账号表:用于存储用户通过微信登录时的相关信息。用户手机号表:用于存储用户的手机号信息。

以下是一个可能的数据库结构设计:

用户表(Users)


id
:主键,自增ID
username
:用户名(可选)
password
:密码(加密存储)其他用户信息字段

微信账号表(WechatAccounts)


id
:主键,自增ID
user_id
:外键,关联到用户表的主键
openid
:微信用户唯一标识其他微信相关信息字段

用户手机号表(PhoneNumbers)


id
:主键,自增ID
user_id
:外键,关联到用户表的主键
phone_number
:手机号其他手机号相关信息字段

通过这样的数据库结构设计,你可以实现以下功能:

用户可以通过手机号进行注册和登录,其信息存储在用户表和用户手机号表中。用户可以通过微信登录,其信息存储在微信账号表中,并且可以与现有的用户账号关联。支持用户手机号和微信账号之间的关联和切换。

这只是一个简单的示例,实际应用中可能还需要根据具体需求进行调整和扩展。例如,你可能还需要考虑用户的权限管理、用户信息的完整性和一致性、用户登录记录等方面。

下面是使用 Egg.js 框架和 egg-sequelize 插件创建上述数据库结构的示例代码。

首先,确保你已经安装了
egg-sequelize
插件,并且已经配置好了数据库连接信息。


config/config.default.js
中添加 Sequelize 配置信息,例如:


// config/config.default.js

module.exports = {
  // 配置数据库连接信息
  sequelize: {
    dialect: 'mysql', // 数据库类型
    host: 'localhost', // 数据库主机地址
    port: 3306, // 数据库端口号
    database: 'your_database', // 数据库名称
    username: 'your_username', // 数据库用户名
    password: 'your_password', // 数据库密码
  },
};

然后,创建模型文件和迁移文件来定义和创建数据库表结构。

创建用户模型(
app/model/user.js
):


// app/model/user.js

module.exports = app => {
  const { STRING, INTEGER } = app.Sequelize;

  const User = app.model.define('user', {
    id: { type: INTEGER, primaryKey: true, autoIncrement: true },
    username: STRING,
    password: STRING,
  });

  return User;
};

创建微信账号模型(
app/model/wechatAccount.js
):


// app/model/wechatAccount.js

module.exports = app => {
  const { STRING, INTEGER } = app.Sequelize;

  const WechatAccount = app.model.define('wechatAccount', {
    id: { type: INTEGER, primaryKey: true, autoIncrement: true },
    openid: STRING,
    // 关联到用户表的外键
    userId: { type: INTEGER, references: { model: 'users', key: 'id' } },
  });

  return WechatAccount;
};

创建用户手机号模型(
app/model/phoneNumber.js
):


// app/model/phoneNumber.js

module.exports = app => {
  const { STRING, INTEGER } = app.Sequelize;

  const PhoneNumber = app.model.define('phoneNumber', {
    id: { type: INTEGER, primaryKey: true, autoIncrement: true },
    phoneNumber: STRING,
    // 关联到用户表的外键
    userId: { type: INTEGER, references: { model: 'users', key: 'id' } },
  });

  return PhoneNumber;
};

创建数据库迁移文件来创建数据库表结构:


$ npx sequelize-cli migration:generate --name create_users_table
$ npx sequelize-cli migration:generate --name create_wechat_accounts_table
$ npx sequelize-cli migration:generate --name create_phone_numbers_table

在生成的迁移文件中编写创建表结构的代码,然后运行迁移命令来执行迁移:


$ npx sequelize-cli db:migrate

以上就是使用 Egg.js 和 egg-sequelize 创建上述数据库表结构的基本步骤。在实际项目中,你可能还需要根据具体需求对模型定义和数据库迁移文件进行调整和扩展。

阿里云短信服务SMS(short message service)

产品使用教程:参考链接

外部文档:https://juejin.cn/post/7226277125127979065

申请流程:新增资质 -> 申请签名 -> 申请模板 -> 进行测试阶段 -> SDK实际调用。

短信类型:验证码、短信通知、推广短信。

数据库操作

egg-sequelize:https://github.com/eggjs/egg-sequelize?tab=readme-ov-file

curd

CURD 是数据库操作的四个基本操作,分别是:

Create(创建):向数据库中插入新的数据记录。Update(更新):更新数据库中已有的数据记录。Read(读取):从数据库中查询数据记录。Delete(删除):从数据库中删除数据记录。

这四个操作通常被称为 CURD 操作,它们是对数据库进行增删改查的基本操作,是数据库操作的核心功能。

ORM

ORM 是对象关系映射(Object-Relational Mapping)的缩写,它是一种编程技术,用于在面向对象的应用程序中实现对关系型数据库的操作。ORM 把数据库中的表(Relations)映射为面向对象编程语言中的类(Objects),将表中的行(Rows)映射为类的实例(Instances),从而实现了数据库和应用程序之间的映射关系。

ORM 的主要目的是简化数据库操作,让开发人员可以用面向对象的方式来操作数据库,而不必关心数据库的底层细节和 SQL 语句的编写。

migrate

要向现有的 Collection 表中增加一个
platform
字段,你可以通过 Sequelize 提供的数据库迁移(migration)功能来实现。以下是一种常见的方法:

生成数据库迁移文件
运行以下命令生成一个新的数据库迁移文件:


npx sequelize migration:generate --name=add-platform-to-collection

编辑生成的迁移文件
打开生成的迁移文件(一般在
database/migrations
目录下),在
up
方法中添加对
platform
字段的操作:


'use strict';

module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.addColumn('collections', 'platform', {
      type: Sequelize.STRING,
      allowNull: false
    });
  },

  down: async (queryInterface, Sequelize) => {
    await queryInterface.removeColumn('collections', 'platform');
  }
};

运行数据库迁移
运行以下命令来执行数据库迁移,将新增的
platform
字段同步到数据库中:


npx sequelize db:migrate

通过以上步骤,你可以通过数据库迁移的方式向 Collection 表中增加一个
platform
字段,确保数据表结构与模型定义保持一致。这样可以避免直接手动修改数据库结构带来的风险,并确保数据库变更操作的可控性和可追踪性。

npx sequelize db:migrate命令

当你执行
npx sequelize db:migrate
命令时,Sequelize CLI 会根据数据库中记录的迁移历史表(sequelizeMeta)来确定要执行的迁移文件。Sequelize 会检查哪些迁移文件尚未被应用到数据库中,然后按顺序逐个执行这些未应用的迁移文件。

通常情况下,Sequelize 会自动执行
migrations
文件夹中尚未应用的最新的迁移文件。这些迁移文件是通过
npx sequelize migration:generate
命令生成的,每个迁移文件描述了一个特定的数据库结构变更操作。

通过执行
npx sequelize db:migrate
命令,Sequelize 会按照生成迁移文件时的顺序,逐个执行尚未被应用的迁移文件,以确保数据库结构与应用程序代码保持一致。如果你想查看具体执行了哪些迁移文件,可以观察命令行输出的日志信息,通常会显示正在执行的迁移文件名称。

npx sequelize db:migrate:undo

如果你想执行数据库迁移的
down
操作,即撤销先前的数据库结构变更,可以使用 Sequelize CLI 提供的命令来执行撤销操作。在 Sequelize 中,可以通过以下命令执行数据库迁移的
down
操作:


npx sequelize db:migrate:undo

这个命令会执行最近一个已应用的迁移文件的
down
操作,即撤销该迁移文件所做的数据库结构变更。通过执行
db:migrate:undo
命令,你可以逐步撤销先前的数据库结构变更,恢复到之前的状态。如果你想撤销多个迁移文件,可以多次执行该命令,每次执行都会撤销一个已应用的迁移文件。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容