阅读 127

「koa2从0开始搭建一个博客系统」(一)登陆模块

想写了一个系列文章,就是剖析一个MVC架构的koa2博客项目,架构设计如上,(干巴巴的文章

1.判断用户名是否存在

routerAPI

// 用户名是否存在
router.post('/isExist', async (ctx, next) => {
    //从客户端那拿到了 username 
    const { userName } = ctx.request.body
    console.log(ctx.request.body);
    //然后传给后端
    ctx.body = await isExist(userName)
    // ctx.body=ctx
})
复制代码

controller

async function isExist(userName) {
    const userInfo = await getUserInfo(userName)
    if (userInfo) {
        // { errno: 0, data: {....} }
        return new SuccessModel(userInfo)
    } else {
        // { errno: 10003, message: '用户名未存在' }
        return new ErrorModel(registerUserNameNotExistInfo)
    }
}
复制代码

server

/**
 * 获取用户信息
 * @param {string} userName 用户名
 * @param {string} password 密码
 */
async function getUserInfo(userName, password) {
    // 查询条件
    const whereOpt = {
        userName
    }
    //这里有传密码就返回了密码
    if (password) {
        Object.assign(whereOpt, { password })
    }

    // 查询
    const result = await User.findOne({
        attributes: ['id', 'userName', 'nickName', 'picture', 'city'],
        where: whereOpt
    })
    if (result == null) {
        // 未找到
        return result
    }

    // 格式化-->这里是用来判断是否用户有自己的图像在上面
    //没有就用自定义头像图片
    const formatRes = formatUser(result.dataValues)
    return formatRes
}
复制代码

model

//关联的数据库字段
// users
const User = seq.define('user', {
    userName: {
        type: STRING,
        allowNull: false,
        unique: true,
        comment: '用户名,唯一'
    },
    password: {
        type: STRING,
        allowNull: false,
        comment: '密码'
    },
    nickName: {
        type: STRING,
        allowNull: false,
        comment: '昵称'
    },
    gender: {
        type: DECIMAL,
        allowNull: false,
        defaultValue: 3,
        comment: '性别(1 男性,2 女性,3 保密)'
    },
    picture: {
        type: STRING,
        comment: '头像,图片地址'
    },
    city: {
        type: STRING,
        comment: '城市'
    }
})
复制代码

2.用户登陆

routerAPI

// 登录
router.post('/login', async (ctx, next) => {
    const { userName, password } = ctx.request.body
    console.log('request-body:',ctx.request.body)
    ctx.body = await login(ctx, userName, password)
    
})
复制代码

controller

async function login(ctx, userName, password) {
    // 获取用户信息
    //docrypto是用来加密密码的
    const userInfo = await getUserInfo(userName, doCrypto(password))
    if (!userInfo) {
        // 登录失败
        return new ErrorModel(loginFailInfo)
    }

    // 登录成功
    if (ctx.session.userInfo == null) {
        ctx.session.userInfo = userInfo
        //知识补充那里有详细介绍
    }
    return new SuccessModel()
}
复制代码

server

//userinfo 同上
复制代码

model

//user类 同上
复制代码

utils

const crypto = require('crypto')
const { CRYPTO_SECRET_KEY } = require('../conf/secretKeys')
//crypto_secret_key是自定义的随机值,用于数据加密加盐
/**
 * md5 加密
 * @param {string} content 明文
 */
function _md5(content) {
    const md5 = crypto.createHash('md5')
    return md5.update(content).digest('hex')
}
/**
 * 加密方法
 * @param {string} content 明文
 */
function doCrypto(content) {
  //md5的加密,设计上是不可逆的,就是我没办法通过结果反推原始值,但它已经被破解了;
  //可以通过crypto_secret_key加盐的方法进行进一步保障
  //只要crypto_secret_key没泄漏就没问题
    const str = `password=${content}&key=${CRYPTO_SECRET_KEY}`
    return _md5(str)
}
复制代码

补充知识说明

1.关于session和cookie

(1)生动形象理解Cookie和Session的关系

  1.Session是保存在服务器端,Cookie是保存在客户端。

  2.每次用户访问网站的时候,相当于去串门。

  3.用户带着cookie去服务器家,当当当敲门。

  4.服务器问是谁啊?

  5.用户:是我(cookie)啊!

  6.服务器:让我来确认一下(session确认)。

  7.服务器确认完毕后,放用户进门。

(2)实际网站登录请求的响应头

  1.这是一个网站登录之后返回的响应头,可以看到服务器要求浏览器设置的Cookies有好几个;这

​ 就是Cookies的来源,而token一般会作为用户的唯一凭证【登录成功,响应头set-cookies,浏览器设置Cookies】

  2.当浏览器下一次再请求该网站时,浏览器会把这些Cookies放到请求头一起提交到服务器;而Cookies携带了SessionID信息 (token)【再次请求,带上Cookies,包含SessionID】

  3.服务器通过SessionID即可找到对应的用户Session信息,然后判断该用户的登录状态【服务器根据SessionID获取用户登录态】

  4.如果Session中某些设置登录状态的变量是有效期内的,证明用户处于登录状态【Session有效,用户已登录】

  5.此时服务器就会返回需要登录之后才可以查看的网页内容,浏览器再进行解析便可以看到了【返回请求响应内容】

  6.当Cookie无效或者Session已过期后,我们再访问网站就需要重新登录了【Cookie无效,Session过期,要再次登录】

2.md5

crypto模块的目的是为了提供通用的加密和哈希算法。用纯JavaScript代码实现这些功能不是不可能,但速度会非常慢。Nodejs用C/C++实现这些算法后,通过cypto这个模块暴露为JavaScript接口,这样用起来方便,运行速度也快。

MD5是一种常用的哈希算法,用于给任意数据一个“签名”。这个签名通常用一个十六进制的字符串表示:

const crypto = require('crypto');

const hash = crypto.createHash('md5');

// 可任意多次调用update():
hash.update('Hello, world!');
hash.update('Hello, nodejs!');

console.log(hash.digest('hex')); // 7e1977739c748beac0c0fd14fd26a544
复制代码

update()方法默认字符串编码为UTF-8,也可以传入Buffer。

如果要计算SHA1,只需要把'md5'改成'sha1',就可以得到SHA1的结果1f32b9c9932c02227819a4151feed43e131aca40

还可以使用更安全的sha256sha512

3.关于md5后的一个小想法

我上面utils里面的密码,是加密而且用前端的secret_key加盐,md5是哈希加密不可逆,安全有保障了

那如果我有一个需求是,我数据库拿到加密的密码后,是要去能够自己解密后看原始值的,而且要保障安全性,那要怎么搞呢

下面这个算法是对称加密的,应该能实现我的那个需求(?好困,懒得研究,大佬看到后告诉我,不熬夜了

![](/Users/szuet/Library/Application Support/typora-user-images/image-20210617011145178.png)

这博客系统是mvc的架构,view-controller-server-model去展示代码的今晚遇到一个问题是,用户提交的密码肯定是加密的,但如果我是管理员,我想知道密码加密前的原始值,那就不能按仓库代码里面的md5+secrect值加盐处理,我看到文章下面有个aes加密,好像是用同一个密钥加密解密的,这样是不是能满足安全性的同时也满足我的需求……(我特喵在说什么,睡觉了)

文章分类
前端
文章标签