npm i -S mongoose
/server/models/user.js
import { Schema, model } from 'mongoose'
const userModel = new Schema({
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
email: {
type: String,
required: true
}
})
export default model('user', userModel)
npm i -S koa-passport passport-local
import passport from 'koa-passport'
import LocalStrategy from 'passport-local'
import UserModel from '../models/users'
passport.use(
new LocalStrategy(async (username, password, done) => {
const result = await UserModel.findOne({ username })
if (!result) return done(null, false, '用户不存在')
if (result.password !== password) return done(null, false, '密码错误')
return done(null, result)
})
)
// 登陆成功后将用户信息存入session
passport.serializeUser((user, done) => {
done(null, user)
})
// 请求时从session中获取信息
passport.deserializeUser((user, done) => {
return done(null, user)
})
export default passport
npm i -S koa-redis koa-generic-session koa-bodyparser koa-json
export const db = 'mongod://127.0.0.1:27017/demo'
export const smtp = {
get host() {
return 'smtp.qq.com'
},
get user() {
return 'xxxx@qq.com'
},
get pass() {
return ''
},
get code() {
return () => {
return Math.random()
.toString(16)
.slice(2, 6)
.toUpperCase()
}
},
get expire() {
return () => {
return new Date().getTime() + 60 * 1000
}
}
}
import { connect } from 'mongoose'
import bodyParser from 'koa-bodyparser'
import json from 'koa-redis'
import session from 'koa-generic-session'
import redis from 'koa-redis'
import {db} from './utils/config'
import passport from './utils/passport'
app.keys = ['demo', 'keyskeys']
app.use(
session({
key: 'app',
prefix: 'app:uid',
store: new redis()
})
)
app.use(
bodyParser({
extendTypes: ['json', 'form', 'text']
})
)
app.use(json())
connect(
dbConfig.dbs,
{
useNewUrlParser: true
}
)
app.use(passport.initialize())
app.use(passport.session())
npm i -S axios nodemailer
import Router from 'koa-router'
import Redis from 'koa-redis'
import nodeMailer from 'nodemailer'
import passport from '../utils/passport'
import UserModel from '../models/users'
import { smtp } from '../utils/config'
const Store = new Redis().client
const router = new Router({
prefix: '/users'
})
// 错误提示
const maps = new Map([
['code.required', '请输入验证码'],
['code.expire', '验证码已过期'],
['code.error', '验证码不正确'],
['user.exist', '用户已注册'],
['register.error', '注册失败'],
['user.notlogin', '用户未登录'],
['verify.over', '1分钟内只能发送一次']
])
// 错误集中处理
const errorHandler = (ctx, key, msg = null) => {
if (msg)
return (ctx.body = {
code: -1,
msg
})
if (maps.has(key)) {
return (ctx.body = {
code: -1,
msg: maps.get(key)
})
}
}
router.post('/signup', async ctx => {
const { username, password, email, code } = ctx.request.body
if (!code) return errorHandler(ctx, 'code.required')
const redisExpire = Store.hget(`nodemail:${username}`, 'expire')
if (new Date().getTime() - redisExpire < 0) return errorHandler(ctx, 'code.expire')
const redisCode = Store.hget(`nodemail:${username}`, 'code')
if (code !== redisCode) return errorHandler(ctx, 'code.error')
const user = await UserModel.find({ username })
if (user.length > 0) return errorHandler(ctx, 'user.exist')
const newUser = await UserModel.create({
username,
password,
email
})
if (!newUser) return errorHandler(ctx, 'register.error')
return (ctx.body = {
code: 0,
msg: '注册成功'
})
})
router.post('/signin', async ctx => {
return passport.authenticate('local', async (err, user, info, status) => {
if (err) return errorHandler(ctx, null, err)
if (user) {
ctx.body = {
code: 0,
msg: '登录成功',
user
}
return ctx.login(user)
}
ctx.body = {
code: 1,
msg: info
}
})(ctx)
})
router.post('/verify', async ctx => {
const { username, email } = ctx.request.body
const redisExpire = Store.hget(`nodemail:${username}`, 'expire')
if (redisExpire && new Date().getTime() - redisExpire < 0) return errorHandler(ctx, 'verify.over')
const { host, user, pass, code, expire } = smtp
const transporter = nodeMailer.createTransport({
host,
port: 587,
secure: false,
auth: {
user,
pass
}
})
const info = {
code: code(),
expire: expire()
}
const emailOptions = {
from: `"发件人"<${user}>`,
to: email,
subject: `测试邀请码`,
html: `您的邀请码是: ${info.code}`
}
await transporter.sendMail(emailOptions, err => {
if (err) return console.log('邮件发送失败!')
Store.hmset(`nodemail:${username}`, 'code', info.code, 'expire', info.expire)
})
ctx.body = {
code: 0,
msg: '验证已发送,有效期1分钟'
}
})
router.get('/exit', async ctx => {
await ctx.logout()
const result = ctx.isAuthenticated()
ctx.body = {
code: result ? -1 : 0,
msg: `退出${result ? '失败' : '成功'}`
}
})
router.get('/getUser', async ctx => {
if (!ctx.isAuthenticated()) return errorHandler(ctx, 'user.notlogin')
const { username, email } = ctx.session.passport.user
ctx.body = {
code: 0,
user: username,
email
}
})
export default router
import users from './routers/user'
app.use(users.routes()).use(users.allowedMethods())