当前位置: 首页 > news >正文

如何在 Node.js 中使用 bcrypt 对密码进行哈希处理

在网页开发领域中,安全性至关重要,特别是涉及到用户凭据如密码时。在网页开发中至关重要的一个安全程序是密码哈希处理。

密码哈希处理确保明文密码在数据库受到攻击时也难以被攻击者找到。但并非所有的哈希方法都是一样的,这就是 bcrypt 突出之处所在。

Node.js 是一个流行的用于开发网页应用的框架,它提供了一个强大的生态系统来构建安全的身份验证系统。在本文中,我们将探讨如何在 Node.js 中使用 bcrypt 对密码进行哈希处理。我们将看看如何在 Node.js 应用中平滑地集成 bcrypt,以提高安全性并有效地保护用户凭据。

无论您是一位经验丰富的 Node.js 开发者,希望加强您的身份验证实践,还是一位初学者,希望学习安全密码管理的最佳技术,本文都将对您有所帮助。让我们看看如何在 Node.js 中使用 bcrypt 对密码进行哈希处理。

(本文内容参考:java567.com)

我们将涵盖以下内容:

  1. 什么是哈希处理?
  2. 什么是 Bcrypt?
  3. 如何在 Node.js 中安装 Bcrypt
  4. 如何在 Node.js 中设置 Bcrypt
  5. 如何使用 Bcrypt 对密码进行哈希处理
  6. 如何使用 Bcrypt 验证密码
  7. 使用 Bcrypt 的安全最佳实践
  8. 结论

什么是哈希处理?

哈希处理涉及将给定的密钥或字符串转换为另一个值。通常,这由一个更短、固定长度的值或密钥来表示,它代表了原始值并便于检索。

什么是密码哈希处理?

密码哈希处理是将输入的密码转换为一串固定长度的字符,通常用于安全地存储和传输密码。

密码哈希函数被设计为单向函数。这意味着计算上不可能从哈希值反向获取原始输入密码。

例如,假设我们要对一个密码如 “password123” 进行哈希处理。该密码将使用像 bcrypt 这样的哈希算法转换为一个固定长度的字符串。一旦哈希函数处理完我们的密码,我们将获得一个哈希结果。

例如,使用 bcrypt 对 “password123” 进行哈希处理,结果可能如下所示:

e234dsdom3k2kmdl3l43iwes9vjro44223m3n32kn5n2ksdo4   

现在你已经了解了密码哈希处理的基本原理,是时候深入探讨使用 bcrypt 算法对密码进行哈希处理的实际应用了。

但在继续之前,让我们更多地了解一下 bcrypt,以便你理解它的工作原理、安装过程以及如何将其整合到 Node.js 项目中。

首先,让我们了解一下 bcrypt - 它是什么、它如何运作,以及在密码安全中的重要性。然后我们将讨论如何安装 bcrypt,并在 Node.js 环境中无缝整合它。这将包括详细的步骤,介绍如何在项目中设置 bcrypt,并有效地利用它的功能。

通过本文,你将全面了解 bcrypt,具备在 Node.js 应用中安全地对密码进行哈希处理的知识。因此,让我们开始这段旅程,通过 bcrypt 整合来提升项目的安全性。

什么是 bcrypt?

bcrypt 是一种加密算法,用于安全地存储密码。它将用户的密码转换为一个唯一的代码。这样,即使有人窃取了数据库,他们也不会轻易地恢复原始密码。

bcrypt 如何工作?

bcrypt 结合了哈希处理和一种称为盐化的技术,后者专门用于增强存储的密码的安全性。

以下是该过程的详细步骤:

  1. 哈希处理:bcrypt 使用一个复杂的数学函数处理用户的密码。这个函数将密码转换为一个看起来随机且无意义的固定长度字符串。存储在数据库中的是哈希值,而不是原始密码。由于哈希处理是单向的,因此反向哈希不会产生原始密码。
  2. 盐化:为了提高安全性,bcrypt 结合了一个称为盐的随机数。该盐对每个密码都是唯一的,并在哈希处理之前附加到密码上。组合值(密码 + 盐)然后传递给哈希处理函数。

如何在 Node.js 中安装 bcrypt

在安装 bcrypt 之前,您需要已经设置好一个 Node.js 项目。如果您还没有创建一个,请按照以下步骤创建一个新的 Node.js 项目:

创建一个目录:

这个命令会创建一个新的目录(文件夹),用于存放您的 Node.js 项目。它的名字是 bcrypt-password-hash。

mkdir bcrypt-password-hash
  • mkdir:该命令表示 “make directory”,用于创建一个新的目录。
  • bcrypt-password-hash:这是您要创建的目录的名称。您可以为项目目录选择任何您喜欢的名称。

进入新创建的目录:

这个命令将您导航到新创建的目录中,这样您就可以在其中开始工作。

cd bcrypt-password-hash
  • cd:该命令表示 “change directory”,用于从一个目录切换到另一个目录。
  • bcrypt-password-hash:这是您想要进入的目录的名称。

初始化一个新的 Node.js 项目:

这个命令在您创建的目录中初始化一个新的 Node.js 项目。它会创建一个 package.json 文件,用于管理您的 Node.js 项目的依赖关系和配置。

npm init -y
  • npm init:该命令使用 npm(Node 包管理器)初始化一个新的 Node.js 项目。
  • -y:这个标志自动接受 package.json 文件的所有默认值,因此您不必为每个字段手动提供输入。

运行这些命令后,您应该会在一个新的目录(bcrypt-password-hash)中看到一个 package.json 文件,这表示您已成功创建了一个新的 Node.js 项目。现在,您可以继续安装依赖并编写代码。

创建一个名为 index.js 的文件,您将在其中编写代码:

要创建一个名为 index.js 的文件,您可以在终端中使用 touch 命令。以下是操作步骤:

touch index.js
  • touch:该命令用于创建一个新文件。(请注意,您必须已经在您的计算机上安装了 touch 才能使用它。如果您还没有安装,可以在终端中运行以下命令安装 touch:npm install touch-cli -g。)
  • index.js:这是您要创建的文件的名称。在这种情况下,您正在创建一个名为 index.js 的 JavaScript 文件。

运行此命令后,您将在项目目录中看到一个名为 index.js 的新文件,您可以在其中编写 Node.js 代码,就像您在下图中看到的那样:

在这里插入图片描述

现在我们已经正确地创建了一个 Node.js 项目,我们可以在项目中安装 bcrypt。

安装所需的依赖项(bcrypt):

要安装 bcrypt,您将使用 npm,Node.js 的包管理器。以下是安装 bcrypt 的命令:

npm install bcrypt
  • npm install:该命令用于从 npm 注册表中安装软件包。
  • bcrypt:这是您要安装的软件包的名称。bcrypt 是一个在 Node.js 中安全地对密码进行哈希处理的常用软件包。

当您运行此命令时,npm 将下载并安装 bcrypt 包及其依赖项到您项目的 node_modules 目录中。这个目录将包括您项目所需的所有依赖项,包括 bcrypt。

如何在 Node.js 中设置 bcrypt

一旦在您的 Node.js 项目中安装了 bcrypt,您就可以将其功能无缝地整合到您的应用程序中。以下是操作步骤:

首先,在使用 npm 安装 bcrypt 包后,确保将其导入到您的 Node.js 应用程序的 index.js 文件中,以有效地利用其功能。

以下是具体操作方法:

const bcrypt = require('bcrypt');

这行代码确保在您的应用程序中可以访问 bcrypt 包,从而让您能够充分利用其强大的功能来进行安全的密码哈希处理和验证。

通过将 bcrypt 整合到您的项目中,您可以增强用户认证和数据保护的安全性。

bcrypt 提供了两个主要的功能来进行密码哈希处理和比较:

  1. bcrypt.hash():该函数用于生成明文密码的哈希值。它接受明文密码和一个盐因子(可选)作为输入参数,并以异步方式返回哈希密码。
  2. bcrypt.compare():该函数用于比较明文密码和其哈希值的对应项。它接受明文密码和哈希密码作为输入参数,并返回一个布尔值,指示密码是否匹配。

如何使用 Bcrypt 对密码进行哈希处理

在深入探讨密码哈希处理的重要性以及哈希和盐的概念之后,让我们在我们的 index.js 文件中将理论付诸实践。

如何生成盐并对密码进行哈希处理

正如我们所学的,安全密码哈希处理的一个关键方面是将一个唯一的盐混入到哈希处理过程中。bcrypt 通过无缝处理盐的生成和密码哈希处理来简化此过程。

首先,我们在我们的 Node.js 应用程序中引入 bcrypt 模块:

const bcrypt = require('bcrypt');

为了确保我们的密码哈希的强度,我们确定盐轮数。这个值决定了哈希的计算成本,因此也决定了安全级别:

const saltRounds = 10; // 通常在 10 和 12 之间

配置完成后,我们可以使用 bcrypt.genSalt() 函数异步生成一个盐。这个盐对每个密码哈希都是唯一的,增强了安全性:

bcrypt.genSalt(saltRounds, (err, salt) => {
if (err) {// 处理错误return;
}// 盐生成成功,继续对密码进行哈希处理
});

一旦生成了盐,我们就可以使用 bcrypt.hash() 函数将其与用户的密码组合起来,并计算哈希值。这将得到一个安全的哈希密码,准备好存储:

const userPassword = 'user_password'; // 替换为实际密码
bcrypt.hash(userPassword, salt, (err, hash) => {if (err) {// 处理错误return;}// 哈希处理成功,'hash' 包含了哈希密码
console.log('哈希密码:', hash);
});

通过在我们的 Node.js 应用程序中利用 bcrypt 进行密码哈希处理,我们确保了用户凭据的强大安全性。每个密码哈希都使用唯一的盐,再加上 bcrypt 的计算复杂性,加强了我们对未经授权的访问和恶意攻击的防御。

在下一节中,我们将探讨如何验证密码,并讨论安全管理哈希密码的最佳实践。

如何使用 Bcrypt 验证密码

现在我们已经了解了如何在我们的 Node.js 应用程序中使用 bcrypt 进行密码哈希处理,让我们将焦点转移到在用户认证过程中验证密码上。

在这一节中,我们将探讨 bcrypt 如何简化密码验证,确保安全和无缝的认证过程。

如何从数据库检索哈希密码

在我们验证用户密码之前,我们需要从数据库中检索与用户帐户关联的哈希密码。

假设您已经有一个用户认证系统,您通常会查询数据库,根据用户名或电子邮件获取与用户帐户关联的哈希密码。

一旦您从数据库中检索到哈希密码,您就可以继续进行密码验证过程。

如何验证密码

要使用 bcrypt 验证密码,使用 bcrypt.compare() 函数。此函数将用户在登录时提供的明文密码与数据库中存储的哈希密码进行比较。

以下是您如何在 Node.js 应用程序中使用 bcrypt 实现密码验证的方法:

const storedHashedPassword = '来自数据库的哈希密码';
const userInputPassword = '用户尝试的密码';bcrypt.compare(userInputPassword, storedHashedPassword, (err, result) => {if (err) {// 处理错误console.error('比较密码时出错:', err);return;}if (result) {// 密码匹配,认证成功console.log('密码匹配!用户已认证。');
} else {// 密码不匹配,认证失败console.log('密码不匹配!认证失败。');
}
});

在这段代码中,storedHashedPassword 表示从数据库中检索到的哈希密码,而 userInputPassword 则是用户在登录时提供的明文密码。bcrypt.compare() 函数比较这两个密码,并返回一个布尔值,指示它们是否匹配。

使用 bcrypt 的安全最佳实践

既然我们已经讨论了使用 bcrypt 进行密码哈希处理和验证的原则,让我们看看一些重要的安全最佳实践,以确保我们认证系统的完整性。

强大的密码准则

鼓励用户创建强大且复杂的密码,抵抗字典攻击。提供有关密码长度、包含字母数字字符、符号以及避免常见模式的指导。

盐化

始终为每个密码哈希使用唯一的盐。这可以防止攻击者使用预先计算的彩虹表来破解密码。bcrypt 自动处理盐的生成,确保每个哈希都是唯一的。

自适应哈希处理

bcrypt 使用自适应哈希处理,允许开发人员随着时间的推移调整哈希处理的计算成本。定期增加哈希处理轮数,以跟上硬件和计算能力的进步。

安全存储

在数据库中安全存储哈希密码。确保设置访问控制,防止未经授权访问用户凭据。避免存储明文密码或使用可逆加密算法。

错误处理

在使用 bcrypt 函数时实现适当的错误处理机制。优雅地处理错误,并避免泄露可能帮助攻击者利用漏洞的敏感信息。

结论

总而言之,我们探讨了密码安全的基本方面以及在 Node.js 应用程序中使用 bcrypt 保护用户凭据的作用。从理解密码哈希处理和盐化的基本原理到实施安全认证机制,我们涵盖了一系列旨在增强应用程序安全性的主题。

通过利用 bcrypt 进行密码哈希处理和验证,我们确保敏感用户数据免受未经授权的访问和恶意攻击。bcrypt 的强大算法,结合自适应哈希处理和盐生成,提供了可靠的防御机制,以应对常见的基于密码的漏洞。

我们还讨论了安全最佳实践,包括强大的密码策略、安全的存储实践和错误处理。通过遵循这些最佳实践,并警惕不断演变的威胁,我们可以创建一个安全的认证系统,让用户信心倍增,并维护应用程序的完整性。

让我们继续将安全性置于首位,努力追求构建强大可靠的应用程序。

感谢您加入我一起探索 bcrypt 的密码安全性。让我们共同为所有用户创造一个更安全的数字环境。

(本文内容参考:java567.com)

相关文章:

如何在 Node.js 中使用 bcrypt 对密码进行哈希处理

在网页开发领域中,安全性至关重要,特别是涉及到用户凭据如密码时。在网页开发中至关重要的一个安全程序是密码哈希处理。 密码哈希处理确保明文密码在数据库受到攻击时也难以被攻击者找到。但并非所有的哈希方法都是一样的,这就是 bcrypt 突…...

嵌入式学习49-单片机2

指令周期 1M 机器周期 12M (晶体震荡器产生) 中断两种方式 …...

汽车EDI:如何与奔驰建立EDI连接?

梅赛德斯-奔驰是世界闻名的豪华汽车品牌,无论是技术实力还是历史底蕴都在全球汽车主机厂中居于领先位置。奔驰拥有多种车型,多元化的产品布局不仅满足了不同用户画像的需求,也对其供应链体系有着极大的考验。 本文将为大家介绍梅赛德斯-奔驰乘…...

性能分析--内存知识

内存相关知识 计算机中与CPU进行数据交换的桥梁。内存的速度,比CPU的速度要慢很多。比磁盘速度要快很多。内存中存放数据,一旦断电就会消失。linux系统的 /proc路径下的文件,都是内存文件。内存大小,一般 是GB为单位。 现在都操作…...

目标检测标签分配策略,难样本挖掘策略

在目标检测任务中,样本的划分对于模型的性能具有至关重要的影响。其中,正样本指的是包含目标物体的图像或区域,而负样本则是不包含目标物体的图像或区域。然而,在负样本中,有一部分样本由于其与正样本在特征上的相似性…...

Java | Leetcode Java题解之第16题最接近的三数之和

题目&#xff1a; 题解&#xff1a; class Solution {public int threeSumClosest(int[] nums, int target) {Arrays.sort(nums);int n nums.length;int best 10000000;// 枚举 afor (int i 0; i < n; i) {// 保证和上一次枚举的元素不相等if (i > 0 && nums…...

FIN和RST的区别,几种TCP连接出现RST的情况

一、RST跟FIN的区别&#xff1a; 正常关闭连接的时候发的包是FIN&#xff0c;但是如果是异常关闭连接&#xff0c;则发送RST包 两者的区别在于&#xff1a; 1.RST不必等缓冲区的包都发出去&#xff0c;直接就丢弃缓存区的包发送RST包。而FIN需要先处理完缓存区的包才能发送F…...

2024/4/1—力扣—删除字符使频率相同

代码实现&#xff1a; 思路&#xff1a; 步骤一&#xff1a;统计各字母出现频率 步骤二&#xff1a;频率从高到低排序&#xff0c;形成频率数组 步骤三&#xff1a;频率数组只有如下组合符合要求&#xff1a; 1, 0...0n 1, n...n (, 0)n...n, 1(, 0) bool equalFrequency(char…...

Spring源码解析-容器基本实现

spring源码解析 整体架构 defaultListableBeanFactory xmlBeanDefinitionReader 创建XmlBeanFactory 对资源文件进行加载–Resource 利用LoadBeandefinitions(resource)方法加载配置中的bean loadBeandefinitions加载步骤 doLoadBeanDefinition xml配置模式 validationMode 获…...

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单视频处理实战案例 之四 简单视频倒放效果

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单视频处理实战案例 之四 简单视频倒放效果 目录 Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单视频处理实战案例 之四 简单视频倒放效果 一、简单介绍 二、简单视频倒放效果实现原理 三、简单视频倒放效果案例实现…...

蓝牙学习十(扫描)

一、简介 从之前的文章中我们知道&#xff0c;蓝牙GAP层定义了四种角色&#xff0c;广播者&#xff08;Broadcaster&#xff09;、观察者&#xff08;Observer&#xff09;、外围设备&#xff08;Peripheral&#xff09;、中央设备&#xff08;Central&#xff09;。 之前的学习…...

(26)4.7 字符函数和字符串函数

#include<stdio.h> #include<string.h> #include<assert.h> //int my_strcmp(const char* str1, const char* str2) //{ // assert(str1 && str2);//指针有效性&#xff0c;不能为空指针 // while (*str1 *str2) // { // if (*str1…...

交换机与队列的简介

1.流程 首先先介绍一个简单的一个消息推送到接收的流程&#xff0c;提供一个简单的图 黄色的圈圈就是我们的消息推送服务&#xff0c;将消息推送到 中间方框里面也就是 rabbitMq的服务器&#xff0c;然后经过服务器里面的交换机、队列等各种关系&#xff08;后面会详细讲&…...

1.docker

Docker 是一种容器化平台&#xff0c;可以在不同的操作系统中轻松运行和管理应用程序。它使用容器技术来打包应用程序及其所有依赖关系&#xff0c;使其可以在任何环境中运行。 Docker 的基本概念包括以下几个部分&#xff1a; 镜像&#xff08;Image&#xff09;&#xff1a;…...

ThinkPHP审计(2) Thinkphp反序列化链5.1.X原理分析从0编写POC

ThinkPHP审计(2) Thinkphp反序列化链子5.1.X原理分析&从0编写POC 文章目录 ThinkPHP审计(2) Thinkphp反序列化链子5.1.X原理分析&从0编写POC动态调试环境配置Thinkphp反序列化链5.1.X原理分析一.实现任意文件删除二.实现任意命令执行真正的难点 Thinkphp反序列化链5.1.…...

KingbsaeES数据库分区表的详细用法

数据库版本&#xff1a;KingbaseES V008R006C008B0014 简介 分区表是一种将大型数据库表拆分为更小、更可管理的部分的技术。它通过将表数据分散存储到多个物理存储单元中&#xff0c;可以提高查询和数据维护的性能&#xff0c;并优化对大型数据集的处理。本篇文章以kingbase为…...

MySQL 索引底层探索:为什么是B+树?

MySQL 索引底层探索&#xff1a;为什么是B树&#xff1f; 1. 由一个例子总结索引的特点2. 基于哈希表实现的哈希索引3. 高效的查找方式&#xff1a;二分查找4. 基于二分查找思想的二叉查找树5. 升级版的BST树&#xff1a;AVL 树6. 更加符合磁盘特征的B树7. 不断优化的B树&#…...

XML HTTP传输 小结

what’s XML XML 指可扩展标记语言&#xff08;eXtensible Markup Language&#xff09;。 XML 被设计用来传输和存储数据&#xff0c;不用于表现和展示数据&#xff0c;HTML 则用来表现数据。 XML 是独立于软件和硬件的信息传输工具。 应该掌握的基础知识 HTMLJavaScript…...

相机标定——四个坐标系介绍

世界坐标系(Xw,Yw,Zw) 世界坐标系是一个用于描述和定位三维空间中物体位置的坐标系&#xff0c;通常反映真实世界下物体的位置和方向。它是一个惯性坐标系&#xff0c;被用作整个场景或系统的参考框架。在很多情况下&#xff0c;世界坐标系被认为是固定不变的&#xff0c;即它…...

C++:MySQL数据库的增删改(三)

1、相关API 执行所有的sql语句都是mysql_query或者mysql_real_query mysql_query无法处理带有特殊字符的sql语句&#xff08;如&#xff1a;反斜杠0&#xff09;mysql_real_query则可以避免&#xff0c;一般使用这个。 mysql_affected_rows&#xff1a;获取sql语句执行结果影响…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...

莫兰迪高级灰总结计划简约商务通用PPT模版

莫兰迪高级灰总结计划简约商务通用PPT模版&#xff0c;莫兰迪调色板清新简约工作汇报PPT模版&#xff0c;莫兰迪时尚风极简设计PPT模版&#xff0c;大学生毕业论文答辩PPT模版&#xff0c;莫兰迪配色总结计划简约商务通用PPT模版&#xff0c;莫兰迪商务汇报PPT模版&#xff0c;…...

ZYNQ学习记录FPGA(一)ZYNQ简介

一、知识准备 1.一些术语,缩写和概念&#xff1a; 1&#xff09;ZYNQ全称&#xff1a;ZYNQ7000 All Pgrammable SoC 2&#xff09;SoC:system on chips(片上系统)&#xff0c;对比集成电路的SoB&#xff08;system on board&#xff09; 3&#xff09;ARM&#xff1a;处理器…...

电脑桌面太单调,用Python写一个桌面小宠物应用。

下面是一个使用Python创建的简单桌面小宠物应用。这个小宠物会在桌面上游荡&#xff0c;可以响应鼠标点击&#xff0c;并且有简单的动画效果。 import tkinter as tk import random import time from PIL import Image, ImageTk import os import sysclass DesktopPet:def __i…...