egg.js+redis+postgresql实现一套基于jwt的sso单点登录系统demo

Bougie 2019-06-28

最终目的

实现一套基于jwt方案的单点登录系统,可以用于平时自身接外包做项目。

整体逻辑

egg.js+redis+postgresql实现一套基于jwt的sso单点登录系统demo

技术准备

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
    }
}

注意点

  1. 一定要改默认端口!一定要改默认端口!一定要改默认端口! 请看:Redis 未授权访问缺陷可轻易导致系统被黑,请修改redis.conf: requirepass(密码) , port(端口)
  2. 可能会遇到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+redis+postgresql实现一套基于jwt的sso单点登录系统demo

前端业务逻辑

egg.js+redis+postgresql实现一套基于jwt的sso单点登录系统demo

中间件

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']
    }

egg.js+redis+postgresql实现一套基于jwt的sso单点登录系统demo

相关推荐