提交 39620190 编写于 作者: C chenruilong

feat: 密码策略升级 (sha256)

上级 39bf8560
......@@ -78,16 +78,16 @@ async function preLoginWithPassword (params = {}) {
}
}
const passwordUtils = new PasswordUtils({
passwordSecret: this.config.passwordSecret
passwordHash: userRecord.password,
passwordSecret: this.config.passwordSecret,
passwordSecretVersion: userRecord.password_secret_version
})
const {
success: checkPasswordSuccess,
refreshPasswordInfo
} = passwordUtils.checkUserPassword({
password,
passwordHash: userRecord.password,
passwordSecretVersion: userRecord.password_secret_version
password
})
if (!checkPasswordSuccess) {
// 更新用户ip对应的密码错误记录
......
......@@ -3,27 +3,62 @@ const {
} = require('../../common/utils')
const crypto = require('crypto')
const PasswordMethodMaps = {
A: 'hmac-sha1',
B: 'hmac-sha256'
}
const PasswordMethodFlagMaps = Object.keys(PasswordMethodMaps).reduce((res, item) => {
res[PasswordMethodMaps[item]] = item
return res
}, {})
const PasswordHashMethod = {
'hmac-sha1': function (content, secret) {
const hmac = crypto.createHmac('sha1', secret.toString('ascii'))
hmac.update(content)
return hmac.digest('hex')
},
'hmac-sha256': function (content, secret) {
const hmac = crypto.createHmac('sha256', secret)
hmac.update(content)
return hmac.digest('hex')
}
}
class PasswordUtils {
constructor ({
passwordSecret
passwordHash = '',
passwordSecret = '',
passwordSecretVersion
} = {}) {
const passwordSecretType = getType(passwordSecret)
if (passwordSecretType === 'array') {
this.passwordSecret = passwordSecret.sort((a, b) => {
return a.version - b.version
})
} else if (passwordSecretType === 'string') {
this.passwordSecret = [{ value: passwordSecret }]
} else {
throw new Error('Invalid password secret')
this.passwordHash = passwordHash
this.passwordSecretVersion = passwordSecretVersion
this.password = this.parsePassword()
// 老版本会存在 passwordSecret
if (passwordSecret) {
const passwordSecretType = getType(passwordSecret)
if (passwordSecretType === 'array') {
this.passwordSecret = passwordSecret.sort((a, b) => {
return a.version - b.version
})
} else if (passwordSecretType === 'string') {
this.passwordSecret = [{ value: passwordSecret }]
}
}
}
parsePassword () {
const [algorithmKey = '', cost = 0, hashStr = ''] = this.passwordHash.split('$').filter(key => key)
const algorithm = PasswordMethodMaps[algorithmKey] || null
const salt = hashStr.substring(0, Number(cost))
const hash = hashStr.substring(Number(cost))
return {
algorithm,
salt,
hash
}
}
......@@ -51,37 +86,34 @@ class PasswordUtils {
checkUserPassword (params = {}) {
const {
password,
passwordHash: passwordHashToCheck,
passwordSecretVersion,
autoRefresh = true
} = params
const currentPasswordSecret = this.getSecretByVersion({
version: passwordSecretVersion
})
if (!currentPasswordSecret) {
throw new Error('Invalid password version')
let passwordHash
if (this.password.algorithm) {
passwordHash = PasswordHashMethod[this.password.algorithm](password, this.password.salt)
} else {
const hash = this.generatePasswordHash({
password
})
passwordHash = hash.passwordHash
}
const {
value: passwordSecret
} = currentPasswordSecret
const {
passwordHash
} = this.generatePasswordHash({
password,
passwordSecret,
passwordSecretVersion
})
if (passwordHashToCheck !== passwordHash) {
if (passwordHash !== this.password.hash && passwordHash !== this.passwordHash) {
return {
success: false
}
}
let refreshPasswordInfo
if (autoRefresh && passwordSecretVersion !== this.getLastestSecret().version) {
if (autoRefresh) {
refreshPasswordInfo = this.generatePasswordHash({
password
password,
forceUseInternal: true
})
}
return {
success: true,
refreshPasswordInfo
......@@ -91,8 +123,7 @@ class PasswordUtils {
generatePasswordHash (params = {}) {
let {
password,
passwordSecret,
passwordSecretVersion
forceUseInternal = false
} = params
if (getType(password) !== 'string') {
throw new Error('Invalid password')
......@@ -101,14 +132,35 @@ class PasswordUtils {
if (!password) {
throw new Error('Invalid password')
}
if (!passwordSecret) {
const lastestSecret = this.getLastestSecret()
passwordSecret = lastestSecret.value
passwordSecretVersion = lastestSecret.version
// 没有 passwordSecret,使用内置算法(新版)
if (forceUseInternal || !this.passwordSecret) {
// 默认使用 sha256 加密算法
const salt = crypto.randomBytes(10).toString('hex')
const sha256Hash = PasswordHashMethod['hmac-sha256'](password, salt)
const algorithm = PasswordMethodFlagMaps['hmac-sha256']
// B 为固定值,对应 PasswordMethodMaps 中的 sha256算法
// hash 格式 $[PasswordMethodFlagMapsKey]$[salt size]$[salt][Hash]
const hashStr = `$${algorithm}$${salt.length}$${salt}${sha256Hash}`
return {
passwordHash: hashStr
}
}
// 旧版本兼容
let secret
if (this.passwordSecretVersion) {
secret = this.getSecretByVersion({
version: this.passwordSecretVersion
})
} else {
secret = this.getLastestSecret()
}
return {
passwordHash: PasswordHashMethod['hmac-sha1'](password, passwordSecret),
version: passwordSecretVersion
passwordHash: PasswordHashMethod['hmac-sha1'](password, secret.value),
version: secret.version
}
}
}
......
......@@ -53,18 +53,14 @@ async function preRegisterWithPassword(params = {}) {
await preRegister.call(this, {
user
})
const passwordUtils = new PasswordUtils({
passwordSecret: this.config.passwordSecret
})
const passwordUtils = new PasswordUtils()
const {
passwordHash,
version
passwordHash
} = passwordUtils.generatePasswordHash({
password
})
const extraData = {
password: passwordHash,
password_secret_version: version
password: passwordHash
}
return {
user,
......
......@@ -12,7 +12,7 @@ const {
userCollection,
EMAIL_SCENE,
CAPTCHA_SCENE,
LOG_TYPE
LOG_TYPE, dbCmd
} = require('../../common/constants')
const {
findUser
......@@ -92,17 +92,14 @@ module.exports = async function (params = {}) {
}
const { _id: uid } = userMatched[0]
const {
passwordHash,
version
} = new PasswordUtils({
passwordSecret: this.config.passwordSecret
}).generatePasswordHash({
passwordHash
} = new PasswordUtils().generatePasswordHash({
password
})
// 更新用户密码
await userCollection.doc(uid).update({
password: passwordHash,
password_secret_version: version,
password_secret_version: dbCmd.remove(),
valid_token_date: Date.now()
})
......
......@@ -12,7 +12,7 @@ const {
userCollection,
SMS_SCENE,
CAPTCHA_SCENE,
LOG_TYPE
LOG_TYPE, dbCmd
} = require('../../common/constants')
const {
findUser
......@@ -92,17 +92,14 @@ module.exports = async function (params = {}) {
}
const { _id: uid } = userMatched[0]
const {
passwordHash,
version
} = new PasswordUtils({
passwordSecret: this.config.passwordSecret
}).generatePasswordHash({
passwordHash
} = new PasswordUtils().generatePasswordHash({
password
})
// 更新用户密码
await userCollection.doc(uid).update({
password: passwordHash,
password_secret_version: version,
password_secret_version: dbCmd.remove(),
valid_token_date: Date.now()
})
......
const {
userCollection
userCollection, dbCmd
} = require('../../common/constants')
const {
ERROR
......@@ -32,16 +32,18 @@ module.exports = async function (params = {}) {
newPassword
} = params
const passwordUtils = new PasswordUtils({
passwordSecret: this.config.passwordSecret
passwordHash: userRecord.password,
passwordSecret: this.config.passwordSecret,
passwordSecretVersion: userRecord.password_secret_version
})
const {
success: checkPasswordSuccess
} = passwordUtils.checkUserPassword({
password: oldPassword,
passwordHash: userRecord.password,
passwordSecretVersion: userRecord.password_secret_version,
autoRefresh: false
})
if (!checkPasswordSuccess) {
throw {
errCode: ERROR.PASSWORD_ERROR
......@@ -49,15 +51,14 @@ module.exports = async function (params = {}) {
}
const {
passwordHash,
version
passwordHash
} = passwordUtils.generatePasswordHash({
password: newPassword
})
await userCollection.doc(uid).update({
password: passwordHash,
password_secret_version: version,
password_secret_version: dbCmd.remove(),
valid_token_date: Date.now() // refreshToken时会校验,如果创建token时间在此时间点之前,则拒绝下发新token,返回token失效错误码
})
// 执行更新密码操作后客户端应将用户退出重新登录
......
......@@ -82,19 +82,15 @@ module.exports = async function (params = {}) {
errCode: ERROR.ACCOUNT_EXISTS
}
}
const passwordUtils = new PasswordUtils({
passwordSecret: this.config.passwordSecret
})
const passwordUtils = new PasswordUtils()
const {
passwordHash,
version
passwordHash
} = passwordUtils.generatePasswordHash({
password
})
const data = {
username,
password: passwordHash,
password_secret_version: version,
dcloud_appid: authorizedApp || [],
nickname,
role: role || [],
......
......@@ -5,7 +5,7 @@ const {
ERROR
} = require('../../common/error')
const {
userCollection
userCollection, dbCmd
} = require('../../common/constants')
const PasswordUtils = require('../../lib/utils/password')
......@@ -106,18 +106,15 @@ module.exports = async function (params = {}) {
}
if (password) {
const passwordUtils = new PasswordUtils({
passwordSecret: this.config.passwordSecret
})
const passwordUtils = new PasswordUtils()
const {
passwordHash,
version
passwordHash
} = passwordUtils.generatePasswordHash({
password
})
data.password = passwordHash
data.password_secret_version = version
data.password_secret_version = dbCmd.remove()
}
await userCollection.doc(uid).update(data)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册