Quellcode durchsuchen

用户登录权限

liuyu vor 5 Jahren
Ursprung
Commit
083466aee9

+ 12 - 0
.travis.yml

@@ -0,0 +1,12 @@
+
+language: node_js
+node_js:
+  - '10'
+before_install:
+  - npm i npminstall -g
+install:
+  - npminstall
+script:
+  - npm run ci
+after_script:
+  - npminstall codecov && codecov

+ 1 - 1
.vscode/settings.json

@@ -1,3 +1,3 @@
 {
-    "eggHelper.serverPort": 35684
+    "eggHelper.serverPort": 37079
 }

+ 42 - 0
app/controller/.role.js

@@ -0,0 +1,42 @@
+module.exports = {
+  create: {
+    requestBody: [
+      'code',
+      '!role_name',
+      'url'
+    ]
+  },
+  destroy: {
+    params: ['!id'],
+    service: 'delete'
+  },
+  update: {
+    params: ['!id'],
+    requestBody: [
+      'code',
+      '!role_name',
+      'url'
+    ]
+  },
+  show: {
+    parameters: {
+      params: ['!id']
+    },
+    service: 'fetch'
+  },
+  index: {
+    parameters: {
+      query: {
+        name:'%role_name%',
+        url:'code'
+      }
+    },
+    service: 'query',
+    options: {
+      query: ['skip', 'limit'],
+      sort: ['meta.createdAt'],
+      desc: true,
+      count: true
+    }
+  },
+};

+ 51 - 0
app/controller/.user.js

@@ -0,0 +1,51 @@
+module.exports = {
+  create: {
+    requestBody: [
+      '!name',
+      '!mobile',
+      '!passwd',
+      'openid',
+      'roles',
+      'uid',
+      'remark'
+    ]
+  },
+  destroy: {
+    params: ['!id'],
+    service: 'delete'
+  },
+  update: {
+    params: ['!id'],
+    requestBody: [
+      'name',
+      'mobile',
+      'passwd',
+      'openid',
+      'uid',
+      'roles',
+      'remark'
+    ]
+  },
+  show: {
+    parameters: {
+      params: ['!id']
+    },
+    service: 'fetch'
+  },
+  index: {
+    parameters: {
+      query: {
+        name:'%name%',
+        uid: 'uid',
+        mobile:'mobile'
+      }
+    },
+    service: 'query',
+    options: {
+      query: ['skip', 'limit'],
+      sort: ['meta.createdAt'],
+      desc: true,
+      count: true
+    }
+  },
+};

+ 35 - 0
app/controller/login.js

@@ -0,0 +1,35 @@
+'use strict';
+
+const _ = require('lodash');
+const Controller = require('egg').Controller;
+
+// 登录管理
+class LoginController extends Controller {
+
+  constructor(ctx) {
+    super(ctx);
+    this.service = this.ctx.service.login;
+  }
+
+  async login() {
+    const res = await this.service.login(this.ctx.request.body);
+    this.ctx.ok({ data: res });
+  }
+
+  async token() {
+    const res = await this.service.token(this.ctx.request.body);
+    this.ctx.ok({ data: res });
+  }
+
+  async destroy() {
+    const res = await this.service.destroy(this.ctx.request.body);
+    this.ctx.ok({ data: res });
+  }
+
+  async wxlogin() {
+    const res = await this.service.wxlogin(this.ctx.request.body);
+    this.ctx.ok({ data: res });
+  }
+}
+
+module.exports = LoginController;

+ 18 - 0
app/controller/role.js

@@ -0,0 +1,18 @@
+'use strict';
+
+const _ = require('lodash');
+const meta = require('./.role.js');
+const Controller = require('egg').Controller;
+const { CrudController } = require('naf-framework-mongoose/lib/controller');
+
+// 管理
+class RoleController extends Controller {
+
+  constructor(ctx) {
+    super(ctx);
+    this.service = this.ctx.service.role;
+  }
+
+}
+
+module.exports = CrudController(RoleController, meta);

