You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hapi.js中JWT授权无法实现,携带Token访问路由返回401错误求助

Hapi.js JWT授权401错误排查与修复

问题场景

在Hapi.js后端集成JWT认证策略后,注册接口能正常返回Token,但携带该Token访问默认需要授权的/路由时,始终返回401未授权错误。

服务端核心代码

const Hapi = require('@hapi/hapi');
const Jwt = require('@hapi/jwt')

const init = async () => {
    const server = Hapi.Server({
        port: 4545,
        host: 'localhost',
        "routes": {
          "cors": {
              "origin": ["*"],
              "headers": ["Accept", "Content-Type"],
              "additionalHeaders": ["X-Requested-With"]
          }
      }
    });

    server.ext('onPreResponse', (request, h) => {
      const response = request.response;
      if (response.isBoom) {
          response.output.headers['Access-Control-Allow-Origin'] = 'https://hapi.dev';
          response.output.headers['Access-Control-Allow-Header'] = '*';
        } else {
          response.headers['Access-Control-Allow-Origin'] = 'https://hapi.dev';
          response.headers['Access-Control-Allow-Headers'] = '*';
      }
      return h.continue;
    });

    await server.register(Jwt)

    server.auth.strategy('my_jwt_strategy', 'jwt', {
      keys: 'some_shared_secret',
      verify: {
          aud: 'urn:audience:test',
          iss: 'urn:issuer:test',
          sub: false,
          nbf: true,
          exp: true,
          maxAgeSec: 14400,
          timeSkewSec: 15
      },
      validate: (artifacts, request, h) => {
        console.log('Validation: Start');
        console.log('Decoded JWT:', artifacts.decoded);
        console.log('Validation: End');
        return { isValid: true };
      }
    });

    server.auth.default('my_jwt_strategy');

    // 注册接口
    server.route({
        method: 'POST',
        path: '/register',
        handler: async (request, h) => {
            const { username, password } = request.payload;
            const token = Jwt.token.generate({ user: username }, 'some_shared_secret');
            return { token };
        },
        options: { auth: false, cors: { origin: ['*'], headers: ['*'], credentials: true } }
    });

    // 需要授权的根路由
    server.route({
      method: 'GET',
      path: '/',
      handler: (request, h) => {
          return 'Hello, World!';
      }
    });

    await server.start();
    console.log('Server started on port 4545 !');
};

init();

客户端请求代码

注册请求:

await fetch('http://localhost:4545/register', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({username: 'balbalsbd', password: 'asdasdada'})
})
.then(res => res.ok ? res.json() : Promise.reject({ status: res.status }))
.then(data => console.log('Registration successful:', data))
.catch(err => console.error('Registration failed:', err));

测试授权请求:

await fetch('http://localhost:4545/', {
    method: 'GET',
    headers: { 'Authorization': `Bearer ${token}` }
})
.then(res => res.ok ? res.json() : Promise.reject({ status: res.status }))
.then(data => console.log('Response:', data))
.catch(err => console.error('Error:', err));

核心问题分析

  1. Token声明不匹配:JWT策略配置中开启了aud(受众)和iss(签发者)验证,但生成Token时仅传入了user字段,缺少这两个必填声明,导致验证失败返回401。
  2. Token生成方法参数错误:Jwt.token.generate的正确用法需要明确指定payload、签名选项(包括密钥、算法),原代码的参数格式不符合@hapi/jwt的要求。
  3. CORS配置冲突:全局onPreResponse中硬编码了固定的Origin头,与注册接口的*配置冲突,可能导致浏览器拦截请求头。

修复方案

1. 修正Token生成逻辑

在登录和注册接口中,生成包含完整声明的Token:

// 替换原Token生成代码
const token = Jwt.token.generate(
  { 
    user: username,
    aud: 'urn:audience:test', // 与策略中的aud配置完全匹配
    iss: 'urn:issuer:test',   // 与策略中的iss配置完全匹配
    exp: Math.floor(Date.now() / 1000) + 14400 // 4小时过期,和maxAgeSec一致
  },
  { key: 'some_shared_secret', algorithm: 'HS256' }, // 明确指定算法和密钥
  { noTimestamp: false }
);

2. 可选:关闭不必要的验证

如果不需要验证aud和iss,可以直接在策略中关闭:

server.auth.strategy('my_jwt_strategy', 'jwt', {
  keys: 'some_shared_secret',
  verify: {
      aud: false, // 关闭受众验证
      iss: false, // 关闭签发者验证
      sub: false,
      nbf: true,
      exp: true,
      maxAgeSec: 14400,
      timeSkewSec: 15
  },
  validate: (artifacts, request, h) => {
    return { isValid: true };
  }
});

3. 统一CORS配置

移除onPreResponse中的硬编码CORS头,改用全局路由配置:

const server = Hapi.Server({
    port: 4545,
    host: 'localhost',
    routes: {
      cors: {
          origin: ['*'],
          headers: ['Accept', 'Content-Type', 'Authorization'], // 加入Authorization头
          additionalHeaders: ['X-Requested-With']
      }
    }
});

// 移除原有的server.ext('onPreResponse')代码

4. 验证修复效果

重启服务后,重新注册获取新Token,携带Token访问/路由,此时应正常返回Hello, World!,不再出现401错误。

内容的提问来源于stack exchange,提问作者Abubakr Shomirsaidov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 03:40:55