登陆逻辑:
- 拿到用户名,密码(mysql);验证码,验证码的Key(redis)(本文暂未做验证码)
- 先校验验证码的正确性(redis);从mysql数据库里面查询用户(mysql);
- 密码的匹配;
- 生成token(2h后过期)(redis);refresh_token(30d过期)(mysql);生成一个tokenKey(cookie);
前置参考文章
- 本文使用了自定义统一返回数据格式,详情参见文章:
云剪切板——统一后端返回数据格式 - 本文使用了nodejs连接mysql文章中封装后的查询数据库方法,详情参见文章:
Nodejs express 连接Mysql - 本文使用了nodejs连接redis文章中封装后的方法,详情参见文章:
Nodejs express 连接 Redis
目录文件结构
server
├─controllers
│ ├─RouterController.js
│ └─UserController.js
├─daos
│ ├─TokenDAO.js
│ └─UserDAO.js
├─dtos
│ └─UserDTO.js
├─services
│ ├─MysqlService.js
│ ├─RedisService.js
│ └─UserService.js
├─utils
│ ├─Constances.js
│ ├─Result.js
│ └─ResultCode.js
└─App.js
shell
主要代码
UserDTO
- UserDTO.js
module.exports = class UserDTO{
/**
* id
*/
id;
/**
* 用户名
*/
name;
/**
* 邮箱
*/
email;
/**
* 额度
*/
count;
constructor(data) {
this.id = data.id;
this.name = data.name;
this.email = data.email;
this.count = data.count;
}
}
js
UserDao
- UserDao.js
import {Query} from "../services/MysqlService";
export function getUser(username) {
return Query("SELECT * FROM `tb_user` WHERE `name` = ? OR `email` = ? ",[username,username])
}
js
UserController
- UserController.js
const express = require("express")
const UserController = express.Router();
const UserService = require("../services/UserService")
UserController.post("/login",UserService.login)
module.exports = UserController
js
RouterController
- RouterController.js
const UserController = require("./UserController")
module.exports =(app)=>{
app.use("/user",UserController)
}
js
App
- App.js
const express = require("express");
const Router = require("./controllers/RouterController")
const app = express();
app.use(express.json())
Router(app)
module.exports ={
path:"api",
handler:app
}
js
MysqlService
- MysqlService.js
const MysqlPoolConfig = {
host: "localhost",
port: "****",
user: "****",
password: "*****",
database: "clipboard",
connectionLimit:20
}
const mysql = require("mysql");
let pool = mysql.createPool(MysqlPoolConfig);
export const Query = (sql = "", params = []) => {
return new Promise((resolve, reject) => {
pool.getConnection((err, conn) => {
if (err) reject(err)
else conn.query(sql, params, (error, result, fields) => {
if (error) reject(error)
else resolve(result)
})
})
})
}
js
RedisService
- RedisService.js
const redis = require("redis");
const RedisConfig={
url:"redis://:***@localhost:0000"
}
const RedisClient = redis.createClient(RedisConfig)
RedisClient.connect()
.then(() => {
console.log("Redis Connect Success....")
})
.catch(err => {
console.log("Redis Connect Error....",err)
})
export const RedisService = ()=>{
return RedisClient
}
js
Contances
- Contances.js
export const JWTSecret = "*******"
export const KEY ={
TOKEN:"token_",
COOKIE:"auth"
}
export const VALUE = {
PLATFORM:{
WEB:"web",
ANDROID:"android",
WINDOWS:"windows"
}
}
js
UserService——核心
使用bcryptjs
验证密码:
bcrypt.compareSync(password, passFromDB)
js
使用jwt
生成token
:
jwt.sign({...userDTO}, JWTSecret, {expiresIn: "30d"})
js
- UserService.js
import {RedisService} from "./RedisService";
import {JWTSecret, KEY, VALUE} from "../utils/Constances";
const UserDTO = require("../dtos/UserDTO");
const Result = require("../utils/Result");
const UserDao = require("../daos/UserDao");
const TokenDao = require("../daos/TokenDao");
const validator = require("express-validator");
const bcrypt = require("bcryptjs")
const jwt = require("jsonwebtoken")
export const login = [
validator.body("username", "用户名不能为空").isLength({min: 1}),
validator.body("password", "密码不能为空").isLength({min: 1}),
(req, res, next) => {
let validationResult = validator.validationResult(req);
if (!validationResult.isEmpty()) return Result.result(res, Result.invalidParams(validationResult.array()))
// 1. 拿到用户名,密码(mysql);验证码,验证码的Key(redis)
let {username, password,platform} = req.body;
if(!platform) platform = VALUE.PLATFORM.WEB
// 2. 先校验验证码的正确性(redis);从mysql数据库里面查询用户(mysql);
//TODO: redis captcha
UserDao.getUser(username)
.then(result => {
if (result.length < 1) return Result.failed("用户名不正确");
let passFromDB = result[0].password;
let userDTO = new UserDTO(result[0]);
// 3. 密码的匹配;
//校验密码
if (!bcrypt.compareSync(password, passFromDB)) return Result.result(res, Result.failed("密码错误"));
// 4. 生成token(2h后过期)(redis);refresh_token(30d过期)(mysql);生成一个tokenKey(cookie);
let token = jwt.sign({...userDTO}, JWTSecret, {expiresIn: "2h"});
let refreshToken = jwt.sign({...userDTO}, JWTSecret, {expiresIn: "30d"})
let tokenKey = bcrypt.hashSync(refreshToken, bcrypt.genSaltSync(10))
//保存token到redis
RedisService().set(KEY.TOKEN + tokenKey, token, {EX: 2 * 60 * 60})
//保存refresh token到mysql
TokenDao.saveToken(userDTO.id,refreshToken,tokenKey,platform)
//保存到cookie
res.cookie(KEY.COOKIE,tokenKey,{maxAge:30*60*60})
return Result.result(res, Result.success(userDTO));
})
.catch(err => {
console.log(err)
return Result.result(res,Result.failed("登陆失败"))
})
}]
js
参考内容
- 【续加仪 云剪切板——统一后端返回数据格式】: https://www.hyz.cool/articles/209
- 【续加仪 Nodejs express 连接Mysql】: https://www.hyz.cool/articles/207
- 【续加仪 Nodejs express 连接 Redis】: https://www.hyz.cool/articles/208
- 【NPM mysql】: https://www.npmjs.com/package/mysql
- 【NPM redis】: https://www.npmjs.com/package/redis
- 【NPM bcryptjs】: https://www.npmjs.com/package/bcryptjs
- 【NPM express-validator】: https://www.npmjs.com/package/express-validator
- 【NPM jsonwebtoken】: https://www.npmjs.com/package/jsonwebtoken