+ 22 - 0
app/controller/user.js

@@ -0,0 +1,22 @@
+'use strict';
+
+const _ = require('lodash');
+const meta = require('./.user.js');
+const Controller = require('egg').Controller;
+const { CrudController } = require('naf-framework-mongoose/lib/controller');
+
+// 管理
+class UserController extends Controller {
+
+  constructor(ctx) {
+    super(ctx);
+    this.service = this.ctx.service.user;
+  }
+
+  async uppasswd() {
+    const res = await this.service.uppasswd(this.ctx.request.body);
+    this.ctx.ok({ data: res });
+  }
+}
+
+module.exports = CrudController(UserController, meta);

+ 21 - 0
app/model/role.js

@@ -0,0 +1,21 @@
+'use strict';
+const Schema = require('mongoose').Schema;
+const metaPlugin = require('naf-framework-mongoose/lib/model/meta-plugin');
+
+// 权限表
+const RoleSchema = {
+  code: { type: String, required: false, maxLength: 200 }, // 权限CODE
+  role_name: { type: String, required: true, maxLength: 64 }, // 权限名称
+  url: { type: String, required: false }, // 权限路由
+};
+
+
+const schema = new Schema(RoleSchema, { toJSON: { virtuals: true } });
+schema.index({ id: 1 });
+schema.index({ code: 1 });
+schema.plugin(metaPlugin);
+
+module.exports = app => {
+  const { mongoose } = app;
+  return mongoose.model('Role', schema, 'role');
+};

+ 26 - 0
app/model/user.js

@@ -0,0 +1,26 @@
+'use strict';
+const Schema = require('mongoose').Schema;
+const metaPlugin = require('naf-framework-mongoose/lib/model/meta-plugin');
+const { Secret } = require('naf-framework-mongoose/lib/model/schema');
+
+// 用户表
+const UserSchema = {
+  name: { type: String, required: false, maxLength: 200 }, // 名称
+  mobile: { type: String, required: true, maxLength: 64 }, // 手机
+  passwd: { type: Secret, select: false }, // 注册密码
+  openid: { type: String, required: false }, // 微信openid
+  uid: { type: String, required: false }, // 用户信息id
+  roles: { type: [ String ], required: false }, // 权限
+  remark: { type: String, required: false }, // 备注
+};
+
+
+const schema = new Schema(UserSchema, { toJSON: { virtuals: true } });
+schema.index({ openid: 1 });
+schema.index({ mobile: 1 });
+schema.plugin(metaPlugin);
+
+module.exports = app => {
+  const { mongoose } = app;
+  return mongoose.model('User', schema, 'user');
+};

+ 16 - 0
app/router.js

@@ -6,4 +6,20 @@
 module.exports = app => {
   const { router, controller } = app;
   router.get('/', controller.home.index);
+
+  // 权限表设置路由
+  router.resources('role', '/api/auth/role', controller.user); // index、create、show、destroy
+  router.post('role', '/api/auth/role/update/:id', controller.user.update);
+
+  // 用户表设置路由
+  router.resources('user', '/api/auth/user', controller.user); // index、create、show、destroy
+  router.post('user', '/api/auth/user/update/:id', controller.user.update);
+  router.post('user', '/api/auth/user/uppasswd', controller.user.uppasswd);
+
+  // 用户登录
+  router.post('/api/auth/login', controller.login.login);
+  // 根据token取得用户信息
+  router.post('/api/auth/token', controller.login.token);
+  // 用户退出登录
+  router.post('/api/auth/logout', controller.login.destroy);
 };

+ 78 - 0
app/service/login.js

