一、koa+passport+mongoose+redis实现登录、注册、验证和退出的接口
- 创建
axios.js
配置文件,通过npm i axios
下载axios
,通过axios.create
创建对外暴露的接口,baseURL
配置基础路径,判断开发环境,timeout
超时时间,headers
头部区域,代码如下所示:
import axios from 'axios'
const instance = axios.create({
baseURL: `http://${process.env.HOST||'localhost'}:${process.env.POST||3000}`,
timeout: 1000,
headers: {
}
})
export default instance
- 创建
config.js
配置文件,配置dbs
数据库,redis
远程字典服务以及smtp
邮件传输协议。在smtp
中, 设置host
邮箱主机,设置user
邮箱账号,设置pass
邮箱授权码,最后返回code
编码和过期时间,代码如下所示:
export default {
dbs: 'mongodb://127.0.0.1:27017/user',
redis: {
get host() {
return '127.0.0.1'
},
get port() {
return 6379
}
},
smtp: {
get host() {
return 'smtp.qq.com'
},
get user() {
return '[email protected]'
},
get pass() {
return 'abcdefg'
},
get code() {
return () => {
// 转换16进制,大写,四位数随机验证码
return Math.random().toString(16).slice(2,6).toUpperCase()
}
},
get expire() {
return () => {
return new Date().getTime()+60*60*1000
}
}
}
}
-
通过
npm i koa-router koa-redis nodemailer
命令下载对应的工具 -
创建
users.js
接口文件,引入koa-router
、koa-redis
、nodemailer
、users
、passport
、config
和axios
,代码如下所示:
import Router from 'koa-router'
import Redis from 'koa-redis'
import nodeMailer from 'nodemailer'
import User from '../dbs/models/users'
import Passport from './untils/passport'
import Email from '../dbs/config'
import axios from './untils/axios'
- 创建接口的前缀,以及
redis
的客户端,代码如下所示:
let router = new Router({
prefix: '/users'
})
let Store = new Redis().client
- 创建注册的接口,从
ctx.request.body
中解构赋值出username
,password
,email
,code
的值。验证验证码,在点击发送验证码以后,会将code
值存储在redis
中,取的时候从redis
中获取。如果验证码存在,从redis
中获取code
和expire
的值。判断验证码是否相等,如果验证码正确,判断时间是否过期, 验证码错误和没有填验证码就进行相应的提示。验证用户名,根据用户名去查找是否有重复的。如果用户名已存在,就提示已被注册。如果用户名不存在,写库,创建新用户,并且进行相应的响应,通过ctx.body
返回对应的code
值和提示信息,代码如下所示:
// 注册的接口
router.post('/signup', async (ctx) => {
const { username, password, email, code} = ctx.request.body;
if (code) {
const saveCode = await Store.hget(`nodemail:${username}`, 'code')
const saveExpire = await Store.hget(`nodemail:${username}`, 'expire')
if (code === saveCode) {
if (new Date().getTime() - saveExpire > 0) {
ctx.body = {
code: -1,
msg: '验证码已过期,请重新尝试'
}
return false
}
} else {
ctx.body = {
code: -1,
msg: '请填写正确的验证码'
}
}
} else {
ctx.body = {
code: -1,
msg: '请填写验证码'
}
}
let user = await User.find({ username })
if (user.length) {
ctx.body = {
code: -1,
msg: '已被注册'
}
return
}
let nuser = await User.create({ username, password, email })
if (nuser) {
let res = await axios.post('/users/signin', { username, password})
if (res.data && res.data.code === 0) {
ctx.body = {
code: 0,
msg: '注册成功',
user: res.data.user
}
} else {
ctx.body = {
code: -1,
msg: 'error'
}
}
} else {
ctx.body = {
code: -1,
msg: '注册失败'
}
}
})
- 创建登录的接口,
passport.authenticate( strategy, options, callback )
, 验证请求用户,返回一个function
,符合funciton(req, res, next)
的middleware
格式。这个仅在登录时验证该登录请求,登录成功后,一般需要配合session
策略,将登录状态保存在session
中,这样每个请求的登录状态就可以通过session
策略来获取了。这种场景,需要确认passport.authenticate
传入的options.session
为true
(默认值),并将passport.session()
注册为middleware
,以便在每个请求时都执行(注册在static
和initialize
之后)。这个中间件会从每个req
的session
中取出’passport
'的值,反序列化出user
,并赋值给req.user
。如果有error
就异常抛出,无error
判断用户是否存在,通过ctx.body
返回对应的code
值和消息提示,代码如下所示:
router.post('/signin', async (ctx, next) => {
return Passport.authenticate('local', function (err, user, info, status ) {
if (err) {
ctx.body = {
code: -1,
msg: err
}
} else {
if (user) {
ctx.body = {
code: 0,
msg: '登录成功',
user
}
return ctx.login(user)
} else {
ctx.body = {
code: 1,
msg: info
}
}
}
})(ctx, next)
})
- 创建验证码验证的接口,从请求中获取
username
的值,从redis
中获取过期时间,存在过期时间并且当前时间小于过期时间,设置不频繁请求。nodeMailer
创建传输方式,发送对象,接收对象,配置发送的邮箱对象。transporter
发送对象去发送邮箱,携带配置对象,err
和info
,有error
报错,无error
存redis
,通过ctx.body
返回对应的code
值和提示信息,代码如下所示:
router.post('/verify', async (ctx, next) => {
let username = ctx.request.body.username
const saveExpire = await Store.hget(`nodemail:${username}`, 'expire')
if (saveExpire && new Date() - saveExpire < 0) {
ctx.body = {
code: -1,
msg: '验证请求过于频繁,1分钟内1次'
}
return false
}
let transporter = nodeMailer.createTransport({
host: Email.smtp.post,
port: 587,
secure: false, // 不使用 SSL 传输
auth: {
user: Email.smtp.user,
pass: Email.smtp.pass
}
})
let ko = {
code: Email.smtp.code(),
expire: Email.smtp.expire(),
email: ctx.request.body.email,
user: ctx.request.body.username
}
let mailOptions = {
from: `"认证邮件" <${Email.smtp.user}>`,
to: ko.email,
subject: '注册码',
html: `您已经注册,您的邀请码是${ko.code}`
}
await transporter.sendMail(mailOptions, (err,info) => {
if (err) {
return console.log('error')
} else {
Store.hmset(`nodemail:${ko.user}`, 'code',ko.code, 'expire',ko.expire, 'email',ko.email )
}
})
ctx.body = {
code: 0,
msg: '验证码已发送,可能会有延时,有效期1分钟'
}
})
- 创建退出的接口,通过
ctx.logout()
执行退出的操作,通过ctx.isAuthenticated()
进行二次校验,看是否还是登录的状态。登录成功,返回code
的值为0
,登录失败,返回code
的值为-1
,代码如下所示:
router.get('/exit', async (ctx, next) => {
await ctx.logout()
if (!ctx.isAuthenticated()) {
ctx.body = {
code: 0
}
} else {
ctx.body = {
code: -1
}
}
})
- 最后通过
export default
对外暴露 接口,代码如下所示:
export default router