背景(R61 实测):浏览器密码管理器在「设置密码」页预填 123456,考生若直接提交,该口令即成为其账号真实口令;而后端此前仅由 validate 校验「长度 >= 6」,弱口令完全合法。R62 已从前端修掉预填,本次补齐后端这道闸门。 - app/lib/passwordPolicy.js + password_blacklist.json:口令策略唯一事实来源。黑名单刻意做成纯 JSON,供 Node 运行时与运维脚本共用,避免两份清单漂移。 - controller/auth.js:activate() 与 resetPassword() 接入策略(错误码 2001 / 7001),位置在 validate 之后、短信校验之前(纯本地计算、无副作用、快速失败)。 - validate/index.js:补 max:20,与前端 el-form 规则对齐(此前只有前端限制长度上限)。 - scripts/_db.cjs:DB 配置解析唯一入口;scan_weak_password_accounts.cjs:存量弱口令只读扫描器;verify_password_policy_api.cjs:真接口验证(含「强口令通过」正向对照臂,防假绿)。
97 lines
3.1 KiB
JavaScript
97 lines
3.1 KiB
JavaScript
'use strict';
|
||
|
||
/**
|
||
* 统一输入校验规则集(基于 egg-validate / parameter)
|
||
*
|
||
* 对应各接口请求体的字段级校验。校验失败由控制器以
|
||
* `ctx.helper.error(400, '参数校验失败:' + validateErr.errors[0].message)`
|
||
* 统一封装为应用错误信封,与 admin 控制器既有模式保持一致。
|
||
*
|
||
* 规则说明:
|
||
* - type: 'string' + required: true + allowEmpty: false:字段必填且不可为空串/纯空白
|
||
* - trim: true:校验前去除首尾空白
|
||
* - min / max:字符串长度区间(口令口径 6-20,与前端 el-form 规则保持一致)
|
||
* - message:为每个校验项提供中文错误文案,parameter 会在 errors[] 中携带对应 message
|
||
*
|
||
* ⚠️ 口令的**强度**(弱口令黑名单 / 纯重复字符 / 连续序列 / 账号派生值)**不在本文件**。
|
||
* 本文件只负责「必填 + 长度格式」这一层;强度规则由 app/lib/passwordPolicy.js
|
||
* 单源提供,调用点在 controller/auth.js 的 activate() 与 resetPassword()。
|
||
* 分层理由:同一份强度规则需被多个口令写入口共用,并须能被运维脚本复用
|
||
* (脚本要扫描存量账号是否命中弱口令),散落实现必然出现「一处拦、一处放」。
|
||
*/
|
||
|
||
const required = (msg) => ({
|
||
type: 'string',
|
||
required: true,
|
||
allowEmpty: false,
|
||
trim: true,
|
||
message: { required: msg, empty: msg },
|
||
});
|
||
|
||
module.exports = {
|
||
// 选手登录:手机号 + 密码
|
||
login: {
|
||
phone: required('请输入手机号'),
|
||
password: required('请输入密码'),
|
||
},
|
||
|
||
// 管理员登录:用户名 + 密码
|
||
adminLogin: {
|
||
username: required('请输入管理员用户名'),
|
||
password: required('请输入密码'),
|
||
},
|
||
|
||
// 发送短信验证码:身份证号 + 图形验证码
|
||
sms: {
|
||
phone: required('请输入身份证号'),
|
||
captcha: required('请输入图形验证码'),
|
||
},
|
||
|
||
// 账号激活:身份证号 + 手机号 + 短信验证码 + 密码(>=6)
|
||
activate: {
|
||
id_number: required('请输入身份证号'),
|
||
phone: required('请输入手机号'),
|
||
smsCode: required('请输入短信验证码'),
|
||
password: {
|
||
type: 'string',
|
||
required: true,
|
||
allowEmpty: false,
|
||
trim: true,
|
||
min: 6,
|
||
max: 20,
|
||
message: {
|
||
required: '请输入密码',
|
||
empty: '请输入密码',
|
||
min: '密码长度不能少于 6 位',
|
||
max: '密码长度不能超过 20 位',
|
||
},
|
||
},
|
||
},
|
||
|
||
// 忘记密码-发码:身份证号 + 图形验证码
|
||
forgotPassword: {
|
||
id_number: required('请输入身份证号'),
|
||
captcha: required('请输入图形验证码'),
|
||
},
|
||
|
||
// 重置密码:身份证号 + 短信验证码 + 密码(>=6)
|
||
resetPassword: {
|
||
id_number: required('请输入身份证号'),
|
||
smsCode: required('请输入短信验证码'),
|
||
password: {
|
||
type: 'string',
|
||
required: true,
|
||
allowEmpty: false,
|
||
trim: true,
|
||
min: 6,
|
||
max: 20,
|
||
message: {
|
||
required: '请输入密码',
|
||
empty: '请输入密码',
|
||
min: '密码长度不能少于 6 位',
|
||
max: '密码长度不能超过 20 位',
|
||
},
|
||
},
|
||
},
|
||
};
|