@@ -0,0 +1,78 @@
+'use strict';
+
+const assert = require('assert');
+const _ = require('lodash');
+const { CrudService } = require('naf-framework-mongoose/lib/service');
+const { BusinessError, ErrorCode } = require('naf-core').Error;
+const jwt = require('jsonwebtoken');
+const uuid = require('uuid');
+
+class LoginService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'login');
+    this.model = this.ctx.model.User;
+    this.rmodel = this.ctx.model.Role;
+  }
+
+  // 用户登录
+  async login(data) {
+    const { mobile, passwd } = data;
+    // 根据用户输入的手机号查询其他用户表中是否存在相应数据
+    const user = await this.model.findOne({ mobile });
+    // 如果用户不存在抛出异常
+    if (!user) {
+      throw new BusinessError(ErrorCode.USER_NOT_EXIST);
+    }
+    const _user = await this.model.findOne({ mobile }, '+passwd');
+    // 将用户输入的密码进行加密并与查询到的用户数据密码相比对
+    const pas = await this.createJwtPwd(passwd);
+    // 如果两个密码不一致抛出异常
+    if (pas !== _user.passwd.secret) {
+      throw new BusinessError(ErrorCode.BAD_PASSWORD);
+    }
+    // 取出用户的类型,根据用户类型返回相应信息
+    const state = uuid();
+    const key = `free:auth:state:${state}`;
+    const token = await this.createJwt(user);
+    console.log('key--' + key);
+    console.log('token--' + token);
+    await this.app.redis.set(key, token, 'EX', 60 * 60 * 24);
+    return { key };
+  }
+
+  // 创建登录Token
+  async createJwtPwd(password) {
+    const { secret, expiresIn, issuer } = this.config.jwt;
+    const token = await jwt.sign(password, secret);
+    return token;
+  }
+
+  // 创建登录Token
+  async createJwt({ id, name, mobile, roles, remark, openid }) {
+    const { secret, expiresIn = '1d', issuer = type } = this.config.jwt;
+    const subject = mobile;
+    const res = { uid: id, name, mobile, roles, openid, remark };
+    const token = await jwt.sign(res, secret, { expiresIn, issuer, subject });
+    return token;
+  }
+
+  // 取得redis内token信息
+  async token({ key }) {
+    assert(key, 'key不能为空');
+    const token = await this.app.redis.get(key);
+    if (!token) {
+      throw new BusinessError(ErrorCode.SERVICE_FAULT, 'token已经过期');
+    }
+    return { token };
+  }
+
+  // 删除操作
+  async destroy({ key }) {
+    const res = await this.app.redis.del(key);
+    console.log(res);
+    return res;
+  }
+
+
+}
+module.exports = LoginService;

+ 17 - 0
app/service/role.js

@@ -0,0 +1,17 @@
+'use strict';
+
+
+const assert = require('assert');
+const _ = require('lodash');
+const { ObjectId } = require('mongoose').Types;
+const { CrudService } = require('naf-framework-mongoose/lib/service');
+const { BusinessError, ErrorCode } = require('naf-core').Error;
+
+class RoleService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'role');
+    this.model = this.ctx.model.Role;
+  }
+}
+
+module.exports = RoleService;

+ 93 - 0
app/service/user.js

