Eggjs登錄註冊 & JWT鑑權

JWT

Json web token (JWT), 是爲了在網絡應用環境間傳遞聲明而執行的一種基於JSON的開放標準([(RFC 7519]).該token被設計爲緊湊且安全的,特別適用於分佈式站點的單點登陸(SSO)場景。JWT的聲明通常被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,以便於從資源服務器獲取資源,也能夠增長一些額外的其它業務邏輯所必須的聲明信息,該token也可直接被用於認證,也可被加密。web

JWT認證流程npm

  • 用戶使用用戶名密碼來請求服務器
  • 服務器進行驗證用戶的信息
  • 服務器經過驗證發送給用戶一個token
  • 客戶端存儲token,並在每次請求時附送上這個token值
  • 服務端驗證token值,並返回數據

基於session和基於JWT的方式的主要區別就是用戶的狀態保存的位置,session是保存在服務端的,而JWT是保存在客戶端的。api

一旦簽發一個JWT,在到期以前就會始終有效,沒法中途廢棄。安全

Eggjs 實現簽發、認證 Token

安裝依賴

# jwt
npm i egg-jwt
# 密碼加密
bcryptjs
# 數據校驗
egg-validate
複製代碼

使用插件

config/plugin.jsbash

jwt: {
  enable: true,
  package: 'egg-jwt',
}, 
validate: {
  enable: true,
  package: 'egg-validate',
},
複製代碼

修改配置文件

config/config.default.js服務器

// jwt
config.jwt = {
  secret: '123456',
  expiresIn: '24h',
};
// 參數
config.validate = {
  enable: true,
  package: 'egg-validate',
};
// 密碼加密
config.bcrypt = {
  saltRounds: 10,
};
複製代碼

用戶註冊、登錄接口開發

建立用戶模型

app/model/user.jsmarkdown

'use strict';

module.exports = app => {
  const { STRING, INTEGER, DECIMAL, DATE } = app.Sequelize;

  const User = app.model.define('users', {
    id: { type: INTEGER, primaryKey: true, autoIncrement: true },
    userName: {
      type: STRING,
      allowNull: false,
      unique: true,
      comment: '用戶名,惟一',
    },
    passWord: STRING,
  });

  return User;
};
複製代碼

user Controller

app/controller/user.js網絡

// 校驗用戶註冊參數
const vUser = {
  userName: { type: 'string', required: true },
  passWord: { type: 'string', required: true },
};

//...
class UserController extends Controller {
    // 用戶註冊
  async rigist() {
    const { ctx } = this;
    // 接收並校驗參數
    ctx.validate(vUser, ctx.request.body);
    // 判斷用戶名是否重複
    const users = await ctx.service.user.checkUserName(ctx.request.body);
    if (users[0]) {
      ctx.body = { status: false, msg: '用戶名已存在', data: users };
      return;
    }
    await ctx.service.user.Rigist(ctx.request.body);
    ctx.body = { status: true, msg: '註冊成功' };
  }

  // 用戶登錄
  async login() {
    const { ctx } = this;
    // 接收並校驗參數
    ctx.validate(vUser, ctx.request.body);
    const data = await ctx.service.user.Login(ctx.request.body);
    if (!data) {
      ctx.status = 401;
      ctx.body = { status: false, msg: '用戶名或密碼錯誤' };
      return;
    }
    ctx.body = { status: true, msg: '登錄成功', data };
  }
}

module.exports = UserController;
複製代碼

user Service

app/service/user.jssession

const bcrypt = require('bcryptjs');

// ......


// 檢查用戶名
async checkUserName(query) {
  const { userName } = query;
  const users = await this.ctx.model.User.findAll({
    attributes: [ 'userName' ],
    where: { userName },
  });
  return users;
}


// 用戶註冊
async Rigist(body) {
  const { userName, passWord } = body;
  // 對密碼加密
  const hash = bcrypt.hashSync(passWord, this.config.bcrypt.saltRounds);
  const user = await this.ctx.model.User.create({ userName, passWord: hash });
  return user;
}


// 用戶登錄
async Login(body) {
  const { userName, passWord } = body;
  const user = await this.ctx.model.User.findOne({
    where: { userName },
  });
  if (!user) return {};

  const match = await bcrypt.compare(passWord, user.passWord);
  if (match) {
    const { id, userName } = user;
    // 獲取jwt配置
    const { jwt: { secret, expiresIn } } = this.app.config;
    // 生成token
    const token = this.app.jwt.sign({
      id, userName,
    }, secret, { expiresIn });
    return { userName, token };
  }
}
複製代碼

router

app/router.jsapp

router.post('/api/v1/user/rigist', controller.user.rigist); // 用戶註冊
router.post('/api/v1/user/login', controller.user.login); // 用戶登錄

// 在路由裏添加jwt中間件 便可使用jwt鑑權
router.put('/api/v1/user/:id', app.jwt, controller.user.update); // 修改用戶信息
複製代碼

獲取token內容

若是接口使用了app.jwt 中間件

const userToken = this.ctx.state.user;
// 可獲取登錄時寫入token的內容 id, userName,
複製代碼

若是接口未使用app.jwt中間件 則沒法獲取 this.ctx.state.user;

對與某些接口可登錄也可不登錄,且獲取數據有差別時。

此時須要封裝解析token的方法

app/extend/utils.js

'use strict';

function getTokenInfo(jwt, auth, secret) {
  // 判斷請求頭是否包含token
  if (
    auth.authorization &&
    auth.authorization.split(' ')[0] === 'Bearer'
  ) {
    const token = auth.authorization.split(' ')[1];
    let decode = '';
    if (token) {
      decode = jwt.verify(token, secret);
    }
    return decode;
  }
  return;
}

module.exports = {
  getTokenInfo,
};

複製代碼

在service裏使用

const { jwt: { secret } } = this.app.config;
// 若是存在token 解析token
const authInfo = getTokenInfo(this.ctx.app.jwt, this.ctx.headers, secret);
if (authInfo) {
  // 便可獲取 token 內容
  // authInfo.id authInfo.userName
}
複製代碼
相關文章
相關標籤/搜索