最近论坛要从 Discuz 改版到 NodeBB 中, 由于原有 dz 框架使用了较长时间, 积累了一定的用户数, 为了对用户进行无感知的迁移, 首先需要将账户登录的问题解决.
1. Discuz 的加密方式
由于是从 dz 迁移到 nodebb, 所以得先了解 dz 的加密方式是如何实现的.
dz 的加密方式比较简单, 参考以下步骤:
1. 首先密码明文 pwd=123456,salt=666666(数据库中存放);
pwd=123456,salt=666666
2. 对 pwd 进行一次 md5, 取 32 位小写结果 hash1;
hash1=md5(pwd)
3. 将 hash1 与 salt 进行拼接得到 temp=hash1+salt;
temp=hash1+salt
4. 对 temp 进行一次 md5, 取 32 为小写结果, 即为数据库中的 password 字段.
password=md5(temp)
知道了 dz 的加密方式, 下面就可以修改 nodebb 了
2. 修改 Nodebb 的注册和登录流程
主要涉及的文件有
- `src/controllers/authentication.js`
- `/src/bcrypt.js`
- `/src/password.js`
- `/src/user/create.js`
1. 首先在注册的时候向数据库中同时存储 salt 字段, 初始的 salt 最好与账户有一定关联, 方便后续修改 mongo 数据库进行查询和修改 (/src/user/create.js)
- async.parallel([
- async.apply(User.setUserField, userData.uid, 'password', hash),
- // 在用户进行注册的时候向 User 信息中增加 salt 字段, 以便与 dz 的加密方式结合
- async.apply(User.setUserField, userData.uid, 'salt','123456'),
- async.apply(User.reset.updateExpiry, userData.uid),
- ],
- next
- )
2. 修改 / src/bcrypt.js 文件
- process.on('message', function (msg) {
- if (msg.type === 'hash') {
- hashPasswordAsDz(msg.password, msg.salt, done);
- } else if (msg.type === 'compare') {
- compare(String(msg.password || ''), String(msg.hash ||''), msg.salt, done);
- }
- });
- // 修改对密码的加密方式为 dz 的加密方式
- function hashPasswordAsDz(password, salt) {
- var md5 = crypto.createHash('md5');
- md5.update(password);
- var hash1 = md5.digest('hex');
- md5 = crypto.createHash('md5');
- var content = hash1 + salt;
- md5.update(content);
- var hash2 = md5.digest('hex');
- done(null, hash2)
- }
- // 修改比较密码时的逻辑, 与 dz 保持一致
- function compare(password, hash, salt, done) {
- var md5 = crypto.createHash('md5');
- md5.update(password);
- var hash1 = md5.digest('hex');
- md5 = crypto.createHash('md5');
- var content = hash1 + salt;
- md5.update(content);
- var hash2 = md5.digest('hex');
- if (hash2==hash)
- done(null, true);
- else done(null, false);
- }
- function done(err, result) {
- if (err) {
- process.send({err: err.message});
- return process.disconnect();
- }
- process.send({result: result});
- process.disconnect();
- }
3. 修改 / src/controllers/authentications.js
- userData: function (next) {
- db.getObjectFields('user:' + uid, ['password', 'passwordExpiry','salt'], next);
- },
在进行 login 的时候, 额外提取 salt 值, 便于后续传递给 compare 函数进行 md5 的计算.
- function (next) {
- Password.compare(password, userData.password, userData.salt,next);
- }
修改 compare 的参数列表, 将 userData 中的 salt 也传递进去.
4. 修改 / src/password.js
- function compare(password, hash,salt, callback) {
- getFakeHash(function (err, fakeHash) {
- if (err) {
- return callback(err);
- }
- forkChild({ type: 'compare', password: password, hash: hash || fakeHash ,salt:salt}, callback);
- });
- }
修改为增加 salt 参数后的 compare 函数, 这是调用 bcrypt.js 中的 compare 函数, 并进行回调.
3.NodeBB 小批量注册.
因为原论坛的数据量较小, 且没有太多的时间去解析 NodeBB 的逻辑, 所以采用最简单暴力的方法, 通过 selenium+pyvirtualdisplay 来进行批量注册. 帐号名和邮箱是 dz 里的数据, password 随意, 因为步骤 4 要改的.
- def register(s_email, s_username, s_password):
- display = Display(visible=0, size=(800, 600))
- display.start()
- browser = webdriver.Chrome()
- browser.get(config.FORUM_HOME_URL)
- time.sleep(2)
- register = browser.find_element_by_xpath('//*[@id="logged-out-menu"]/li[1]/a');
- register.click();# 打开注册页面
- time.sleep(2)
- browser.refresh()
- # 获取注册列表项
- email = browser.find_element_by_id('email')
- username = browser.find_element_by_id('username')
- password = browser.find_element_by_id('password')
- password_confirm = browser.find_element_by_id('password-confirm')
- register = browser.find_element_by_id('register')
- # 填充值
- email.send_keys(s_email)
- username.send_keys(s_username)
- password.send_keys(s_password)
- password_confirm.send_keys(s_password)
- register.click()
- time.sleep(3)
- browser.refresh()
- # 获取协议同意按钮并提交
- aggree1 = browser.find_element_by_id('gdpr_agree_data')
- aggree2 = browser.find_element_by_id('gdpr_agree_email')
- submit = browser.find_element_by_xpath('//*[@id="content"]/form/div[2]/div/button')
- aggree1.click()
- aggree2.click()
- submit.click()
- time.sleep(3)
大概跑了半天的时间注册完成, 然后开始修改 mongodb 数据库中的内容, 来完成最后一步
4. 修改 NodeBB 数据源
现在跑完步骤 3 其实可以使用我们设置的初始密码登陆, 但是本意是迁移 dz 到 NodeBB 中, 所以还需要修改 NodeBB 的数据库, 这里以 mongodb 为例.
1. 查看 mongodb 数据库里的内容, 发现主要与一个文档有关.
- 2.
- {
- "_id": "id",
- "_key": "user:1",
- "acceptTos": 0,
- "banned": 0,
- "birthday": "","email":"cc.zhang1024@gmail.com","fullname":"",
- "gdpr_consent": 1,
- "joindate": 1528885781389,
- "lastonline": 1529026648838,
- "lastposttime": 0,
- "location": "","picture":"",
- "postcount": 0,
- "profileviews": 0,
- "reputation": 0,
- "signature": "","status":"online","topiccount": 0,"uid": 1,"uploadedpicture":"",
- "username": "cc.zhang",
- "userslug": "cc-zhang",
- "website": "","password":"password","salt":"salt","passwordExpiry": 0
- }
其中_key='user:'+user.id, 我们要做的就是找到相关的 user 账户然后修改其 salt 为 dz 导出的 salt, 以及修改 password 为 dz 导出的 password 即可.
data.update({'email': email, 'salt': old_salt}, {'$set': {'salt': new_salt, 'password': hash}})
这里是采用 email 和 salt 确定文档, 然后进行更新.
5. 登陆验证
打开登陆页面, 输入在 dz 登陆的帐号密码验证是否成功, 若提示失败可能需要重启服务器. 目前仅是帐号密码部分进行了迁移和修改, 可能还会有部分尚未完成修改, 等后续发现再改吧
来源: http://blog.51cto.com/13801588/2130847