密钥泄漏:访问令牌验证端点

bas*_*ith 4 node.js keycloak

在独立模式下运行keycloak,并使用node.js适配器创建用于验证api调用的微服务。

来自keyclaok的jwt令牌与每个api调用一起发送。仅当发送的令牌是有效令牌时,它才会响应。

  • 如何从微服务验证访问令牌?
  • keycloak是否有任何令牌验证?

Mar*_*kus 7

两种验证令牌的方法:

  • 在线的
  • 离线

上面描述的变体是在线验证。这当然非常昂贵,因为它为每次验证引入了另一个 http/往返。

许多更有效的离线验证:一个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...
Run Code Online (Sandbox Code Playgroud)

为什么不使用官方的keycloak-connectnode.js 库(而是使用keycloak-backend)?官方库更侧重于作为中间件的 express 框架,并且(据我所知)不直接公开任何验证功能。或者您可以使用任意 JWT/OICD 库,因为验证是一个标准化过程。

  • 你好阿拉比德,你是完全正确的。这是一个需要做出的决定和权衡。JWT 无论如何应该是相当短暂的。另一种方法是将某种“注销事件”推送到内存中的失效存储:因此,您确实检查每个令牌,但不检查远程服务,仅检查包含推送的失效的进程/系统内部缓存。但我不知道有哪个库实现了这个。 (3认同)
  • 是的,在线验证的成本很高,但是如果您使用纯离线验证,我们如何知道令牌是否因注销而失效? (2认同)
  • 我正在为我的node-rest-api使用keycloak-connect库,但是当我在令牌过期之前从react-app注销或关闭keycloak管理控制台中的所有会话时,我仍然可以使用之前生成的令牌调用rest api后端在登录时(例如与邮递员)。keycloak 库中有一些方法可以验证令牌吗? (2认同)

tro*_*r19 6

我会为此使用这个 UserInfo 端点,您还可以使用它检查其他属性,例如电子邮件以及您在映射器中定义的内容。您必须使用 Bearer Authorization 在标头属性中发送访问令牌:Bearer access_token

http://localhost:8081/auth/realms/demo/protocol/openid-connect/userinfo


kfr*_*bie 6

扩展troger19的答案

问题1:如何验证微服务的访问令牌?

实现一个功能,以检查每个对承载令牌的请求,并在将该令牌传递给api的路由处理程序之前,将其发送给userinfo端点上的密钥库服务器进行验证。

您可以通过请求其找到你keycloak服务器的特定端点(如用户信息的路由)知名配置

如果您在节点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);
});
Run Code Online (Sandbox Code Playgroud)

问题2:Keycloak是否有任何令牌验证?

向Keycloak的userinfo端点发出请求是验证令牌有效的一种简便方法。

来自有效令牌的 Userinfo响应:

状态:200 OK

{
    "sub": "xxx-xxx-xxx-xxx-xxx",
    "name": "John Smith",
    "preferred_username": "jsmith",
    "given_name": "John",
    "family_name": "Smith",
    "email": "john.smith@example.com"
}
Run Code Online (Sandbox Code Playgroud)

来自无效有效令牌的 Userinfo响应:

状态:401未经授权

{
    "error": "invalid_token",
    "error_description": "Token invalid: Token is not active"
}
Run Code Online (Sandbox Code Playgroud)

附加信息:

Keycloak提供了自己的npm软件包,称为keycloak-connect。该文档描述了路由上的简单身份验证,要求用户登录才能访问资源:

app.get( '/complain', keycloak.protect(), complaintHandler );
Run Code Online (Sandbox Code Playgroud)

我没有发现此方法可用于仅承载身份验证。以我的经验,在路由上实施这种简单的身份验证方法会导致“访问被拒绝”响应。此问题还询问如何使用Keycloak访问令牌对rest api进行身份验证。接受的答案建议也使用keycloak-connect提供的简单身份验证方法,但是Alex在注释中指出:

“ keyloak.protect()函数(没有)从标头中获取承载令牌。我仍在寻找该解决方案以进行仅承载身份验证– Alex 17年2月2日在14:02