自定义身份验证策略

FeatusJS的Auk版本包括一个强大的新的 认证 建立在 PassportJS 之上.新插件非常灵活,允许您自定义几乎所有内容.我们可以利用它来使用 Passport Custom 创建完全自定义的身份验证策略.我们来看看本指南中的两个这样的例子.

设置基本应用程序

让我们首先创建一个基本的服务器设置.

const feathers = require('@feathersjs/feathers');
const express = require('@feathersjs/express');
const auth = require('@feathersjs/authentication');
const jwt = require('@feathersjs/authentication-jwt');
const memory = require('feathers-memory');

const app = express(feathers());

app.configure(express.rest());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.configure(auth({ secret: 'secret' }));
app.configure(jwt());
app.use('/users', memory());

app.hooks({
  before: {
    all: [auth.hooks.authenticate('jwt')]
  }
});

app.listen(8080);

创建自定义API密钥身份验证策略

我们可以看到的第一个自定义策略示例是API密钥策略.在其中,我们将检查请求中是否存在包含特定API密钥的特定标头.如果为true,我们将成功授权该请求.

First let’s make the strategy using passport-custom npm package.

const Strategy = require('passport-custom');

module.exports = opts => {
  return function() {
    const verifier = (req, done) => {

      // get the key from the request header supplied in opts
      const key = req.params.headers[opts.header];

      // check if the key is in the allowed keys supplied in opts
      const match = opts.allowedKeys.includes(key);

      // user will default to false if no key is present
      // and the authorization will fail
      const user = match ? 'api' : match;
      return done(null, user);
    };

    // register the strategy in the app.passport instance
    this.passport.use('apiKey', new Strategy(verifier));
    // Add options for the strategy
    this.passport.options('apiKey', {});
  };
};

接下来让我们将它添加到我们的服务器设置中

const apiKey = require('./apiKey');

app.configure(
  apiKey({
    // which header to look at
    header: 'x-api-key',
    // which keys are allowed
    allowedKeys: ['opensesame']
  })
);

接下来让我们创建一个自定义身份验证挂钩,有条件地为所有外部请求应用auth.

const commonHooks = require('feathers-hooks-common');

const authenticate = () =>
  commonHooks.iff(
    // if and only if the request is external
    commonHooks.every(commonHooks.isProvider('external')),
    commonHooks.iffElse(
      // if the specific header is included
      ctx => ctx.params.headers['x-api-key'],
      // authentication with this strategy
      auth.hooks.authenticate('apiKey'),
      // else fallback on the jwt strategy
      auth.hooks.authenticate(['jwt'])
    )
  );

app.hooks({
  before: {
    all: [authenticate()]
  }
});

最后我们的 server.js 看起来像这样:

const feathers = require('@feathersjs/feathers');
const express = require('@feathersjs/express');
const auth = require('@feathersjs/authentication');
const jwt = require('@feathersjs/authentication-jwt');
const memory = require('feathers-memory');
const commonHooks = require('feathers-hooks-common');

const apiKey = require('./apiKey');

const app = express(feathers());

app.configure(express.rest());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.configure(auth({ secret: 'secret' }));
app.configure(jwt());
app.configure(
  apiKey({
    header: 'x-api-key',
    allowedKeys: ['opensesame']
  })
);

app.use('/users', memory());

const authenticate = () =>
  commonHooks.iff(
    commonHooks.every(commonHooks.isProvider('external')),
    commonHooks.iffElse(
      ctx => ctx.params.headers['x-api-key'],
      auth.hooks.authenticate('apiKey'),
      auth.hooks.authenticate(['jwt'])
    )
  );

app.hooks({
  before: {
    all: [authenticate()]
  }
});

app.listen(8080);

现在任何带有头文件 x-api-key 和值 opensesame 的请求都将由服务器验证.

创建匿名用户策略

我们将看到的第二个策略是匿名用户.对于这个特定的流程,我们希望客户端调用 /authentication 端点,让我们知道它想要匿名进行身份验证.然后,服务器将创建一个新用户并返回一个新的JWT令牌,客户端必须从该点开始使用该令牌.

首先让我们使用``passport-custom``创建策略

const Strategy = require('passport-custom');

module.exports = opts => {
  return function() {
    const verifier = async (req, done) => {
      // create a new user in the user service
      // mark this user with a specific anonymous=true attribute
      const user = await this.service(opts.userService).create({
        anonymous: true
      });

      // authenticate the request with this user
      return done(null, user, {
        userId: user.id
      });
    };

    // register the strategy in the app.passport instance
    this.passport.use('anonymous', new Strategy(verifier));
  };
};

接下来让我们更新我们的 server.js 来使用这个策略.

const anonymous = require('./anonymous');

app.configure(
  anonymous({
    // the user service
    userService: 'users'
  })
);

const authenticate = () =>
  commonHooks.iff(
    commonHooks.every(commonHooks.isProvider('external')),
    commonHooks.iffElse(
      ctx => ctx.params.headers['x-api-key'],
      auth.hooks.authenticate('apiKey'),
      // add the additional anonymous strategy
      auth.hooks.authenticate(['jwt', 'anonymous'])
    )
  );

最后我们的 server.js 看起来像这样:

const feathers = require('@feathersjs/feathers');
const express = require('@feathersjs/express');
const auth = require('@feathersjs/authentication');
const jwt = require('@feathersjs/authentication-jwt');
const memory = require('feathers-memory');
const commonHooks = require('feathers-hooks-common');

const apiKey = require('./apiKey');
const anonymous = require('./anonymous');

const app = express(feathers());

app.configure(express.rest());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.configure(auth({ secret: 'secret' }));
app.configure(jwt());
app.configure(
  apiKey({
    header: 'x-api-key',
    allowedKeys: ['opensesame']
  })
);
app.configure(
  anonymous({
    userService: 'users'
  })
);

app.use('/users', memory());

const authenticate = () =>
  commonHooks.iff(
    commonHooks.every(commonHooks.isProvider('external')),
    commonHooks.iffElse(
      ctx => ctx.params.headers['x-api-key'],
      auth.hooks.authenticate('apiKey'),
      auth.hooks.authenticate(['jwt', 'anonymous'])
    )
  );

app.hooks({
  before: {
    all: [authenticate()]
  }
});

app.listen(8080);

现在任何此类请求都将返回有效的JWT令牌:

POST /authentication

{
  strategy: 'anonymous'
}

注解

this looks very similar to a request body for local strategy:

POST /authentication

{
  strategy: 'local',
  username: 'admin',
  password: 'password'
}

因此,对于我们注册的任何新策略,我们可以使用特定的主体调用 /authentication 端点并期望返回有效的JWT,我们可以在其上使用它.


我们可以看到使用 passport-custom 以标准护照方式创建完全自定义的身份验证策略非常容易.

快乐黑客!!