最终目的
实现一套基于jwt方案的单点登录系统,可以用于平时自身接外包做项目。
整体逻辑
技术准备
egg.js
前置知识
1.eggjs基于koa2
,可以认为是koa2的框架层面的约束,需要有koa2基础,可以参考koa2文档
2.关于koa2洋葱圈模型的解析可以看这里
3.node版本8.x
,可以很方便地使用async/await来写异步代码
4.egg.js官方文档
目录结构以及框架约定
官方文档推荐传送门,不过我们用不到这么多,一切以需求为主,只介绍用的到的地方,其他的功能可以以后慢慢摸索
├── package.json
├── app
| ├── router.js
│ ├── controller
│ | └── user.js
│ ├── service
│ | └── user.js
│ ├── middleware
│ | └── checkToken.js
│ └── model
│ └── user.js
├── config
| ├── plugin.js
| ├── config.default.js
以上就是框架约定的目录,由于我们前端分离,所以view目录也不需要了:
- app/router.js 用于配置 URL 路由规则。
- app/controller/** 用于解析用户的输入,处理后返回相应的结果。
- app/service/** 用于编写业务逻辑层。
- app/middleware/** 用于编写中间件。
- config/config.{env}.js 用于编写配置文件。
- config/plugin.js 用于配置需要加载的插件。
tips
修改cors
修改plugin.js
exports.cors = {
enable: true,
package: 'egg-cors',
}
修改config.default.js
exports.security = {
csrf: {
enable: false,
ignoreJSON: true
},
domainWhiteList: ['*']
}
exports.cors = {
origin: '*',
allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH'
}
在egg.js中使用redis
安装请看redis在mac下的安装,其他操作系统请根据口味自行百度。
使用请参考egg-redis文档
Redis是什么
yarn add egg-redis
修改plugin.js
exports.redis = {
enable: true,
package: 'egg-redis',
}
修改config.default.js
exports.redis = {
client: {
port: [port],
host: '127.0.0.1',
password: [password],
db: 0
}
}
注意点
-
一定要改默认端口!一定要改默认端口!一定要改默认端口!
请看:Redis 未授权访问缺陷可轻易导致系统被黑,请修改redis.conf:requirepass
(密码) ,port
(端口) - 可能会遇到redis快照关闭导致无法写入数据的情况,会报错,类似:
MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. Commands that may modify the data set are disabled, because this instance is configured to report errors during writes if RDB snapshotting fails (stop-writes-on-bgsave-error option). Please check the Redis logs for details about the RDB error.
解决方案是修改redis.conf:stop-writes-on-bgsave-error
改为no
在egg.js中使用postgresql
安装请看postgresql在mac下的安装,其他操作系统请根据口味自行百度。
使用请参考egg-sequelize文档
PostgreSQL 与 MySQL 相比,优势何在?
yarn add egg-sequelize
yarn add pg pg-hstore
修改plugin.js
exports.sequelize = {
enable: true,
package: 'egg-sequelize'
}
修改config.default.js
exports.sequelize = {
dialect: 'postgres',
database: 'postgres',
host: 'localhost',
port: '8888',
username: 'postgres',
password: '123456'
}
生成token
yarn add jsonwebtoken
var jwt = require('jsonwebtoken');
var tokenKey = 'token key'
var token = jwt.sign({ foo: 'bar' }, tokenKey);
var decoded = jwt.verify(token, tokenKey);
console.log(decoded.foo) // bar
后端业务逻辑
前端业务逻辑
中间件
egg.js是基于Koa2的,可以非常容易的引入 Koa 中间件生态。
在我们这个应用中,不是所有的请求都需要验证token,所以可以通过中间件来处理,下面我们就来写一个中间件。
写法
app/model/checkToken.js
const { verify } = require('jsonwebtoken')
const moment = require('moment')
module.exports = options => {
return async function checkToken(ctx, next) {
const { jwtKey } = ctx.app.config.appConfig
const {request: { path, header: {token} }} = ctx
const {exclude=[]} = options
let decodedJwt = {}
try {
if (exclude.indexOf(path.replace('/', '')) === -1) { // 需要token的接口
decodedJwt = verify(token, jwtKey)
// token exp 超时
if(moment().isAfter(decodedJwt.exp)) {
throw {
code: -340,
msg: 'token 过期'
}
}
} else { //不需要token的接口
decodedJwt.exp = -1
}
} catch (error) {
ctx.app.logger.error('token error', error)
if (error.code) {
ctx.body = error
} else {
ctx.body = {
code: -360,
msg: 'token 错误'
}
}
}
if (decodedJwt.exp) {
await next()
}
}
}
配置
修改config.default.js
exports.middleware = ['checkToken'] // 中间件会按顺序执行
// 中间件需要的配置项,可以通过app.config[${middlewareName}]访问
exports.checkToken = {
exclude: ['login', 'signup']
}