使用 Redis 实施的 OAuth 2 - 授予类型密码
在这个例子中,我将使用 redis 数据库在 rest api 中使用 oauth2
重要提示: 你需要在你的计算机上安装 redis 数据库,从这里为 Linux 用户下载并从此处安装 Windows 版本,我们将使用 redis manager 桌面应用程序,从此处安装。
现在我们必须将 node.js 服务器设置为使用 redis 数据库。
- 创建服务器文件:app.js
var express = require('express'),
bodyParser = require('body-parser'),
oauthserver = require('oauth2-server'); // Would be: 'oauth2-server'
var app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.oauth = oauthserver({
model: require('./routes/Oauth2/model'),
grants: ['password', 'refresh_token'],
debug: true
});
// Handle token grant requests
app.all('/oauth/token', app.oauth.grant());
app.get('/secret', app.oauth.authorise(), function (req, res) {
// Will require a valid access_token
res.send('Secret area');
});
app.get('/public', function (req, res) {
// Does not require an access_token
res.send('Public area');
});
// Error handling
app.use(app.oauth.errorHandler());
app.listen(3000);
- 在 routes / Oauth2 / model.js 中创建 Oauth2 模型
var model = module.exports,
util = require('util'),
redis = require('redis');
var db = redis.createClient();
var keys = {
token: 'tokens:%s',
client: 'clients:%s',
refreshToken: 'refresh_tokens:%s',
grantTypes: 'clients:%s:grant_types',
user: 'users:%s'
};
model.getAccessToken = function (bearerToken, callback) {
db.hgetall(util.format(keys.token, bearerToken), function (err, token) {
if (err) return callback(err);
if (!token) return callback();
callback(null, {
accessToken: token.accessToken,
clientId: token.clientId,
expires: token.expires ? new Date(token.expires) : null,
userId: token.userId
});
});
};
model.getClient = function (clientId, clientSecret, callback) {
db.hgetall(util.format(keys.client, clientId), function (err, client) {
if (err) return callback(err);
if (!client || client.clientSecret !== clientSecret) return callback();
callback(null, {
clientId: client.clientId,
clientSecret: client.clientSecret
});
});
};
model.getRefreshToken = function (bearerToken, callback) {
db.hgetall(util.format(keys.refreshToken, bearerToken), function (err, token) {
if (err) return callback(err);
if (!token) return callback();
callback(null, {
refreshToken: token.accessToken,
clientId: token.clientId,
expires: token.expires ? new Date(token.expires) : null,
userId: token.userId
});
});
};
model.grantTypeAllowed = function (clientId, grantType, callback) {
db.sismember(util.format(keys.grantTypes, clientId), grantType, callback);
};
model.saveAccessToken = function (accessToken, clientId, expires, user, callback) {
db.hmset(util.format(keys.token, accessToken), {
accessToken: accessToken,
clientId: clientId,
expires: expires ? expires.toISOString() : null,
userId: user.id
}, callback);
};
model.saveRefreshToken = function (refreshToken, clientId, expires, user, callback) {
db.hmset(util.format(keys.refreshToken, refreshToken), {
refreshToken: refreshToken,
clientId: clientId,
expires: expires ? expires.toISOString() : null,
userId: user.id
}, callback);
};
model.getUser = function (username, password, callback) {
db.hgetall(util.format(keys.user, username), function (err, user) {
if (err) return callback(err);
if (!user || password !== user.password) return callback();
callback(null, {
id: username
});
});
};
你只需在计算机上安装 redis 并运行以下节点文件
#! /usr/bin/env node
var db = require('redis').createClient();
db.multi()
.hmset('users:username', {
id: 'username',
username: 'username',
password: 'password'
})
.hmset('clients:client', {
clientId: 'client',
clientSecret: 'secret'
})//clientId + clientSecret to base 64 will generate Y2xpZW50OnNlY3JldA==
.sadd('clients:client:grant_types', [
'password',
'refresh_token'
])
.exec(function (errs) {
if (errs) {
console.error(errs[0].message);
return process.exit(1);
}
console.log('Client and user added successfully');
process.exit();
});
注意 :此文件将为你的前端设置凭据以请求令牌以便你的请求来自
调用上述文件后的示例 redis 数据库:
请求如下:
调用 api 的示例
标题:
-
授权:基本后跟第一次设置 redis 时设置的密码:
一个。clientId + secretId 到 base64
-
数据格式:
username:请求令牌的用户
密码:用户密码
grant_type:取决于你想要什么选项,我选择 passwod 只需要用 redis 创建用户名和密码,redis 上的数据如下:
{ "access_token":"1d3fe602da12a086ecb2b996fd7b7ae874120c4f", "token_type":"bearer", // Will be used to access api + access+token e.g. bearer 1d3fe602da12a086ecb2b996fd7b7ae874120c4f "expires_in":3600, "refresh_token":"b6ad56e5c9aba63c85d7e21b1514680bbf711450" }
所以我们需要调用我们的 api 并使用我们刚刚创建的访问令牌获取一些安全数据,如下所示:
当令牌过期 api 会抛出令牌过期的错误,你无法访问任何 api 调用,见下图:
让我们看看如果令牌过期该怎么办,让我先解释一下,如果访问令牌过期,redis 中存在一个引用过期 access_token 的 refresh_token 那么我们需要的是再次使用 refresh_token grant_type 调用 oauth / token 并设置授权给 Basic clientId:clientsecret(以 64 位为基础!)并最终发送 refresh_token,这将生成一个带有新到期数据的新 access_token。
下图显示了如何获取新的访问令牌: