哈希
AdonisJS 哈希模块允许你使用 bcrypt 或 Argon2 对值进行哈希处理,以及添加自定义哈希驱动程序的选项。
你可以在 config/hash.ts
文件中配置你选择的驱动程序。
import { hashConfig } from '@adonisjs/core/build/config'
export default hashConfig({
default: Env.get('HASH_DRIVER', 'argon'),
list: {
/**
* 确保从 npm 安装驱动程序
* ------------------------------------
* npm i phc-argon2
* ------------------------------------
*/
argon: {
driver: 'argon2',
variant: 'id',
iterations: 3,
memory: 4096,
parallelism: 1,
saltSize: 16,
},
/**
* 确保从 npm 安装驱动程序
* ------------------------------------
* npm i phc-bcrypt
* ------------------------------------
*/
bcrypt: {
driver: 'bcrypt',
rounds: 10,
},
},
})
默认哈希器
default
属性将散列器配置为默认用于散列值。它必须是列表对象中可用的哈希器之一。
可用的哈希器
list
对象包含一个或多个可用于散列值的散列器。每个哈希器都必须使用可用的驱动程序之一。例如:
- argon 哈希器使用
argon2
驱动程序。 - bcrypt 散列器使用
bcrypt
驱动程序。
# 如果使用 bcrypt
npm i phc-bcrypt
# 如果使用argon2
npm i phc-argon2
哈希值
make
Hash.make
方法接受一个字符串值得到一个散列。
import Hash from '@ioc:Adonis/Core/Hash'
const hashedPassword = await Hash.make(user.password)
Most of the time, you will be hashing the user's password, so it is better to use a model hook to perform the hashing.
import Hash from '@ioc:Adonis/Core/Hash'
import { column, beforeSave, BaseModel } from '@ioc:Adonis/Lucid/Orm'
export default class User extends BaseModel {
@column({ isPrimary: true })
public id: number
@column({ serializeAs: null })
public password: string
@beforeSave()
public static async hashPassword(user: User) {
if (user.$dirty.password) {
user.password = await Hash.make(user.password)
}
}
}
验证
您不能将哈希值转回明文字符串,你仅能验证给定的明文字符串是否匹配对应的哈希值。
if (await Hash.verify(hashedValue, plainTextValue)) {
// 验证通过
}
NeedsReHash
查询之前生成的哈希值是否需要重新生成。如果哈希的加密系数改变,此方法将返回 true 。
在用户登录时通常是检查 needsReHash
的最好时机。
if (Hash.needsReHash(user.password)) {
// 你必须修改模型钩子函数,
// 不要重新哈希已经被哈希过的密码
user.password = await Hash.make(plainPassword)
}
PHC 字符串格式
bcrypt 和 Argon2 驱动程序根据 PHC string format 返回哈希值。它允许我们验证哈希值是否与目前配置的哈希函数匹配,并确定是否需要重新哈希。
添加自定义驱动程序
哈希模块是可扩展的,允许你注册自定义的驱动程序。每个驱动程序必须实现下面的 hashdriverContract
接口。
interface HashDriverContract {
ids?: string[]
params?: any
/**
* 使用默认映射对纯文本进行哈希
*/
make(value: string): Promise<string>
/**
* 检查哈希值是否需要重新哈希
*/
needsReHash?(hashedValue: string): boolean
/**
* 根据散列值验证普通值是否是有效
*/
verify(hashedValue: string, plainValue: string): Promise<boolean>
}
make
make
方法负责哈希纯字符串值。
verify
verify
方法负责根据预先存在的哈希验证纯字符串。
needsReHash
needsReHash
是可选的。但是,如果你的散列算法支持它,则必须实现它。
params/ids
params
和 ids
属性是你在使用 PHC 字符串格式时需要的东西。只需检查现有驱动程序的实现并阅读有关 PHC 字符串格式的信息即可了解更多信息。
由外向内延伸
任何时候你都在扩展框架的核心。最好假设你无权访问应用程序代码及其依赖项。换句话说,像编写第三方包一样编写扩展,并使用依赖注入来依赖其他依赖。
出于演示目的,让我们创建一个虚拟哈希驱动程序:
mkdir providers/HashDriver
touch providers/HashDriver/index.ts
目录结构如下所示。
providers
└── HashDriver
└── index.ts
打开HashDriver/index.ts
文件并将以下内容粘贴到其中。
// 文件名: providers/HashDriver/index.ts
import { HashDriverContract } from '@ioc:Adonis/Core/Hash'
export class PlainTextDriver implements HashDriverContract {
public async make(value: string) {
return value
}
public async verify(hashedValue: string, plainValue: string) {
return hashedValue === plainValue
}
}
最后,打开 providers/AppProvider.ts
文件并在 boot
方法中添加自定义驱动程序。
import { ApplicationContract } from '@ioc:Adonis/Core/Application'
export default class AppProvider {
public static needsApplication = true
constructor(protected app: ApplicationContract) {}
public async boot() {
const { PlainTextDriver } = await import('./HashDriver')
const Hash = this.app.container.use('Adonis/Core/Hash')
Hash.extend('plainText', () => {
return new PlainTextDriver()
})
}
}
瞧!你的 PlainTextDriver
驱动程序已准备好使用。
通知 TypeScript 新驱动程序
在可以在 config/hash.ts
文件中引用此驱动程序之前,你必须告知 TypeScript 静态编译器它的存在。
如果你正在创建一个包,那么你可以在你的包主文件中编写以下代码,否则你可以将它写在 contracts/hash.ts
文件中。
import { PlainTextDriver } from '../providers/HashDriver'
declare module '@ioc:Adonis/Core/Hash' {
interface HashDrivers {
plainText: {
config: {
driver: 'plainText',
// ...其余的配置
}
implementation: PlainTextDriver
}
}
}
更新配置
为了使用驱动程序,你必须在配置文件中定义一个映射,设置 driver=plainText
。
// 文件名: config/hash.ts
list: {
myHashDriver: {
driver: 'plainText',
},
// 其他哈希器
}
现在,你可以使用新定义的映射,如下所示。
await Hash.use('myHashDriver').make('foo')
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。