Keycloak:访问令牌验证端点
Posted
技术标签:
【中文标题】Keycloak:访问令牌验证端点【英文标题】:Keycloak: Access token validation end point 【发布时间】:2018-06-24 17:58:08 【问题描述】:在独立模式下运行 keycloak。并使用 node.js 适配器创建了一个微服务来验证 api 调用。
来自 keyclaok 的 jwt 令牌与每个 api 调用一起发送。只有在发送的令牌是有效令牌时才会响应。
如何验证来自微服务的访问令牌? keycloak 是否提供任何令牌验证?【问题讨论】:
【参考方案1】:扩展troger19's answer:
问题 1:如何验证来自微服务的访问令牌?
实现一个函数来检查对不记名令牌的每个请求,并在将令牌传递给您的 api 的路由处理程序之前,将该令牌发送到 userinfo 端点处由您的 keycloak 服务器进行验证。
您可以通过请求其well-known configuration 来找到您的 keycloak 服务器的特定端点(如 userinfo 路由)。
如果您在节点 api 中使用 expressjs,则可能如下所示:
const express = require("express");
const request = require("request");
const app = express();
/*
* additional express app config
* app.use(bodyParser.json());
* app.use(bodyParser.urlencoded( extended: false ));
*/
const keycloakHost = 'your keycloak host';
const keycloakPort = 'your keycloak port';
const realmName = 'your keycloak realm';
// check each request for a valid bearer token
app.use((req, res, next) =>
// assumes bearer token is passed as an authorization header
if (req.headers.authorization)
// configure the request to your keycloak server
const options =
method: 'GET',
url: `https://$keycloakHost:$keycloakPort/auth/realms/$realmName/protocol/openid-connect/userinfo`,
headers:
// add the token you received to the userinfo request, sent to keycloak
Authorization: req.headers.authorization,
,
;
// send a request to the userinfo endpoint on keycloak
request(options, (error, response, body) =>
if (error) throw new Error(error);
// if the request status isn't "OK", the token is invalid
if (response.statusCode !== 200)
res.status(401).json(
error: `unauthorized`,
);
// the token is valid pass request onto your next function
else
next();
);
else
// there is no token, don't process request further
res.status(401).json(
error: `unauthorized`,
);
);
// configure your other routes
app.use('/some-route', (req, res) =>
/*
* api route logic
*/
);
// catch 404 and forward to error handler
app.use((req, res, next) =>
const err = new Error('Not Found');
err.status = 404;
next(err);
);
问题 2:Keycloak 是否提供任何令牌验证?
向 Keycloak 的 userinfo 端点发出请求是验证令牌是否有效的简单方法。
来自有效令牌的用户信息响应:
状态:200 正常
"sub": "xxx-xxx-xxx-xxx-xxx",
"name": "John Smith",
"preferred_username": "jsmith",
"given_name": "John",
"family_name": "Smith",
"email": "john.smith@example.com"
来自无效的有效令牌的用户信息响应:
状态:401 未授权
"error": "invalid_token",
"error_description": "Token invalid: Token is not active"
其他信息:
Keycloak 提供了自己的 npm 包,名为 keycloak-connect。该文档描述了路由的简单身份验证,要求用户登录才能访问资源:
app.get( '/complain', keycloak.protect(), complaintHandler );
我还没有发现这种方法可以使用仅承载身份验证。根据我的经验,在路由上实施这种简单的身份验证方法会导致“拒绝访问”响应。 This question 还询问如何使用 Keycloak 访问令牌来验证 rest api。 The accepted answer 建议也使用 keycloak-connect 提供的简单身份验证方法,但正如 Alex 在 cmets 中所述:
"keyloak.protect() 函数(不)从 标题。我仍在寻找仅做承载的解决方案 身份验证 – 亚历克斯 2017 年 11 月 2 日 14:02
【讨论】:
嗨。但是当我点击 userInfo 端点时。我每次都会收到这个回复。 "sub": "xxxxxxxxxxx", "email_verified": false, "preferred_username": "service-account-testclient" 但我的用户名是“用户”。谁能解释一下为什么? 以上代码 sn-p 在我的设置中有效。尝试使用 kecloak-connect,但没有按预期工作。 在每次请求之前向 keycloak 服务器发出请求,是否会减慢响应速度并因此减慢应用程序? 您所指的“知名配置”是什么?【参考方案2】:有两种验证令牌的方法:
在线 离线上述变体是在线验证。这当然是相当昂贵的,因为它为每次验证引入了另一个 http/round 行程。
更多离线验证更有效:JWT 令牌是一个 base64 编码的 JSON 对象,它已经包含所有信息(声明)以进行离线验证。您只需要公钥并验证签名(以确保内容“有效”):
有几个库(例如keycloak-backend)可以离线验证,无需任何远程请求。离线验证就这么简单:
token = await keycloak.jwt.verifyOffline(someAccessToken, cert);
console.log(token); //prints the complete contents, with all the user/token/claim information...
为什么不使用官方的keycloak-connect
node.js 库(而是使用keycloak-backend)?官方库更侧重于将 express 框架作为中间件,并且(据我所见)不直接公开任何验证函数。或者您可以使用任意 JWT/OICD 库,因为验证是一个标准化过程。
【讨论】:
是的,在线验证的成本很高,但是如果您使用纯离线验证,我们怎么知道令牌没有被注销失效? 你好 alabid,你是绝对正确的。这是一个决定和权衡。无论如何,JWT 应该是相当短暂的。另一种选择是将某种“注销事件”推送到内存中的失效存储:因此,您确实检查了每个令牌,但不检查远程服务,只检查包含推送的失效的进程/系统内部缓存。但我不知道有什么库可以实现这一点。 确实如此。我很惊讶没有图书馆真正做到这一点,这对一个恕我直言来说是个好主意。无论如何,我认为一个短暂的令牌就足够了。请再问一个问题,当它到期时,我们使用刷新令牌来生成一个新的,对吗? @alabid:是的,你也完全正确!正如您所写,您使用“刷新令牌”来获取新的“访问令牌”。我认为当您查询新的“访问令牌”时,有些服务器甚至会返回一个新的刷新令牌。这是某种“刷新令牌轮换”。 我正在为我的 node-rest-api 使用 keycloak-connect 库,但是当我在令牌过期之前从 react-app 注销或关闭 keycloak 管理控制台中的所有会话时,我仍然可以调用使用登录时生成的前一个令牌(例如使用邮递员)休息 api 后端。 keycloak 库中是否有一些方法可以验证令牌?【参考方案3】:我会为此使用这个 UserInfo 端点,您还可以使用它检查其他属性,例如电子邮件以及您在映射器中定义的内容。您必须使用 Bearer 在标头属性中发送访问令牌 授权:Bearer access_token
http://localhost:8081/auth/realms/demo/protocol/openid-connect/userinfo
【讨论】:
【参考方案4】:@kfrisbie 感谢您的回复,通过您的示例,我可以使用 keycloak 连接适配器重构您的代码:
// app.js
app.use(keycloakConfig.validateTokenKeycloak); // valid token with keycloak server
// add routes
const MyProtectedRoute = require('./routes/protected-routes'); // routes using keycloak.protect('some-role')
app.use('/protected', MyProtectedRoute);
因此,当发送授权标头时,我可以验证令牌对 keycloak 服务器仍然有效,因此如果在过期令牌之前从管理控制台或前端 spa 注销,我的 rest api 会抛出 401 错误,在其他情况下 keycloak使用protect方法。
// keycloak.config.js
let memoryStore = new session.MemoryStore();
let _keycloak = new Keycloak( store: memoryStore );
async function validateTokenKeycloak(req, res, next)
if (req.kauth && req.kauth.grant)
console.log('--- Verify token ---');
try
var result = await _keycloak.grantManager.userInfo(req.kauth.grant.access_token);
//var result = await _keycloak.grantManager.validateAccessToken(req.kauth.grant.access_token);
if(!result)
console.log(`result:`, result);
throw Error('Invalid Token');
catch (error)
console.log(`Error: $error.message`);
return next(createError.Unauthorized());
next();
module.exports =
validateTokenKeycloak
;
【讨论】:
以上是关于Keycloak:访问令牌验证端点的主要内容,如果未能解决你的问题,请参考以下文章
Keycloak 直接访问授权在 keycloak userinfo 端点上无效
Keycloak 使用不同的客户端重新验证经过身份验证的用户
我应该明确验证 Keycloak 令牌还是由 Keycloak 适配器完成?
服务器端使用 Bearer 令牌以编程方式在 Java 中验证 Keycloak 用户