美文网首页
eggjs中的passport实践

eggjs中的passport实践

作者: 无涯老人 | 来源:发表于2019-07-25 10:56 被阅读0次

eggjs是阿里开源的一个针对企业级应用的nodejs框架,最近在学习eggjs中的passport,走了一些弯路,写一点心得,使用local策略实现本地登录。

安装相关的依赖

npm i --save egg-passport egg-passport-local

开启插件

config\plugin.js

exports.passport = {
  enable: true,
  package: 'egg-passport',
};

exports.passportLocal = {
  enable: true,
  package: 'egg-passport-local',
};

配置用户名密码字段

config\config.default.js

  config.passportLocal = {
    usernameField: 'username',
    passwordField: 'password',
  };

配置验证用户以及序列化与反序列化

app.js中,添加逻辑

  app.passport.verify(async (ctx, user) => {
    // 查找数据库,并验证密码是否正确
    /*
    const User = ctx.model.User;
    let foundUser = await User.findOne({ username: user.username });
    if(!foundUser || !foundUser.encryptPassword(user.password)) return false;
    return foundUser;
    */
    return user;
  });

  // 序列化与反序列化,序列化存储到session中只保存用户id
  app.passport.serializeUser(async (ctx, user) => {
    // return pick(user, ['id', 'name', 'username', 'email']);
    return user;
  });

  app.passport.deserializeUser(async (ctx, user) => {
    return user;
  });

创建控制路由

controller文件夹中,创建home.js,添加控制路由

const Controller = require('egg').Controller;

class HomeController extends Controller {
  async index() {
    const ctx = this.ctx;
    ctx.body = `
      <div>
        <h2>${ctx.path}</h2>
        <a href="/admin">admin</a>
      </div>
    `;
  }

  async admin() {
    const { ctx } = this;
    if (ctx.isAuthenticated()) {
      console.log('ctx.user', ctx.user);
      // show user info
      ctx.body = `
        <div>
          <h3>You login</h3>
          <a href="/logout">Logout</a>
        </div>
      `;
      
    } else {
      // redirect to origin url by ctx.session.returnTo
      ctx.session.returnTo = ctx.path;
      // await ctx.render('login.html');
      ctx.redirect('/login');
    }
  }

  async login() {
    await this.ctx.render('login.html');
  }

  async logout() {
    const ctx = this.ctx;
    ctx.logout();
    ctx.redirect(ctx.get('referer') || '/');
  }

  async register() {
    await this.ctx.render('register.html');
  }

  async doRegister() {
    const ctx = this.ctx;
    const User = ctx.model.User;
    console.log('model is ', User);
    const requestBody = ctx.request.body;
    const user = new User(requestBody);
    user.provider = 'local';
    const result = await user.save();
    console.log('result is ', result);
    ctx.redirect('/');
  }

  async loginCallback() {
    let { ctx } = this;
    if (ctx.isAuthenticated()) {
      ctx.body = {
        code: 0,
        data: ctx.user,
        msg: '',
      };
    } else {
      ctx.body = {
        code: -1, 
        data: null,
        msg: '用户名或密码错误'
      }
    }

  }
}

module.exports = HomeController;

配置路由

router.js中,配置

module.exports = app => {
  const { router, controller } = app;

  let { home } = controller;

  router.get('/', home.index);
  router.get('/admin', home.admin);
  router.get('/logout', home.logout);
  router.get('/login', home.login);

  const localStrategy = app.passport.authenticate('local', {successRedirect: '/loginCallback', failureRedirect: '/loginCallback'});
  router.post('/passport/local', localStrategy);
  router.post('/passport/local', home.localPassport);
  router.get('/loginCallback', home.loginCallback);
  
  router.get('/register', home.register);
  router.post('/register', home.doRegister);
};

在这里,localStrategy的定义中,添加了 successRedirectfailureRedirect并且指向了同一个路由,在loginCallback中,判断是否登录,并返回对应的json数据,这种是为了处理ajax请求的情况。

app.passport.authenticate('local', {successRedirect: '/loginCallback', failureRedirect: '/loginCallback'})

前端代码

app/view
login.html

<html>
  <head>
    <title>Login</title>
    <script type="text/javascript" src="/public/lib/js.cookie.js"></script>
    <script type="text/javascript" src="/public/lib/jquery-1.12.4.min.js"></script>
    <script>
    $(function(){
    // 对于所有的ajax请求,请求头添加cookie标识
    var csrftoken = Cookies.get('csrfToken');

    function csrfSafeMethod(method) {
      // these HTTP methods do not require CSRF protection
      return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }
    
    $.ajaxSetup({
      beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
          xhr.setRequestHeader('x-csrf-token', csrftoken);
          }
        },
      });
    });
    </script>
  </head>
  <body>
    <form method="post" id="loginForm">
      <input type="text" name="username" placeholder="用户"/>
      <input type="password" name="password" placeholder="密码"/>
      <input type="submit" value="提交"/>
    </form>
    <div>
      没有账号?<a href="/register">注册</a>
    </div>
  </body>
  <script type="text/javascript">
    $('#loginForm').on('submit', function(e) {
      e.preventDefault();
      let username = this.username.value.trim();
      let password = this.password.value.trim();
      $.ajax({
        url: '/passport/local',
        method: 'post',
        data: {
          username: username,
          password: password
        }
      }).then((res, statusCode, xx) => {
        console.log('res ', res, statusCode, xx);
        console.log()
      }, error => {
        if(error.status === 401) {
          alert("账号或密码错误");
        }
        console.warn('error ', error);
      })
    });
  </script>
</html>

Done!

相关文章

网友评论

      本文标题:eggjs中的passport实践

      本文链接:https://www.haomeiwen.com/subject/lsjfrctx.html