@@ -0,0 +1,93 @@
+'use strict';
+
+const assert = require('assert');
+const _ = require('lodash');
+const { ObjectId } = require('mongoose').Types;
+const { CrudService } = require('naf-framework-mongoose/lib/service');
+const { BusinessError, ErrorCode } = require('naf-core').Error;
+const jwt = require('jsonwebtoken');
+
+class UserService extends CrudService {
+  constructor(ctx) {
+    super(ctx, 'user');
+    this.model = this.ctx.model.User;
+    this.rmodel = this.ctx.model.Role;
+  }
+
+  // 重写创建方法
+  async create(data) {
+    const { name, mobile, passwd } = data;
+    console.log(data);
+    assert(name && mobile && passwd, '缺少部分信息项');
+    assert(/^\d{11}$/i.test(mobile), 'mobile无效');
+    const user = await this.model.findOne({ mobile });
+    if (user) {
+      throw new BusinessError(ErrorCode.DATA_EXISTED);
+    }
+    const newdata = data;
+    const pas = await this.createJwtPwd(passwd);
+    newdata.passwd = { secret: pas };
+    const res = await this.model.create(newdata);
+    return res;
+  }
+
+  // 创建登录Token
+  async createJwtPwd(password) {
+    const { secret, expiresIn, issuer } = this.config.jwt;
+    const token = await jwt.sign(password, secret);
+    return token;
+  }
+
+  // 重写修改方法
+  async update({ id }, data) {
+    const { name, mobile, passwd, openid, roles, remark, uid } = data;
+    const user = await this.model.findById(id, '+passwd');
+    if (name) {
+      user.name = name;
+    }
+    if (mobile) {
+      user.mobile = mobile;
+    }
+    if (passwd) {
+      const newpasswd = await this.createJwtPwd(passwd);
+      user.passwd = { secret: newpasswd };
+    }
+    if (openid) {
+      user.openid = openid;
+    }
+    if (roles) {
+      user.roles = roles;
+    }
+    if (uid) {
+      user.uid = uid;
+    }
+    if (remark) {
+      user.remark = remark;
+    }
+    await user.save();
+  }
+
+  // 用户修改密码
+  async uppasswd(data) {
+    const { id, oldpasswd, newpasswd } = data;
+    assert(id && oldpasswd && newpasswd, '缺少部分信息项');
+    // 根据用户id查询其他用户表中是否存在相应数据
+    const user = await this.model.findById(id, '+passwd');
+    // 如果用户不存在抛出异常
+    if (!user) {
+      throw new BusinessError(ErrorCode.USER_NOT_EXIST);
+    }
+    // 将用户输入的密码进行加密并与查询到的用户数据密码相比对
+    const _oldpasswd = await this.createJwtPwd(oldpasswd);
+    // 如果两个密码不一致抛出异常
+    if (_oldpasswd !== user.passwd.secret) {
+      throw new BusinessError(ErrorCode.BAD_PASSWORD);
+    }
+    const _newpasswd = await this.createJwtPwd(newpasswd);
+    user.passwd = { secret: _newpasswd };
+    await user.save();
+  }
+
+}
+
+module.exports = UserService;

+ 14 - 0
appveyor.yml

@@ -0,0 +1,14 @@
+environment:
+  matrix:
+    - nodejs_version: '10'
+
+install:
+  - ps: Install-Product node $env:nodejs_version
+  - npm i npminstall && node_modules\.bin\npminstall
+
+test_script:
+  - node --version
+  - npm --version
+  - npm run test
+
+build: off

+ 45 - 0
config/config.default.js

@@ -2,6 +2,8 @@
 
 'use strict';
 
+const { jwt } = require('./config.secret');
+
 /**
  * @param {Egg.EggAppInfo} appInfo app info
  */
@@ -23,6 +25,49 @@ module.exports = appInfo => {
     // myAppName: 'egg',
   };
 
