在网站和自有API之间进行身份验证

Jus*_*tin 7 authentication

这可能是之前被问过的,所以我先发制先知道歉.

我建了一个网站,我建了一个API.移动应用程序将来也会使用该API.我拥有这两个,所以我很确定两个和三个腿的OAuth不适合我.API包含可供全球访问的部分以及受保护且需要用户帐户的其他部分.为了简单起见,我刚刚使用了https + Basic Auth解决方案(目前为止).当手动向API测试请求时,这一切都很好(我没有写测试,因为我是一个坏人),事情按预期工作,Basic Auth很好.

我正在尝试解决用明文用户和密码登录的用户流,将其发送到API进行身份验证,API只需要说是或否,但是来自网站的所有请求(代表用户)应该以某种方式使用他们的凭据签署API,以便他们想要POST/GET/PUT/DEL一个受保护的资源.

在我读过的所有auth资源中,我仍然对使用什么方案感到困惑.在网站端存储明文密码,以便我可以对64进行编码并通过网络发送它看起来很糟糕,但看起来这就是我必须要做的事情.我已经阅读了digest auth但我不确定是否已经知道了.欢迎提出任何建议.

Jos*_*llo 10

这就是我处理这种情况的方式;

  1. 当然,使用HTTPS将用户名和密码作为纯文本发布到api.
  2. 然后验证它到你的数据库,现在使用盐密码的最佳算法是bcrypt.
  3. 如果用户无效则返回401,或者其他什么.
  4. 如果用户有效,则返回JWT令牌,其配置文件使用公钥算法进行签名.
  5. 你的前端知道公钥,所以它可以解码JWT,但它不能生成一个新的.
  6. 对于每个需要身份验证的请求,都附加一个Authentication标头Bearer [JWT]
  7. 后端中的中间件读取此标头并使用私钥对其进行验证.

不要害怕JWT,每种语言和框架都有很多实现,比你想象的要容易.许多应用程序已经使用JWT甚至谷歌.

Auth0是一个身份验证代理,可以对任何身份提供程序或自定义数据库进行验证,并返回JWT.它提供了一个clientID,可以用来解码前端的配置文件,以及一个秘密来验证后端的令牌以及客户端库来执行此操作.

免责声明:我为auth0工作.

更新:既然你提到了node.js并在评论中表达,我将举例说明这项技术.

var http = require('http');
var express = require('express');

var jwt = require('jsonwebtoken');  //https://npmjs.org/package/node-jsonwebtoken
var expressJwt = require('express-jwt'); //https://npmjs.org/package/express-jwt

var secret = "this is the secret secret secret 12356";


var app = express();

app.configure(function () {
  this.use(express.urlencoded());
  this.use(express.json());
  this.use('/api', expressJwt({secret: secret}));
});

//authentication endpoint
app.post('/authenticate', function (req, res) {
  //validate req.body.username and req.body.password
  //if is invalid, return 401
  var profile = {
    first_name: 'John',
    last_name: 'Foo',
    email: 'foo@bar.com',
    id: 123
  };

  var token = jwt.sign(profile, secret, {
    expiresInMinutes: 60*5
  });

  res.json({
    token: token
  });
});

//protected api
app.get('/api/something', function (req, res) {
  console.log('user ' + req.user.email + ' is calling /something');
  res.json({
    name: 'foo'
  });
});

//sample page
app.get('/', function (req, res) {
  res.sendfile(__dirname + '/index.html');
});

http.createServer(app).listen(8080, function () {
  console.log('listening on http://localhost:8080');
});
Run Code Online (Sandbox Code Playgroud)

这是一个快速应用程序,其中一个端点验证用户名和密码.如果凭据有效,则返回具有完整配置文件的JWT令牌,并且有效期为5小时.

然后我们有一个示例端点,/api/something但由于我有一个express-jwt中间件,/api它需要一个Authorization:带有有效令牌的Bearer头.中间件不仅验证令牌,还解析配置文件并将其放在req.user上.

如何使用这个客户端?这是jquery的一个例子:

//this is used to parse the profile
function url_base64_decode(str) {
  var output = str.replace("-", "+").replace("_", "/");
  switch (output.length % 4) {
    case 0:
      break;
    case 2:
      output += "==";
      break;
    case 3:
      output += "=";
      break;
    default:
      throw "Illegal base64url string!";
  }
  return window.atob(output); //polifyll https://github.com/davidchambers/Base64.js
}
var token;

//authenticate at some point in your page
$(function () {
    $.ajax({
        url: '/authenticate',
        type: 'POST',
        data: {
            username: 'john',
            password: 'foo'
        }
    }).done(function (authResult) {
        token = authResult.token;
        var encoded = token.split('.')[1];
        var profile = JSON.parse(url_base64_decode(encoded));
        alert('Hello ' + profile.first_name + ' ' + profile.last_name);
    });
});

//send the authorization header with token on every call to the api
$.ajaxSetup({
    beforeSend: function(xhr) {
        if (!token) return;
        xhr.setRequestHeader('Authorization', 'Bearer ' + token);
    }
});

//api call
setTimeout(function () {
    $.ajax({
        url: '/api/something',
    }).done(function (res) {
        console.log(rest);
    });
}, 5000);
Run Code Online (Sandbox Code Playgroud)

首先,我使用用户名和密码进行身份验证调用,我可以解码JWT中的配置文件以获取用户配置文件,并且我还保存令牌以便稍后在每个请求中使用.

ajaxSetup/beforeSend技巧为每个调用添加标头.那么,我可以向/ api/something发出请求.

您可以想象这种方法不使用cookie和会话,因此它在CORS场景中开箱即用.

我是passport.js的忠实粉丝,我为其他适配器贡献了很多适配器和修复程序但是对于这种特殊情况我不会使用它.