+  // add your config here
+  config.cluster = {
+    listen: {
+      port: 9000,
+    },
+  };
+
+  config.mongoose = {
+    url: 'mongodb://localhost:27017/platform',
+    options: {
+      user: 'admin',
+      pass: 'admin',
+      authSource: 'admin',
+      useNewUrlParser: true,
+      useCreateIndex: true,
+      useUnifiedTopology: true,
+    },
+  };
+
+  // redis config
+  config.redis = {
+    client: {
+      port: 6379, // Redis port
+      host: '127.0.0.1', // Redis host
+      password: 123456,
+      db: 0,
+    },
+  };
+
+  // 安全配置
+  config.security = {
+    csrf: {
+      // ignoreJSON: true, // 默认为 false,当设置为 true 时,将会放过所有 content-type 为 `application/json` 的请求
+      enable: false,
+    },
+  };
+  // // JWT config
+  config.jwt = {
+    ...jwt,
+    expiresIn: '1d',
+    issuer: 'jobs',
+  };
+
   return {
     ...config,
     ...userConfig,

+ 24 - 0
config/config.local.js

@@ -0,0 +1,24 @@
+'use strict';
+
+module.exports = () => {
+  const config = (exports = {});
+
+  config.logger = {
+    level: 'DEBUG',
+    consoleLevel: 'DEBUG',
+  };
+
+  config.mongoose = {
+    url: 'mongodb://localhost:27017/platform',
+    options: {
+      user: 'demo',
+      pass: 'demo',
+      authSource: 'admin',
+      useNewUrlParser: true,
+      useCreateIndex: true,
+      useUnifiedTopology: true,
+    },
+  };
+
+  return config;
+};

+ 34 - 0
config/config.prod.js

@@ -0,0 +1,34 @@
+'use strict';
+
+module.exports = () => {
+  const config = exports = {};
+
+  config.logger = {
+    // level: 'DEBUG',
+    // consoleLevel: 'DEBUG',
+  };
+
+  config.mongoose = {
+    url: 'mongodb://localhost:27017/platform',
+    options: {
+      user: 'admin',
+      pass: 'admin',
+      authSource: 'admin',
+      useNewUrlParser: true,
+      useCreateIndex: true,
+      useUnifiedTopology: true,
+    },
+  };
+
+  // redis config
+  config.redis = {
+    client: {
+      port: 6379, // Redis port
+      host: '127.0.0.1', // Redis host
+      password: 123456,
+      db: 0,
+    },
+  };
+
+  return config;
+};

+ 7 - 0
config/config.secret.js

@@ -0,0 +1,7 @@
+'use strict';
+
+module.exports = {
+  jwt: {
+    secret: 'Ziyouyanfa!@#',
+  },
+};

+ 7 - 6
config/plugin.js

@@ -1,9 +1,10 @@
 'use strict';
 
-/** @type Egg.EggPlugin */
-module.exports = {
-  // had enabled by egg
-  // static: {
-  //   enable: true,
-  // }
+exports.multiTenancy = {
+  enable: false,
+};
+
+exports.redis = {
+  enable: true,
+  package: 'egg-redis',
 };

+ 17 - 0
ecosystem.config.js

@@ -0,0 +1,17 @@
+'use strict';
+
+const app = 'service-auth';
+module.exports = {
+  apps: [{
+    name: app, // 应用名称
+    script: './server.js', // 实际启动脚本
+    out: `./logs/${app}.log`,
+    error: `./logs/${app}.err`,
+    watch: [ // 监控变化的目录,一旦变化,自动重启
+      'app', 'config',
+    ],
+    env: {
+      NODE_ENV: 'production', // 环境参数,当前指定为生产环境
+    },
+  }],
+};

+ 14 - 3
package.json

@@ -4,11 +4,22 @@
   "description": "",
   "private": true,
   "egg": {
-    "declarations": true
+    "framework": "naf-framework-mongoose"
   },
   "dependencies": {
-    "egg": "^2.15.1",
-    "egg-scripts": "^2.11.0"
+    "egg": "^2.23.0",
+    "egg-scripts": "^2.11.0",
+    "jsonwebtoken": "^8.5.1",
+    "lodash": "^4.17.15",
+    "moment": "^2.24.0",
+    "naf-framework-mongoose": "^0.6.11",
+    "silly-datetime": "^0.1.2",
+    "string-random": "^0.1.3",
+    "url-join": "^4.0.1",
+    "uuid": "^3.3.3",
+    "xlsx": "^0.15.1",
+    "egg-redis": "^2.4.0",
+    "xmlreader": "^0.2.3"
   },
   "devDependencies": {
     "autod": "^3.0.1",

+ 9 - 0
server.js

@@ -0,0 +1,9 @@
+
+// eslint-disable-next-line strict
+const egg = require('egg');
+
+const workers = Number(process.argv[2] || require('os').cpus().length);
+egg.startCluster({
+  workers,
+  baseDir: __dirname,
+});