Docker环境下NestJS集成Keycloak的Token验证错误排查
Docker环境下NestJS集成Keycloak的认证问题
环境配置
Dockerfile
FROM node:18-alpine WORKDIR /app COPY package.json ./ COPY package-lock.json ./ RUN npm install COPY . . RUN npx prisma generate RUN npm run build EXPOSE 3000 CMD ["node", "dist/main.js"]
docker-compose.yml
version: '3.8' services: nest-app: build: context: . dockerfile: Dockerfile ports: - "3000:3000" environment: DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/mydatabase" depends_on: postgres: condition: service_healthy networks: - my-network postgres: image: postgres:latest environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: mydatabase ports: - "5432:5432" volumes: - postgres_data:/var/lib/postgresql/data networks: - my-network healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 10s timeout: 5s retries: 5 keycloak: image: quay.io/keycloak/keycloak:latest environment: DB_VENDOR: h2 KEYCLOAK_ADMIN: admin KEYCLOAK_ADMIN_PASSWORD: admin KEYCLOAK_IMPORT: /tmp/realm.json ports: - "8081:8080" volumes: - ./realm.json:/tmp/realm.json entrypoint: ["/opt/keycloak/bin/kc.sh"] command: ["start-dev"] networks: - my-network volumes: postgres_data: networks: my-network: driver: bridge
app.module.ts
import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { AuthGuard, KeycloakConnectModule, ResourceGuard, RoleGuard, } from 'nest-keycloak-connect'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { PrismaModule } from './prisma.module'; import { APP_GUARD } from '@nestjs/core'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, }), PrismaModule, KeycloakConnectModule.register({ authServerUrl: 'http://127.0.0.1:8081/', realm: 'test', clientId: 'my-nest-project', secret: 'GSLUjhMMctvip6B01q3g7xu6P8SJBvT6', // or `credentials.secret` if used }), ], controllers: [AppController], providers: [ AppService, { provide: APP_GUARD, useClass: AuthGuard, }, { provide: APP_GUARD, useClass: ResourceGuard, }, { provide: APP_GUARD, useClass: RoleGuard, }, ], }) export class AppModule {}
app.service.ts
import { Injectable } from '@nestjs/common'; @Injectable() export class AppService { getHello(): string { return 'Hello, World!'; } getProtectedMessage(): string { return 'This is a protected route'; } getTestMessage(): string { return 'This is a test route'; } }
main.ts
import { NestFactory } from '@nestjs/core'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { AppModule } from './app.module'; import { PrismaService } from './prisma.service'; async function bootstrap() { const app = await NestFactory.create(AppModule); // Swagger configuration const config = new DocumentBuilder() .setTitle('API Documentation') .setDescription('The API description') .setVersion('1.0') .addBearerAuth() .addTag('app') .addTag('hello') .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('api', app, document); const prismaService = app.get(PrismaService); await prismaService.enableShutdownHooks(app); await app.listen(3000); } bootstrap();
问题现象
Keycloak日志出现错误:
WARN [Keycloak] Cannot validate access token: Error: Grant validation failed. Reason: invalid token (wrong ISS)
API调用情况
- 公开端点:
- GET
http://localhost:3000/(正常) - GET
http://localhost:3000/test(正常) - GET
http://localhost:3000/hello(正常) - GET
http://localhost:8081/realms/test(正常)
- GET
- 获取Token:POST
http://localhost:8081/realms/test/protocol/openid-connect/token(正常) - 访问受保护路由:GET
http://localhost:3000/protected(返回401 Unauthorized)
已尝试的解决方法
- 验证Token有效性:通过Keycloak管理界面获取Token访问受保护路由,仍返回401
- 调整
authServerUrl:测试http://127.0.0.1:8081/、http://127.0.0.1:8081/auth、http://localhost:8081/auth等配置,问题依旧 - 重建Keycloak Realm:重新创建后错误仍存在
- 搜索相关问题:未找到有效解决方案
求助问题
- 可以检查或调整哪些配置解决此错误?
- Docker或NestJS配置中是否需要额外设置确保与Keycloak正确集成?
解决方案
核心原因
wrong ISS错误本质是Token中的签发者(iss)地址与NestJS验证时使用的地址不匹配:
- 从宿主机访问Keycloak获取Token时,Token的
iss字段是http://localhost:8081/realms/test - NestJS运行在Docker容器中,配置的
authServerUrl是http://127.0.0.1:8081/,但容器内的127.0.0.1指向自身而非宿主机的Keycloak服务,导致验证时推导的iss与Token中的不一致。
具体修复步骤
1. 调整Keycloak的前端URL配置
在docker-compose.yml的keycloak服务中添加环境变量,强制Token的iss使用宿主机可访问的地址:
keycloak: image: quay.io/keycloak/keycloak:latest environment: DB_VENDOR: h2 KEYCLOAK_ADMIN: admin KEYCLOAK_ADMIN_PASSWORD: admin KEYCLOAK_IMPORT: /tmp/realm.json # 添加这一行 KEYCLOAK_FRONTEND_URL: http://localhost:8081/ ports: - "8081:8080" volumes: - ./realm.json:/tmp/realm.json entrypoint: ["/opt/keycloak/bin/kc.sh"] command: ["start-dev"] networks: - my-network
2. 修改NestJS的Keycloak配置
让NestJS容器能正确访问Keycloak服务,同时指定匹配的issuer:
修改app.module.ts中的KeycloakConnectModule.register配置:
KeycloakConnectModule.register({ // 用Docker网络内的服务名访问Keycloak,容器内部端口是8080 authServerUrl: 'http://keycloak:8080/', realm: 'test', clientId: 'my-nest-project', secret: 'GSLUjhMMctvip6B01q3g7xu6P8SJBvT6', // 手动指定与Token一致的issuer地址 issuer: 'http://localhost:8081/realms/test', }),
3. 重启服务
执行以下命令重启所有服务,让配置生效:
docker-compose down docker-compose up --build
额外检查项
- 确认
realm.json中的realm配置里,issuer字段是否与KEYCLOAK_FRONTEND_URL一致,如果不一致,修改realm.json后重新导入 - 检查Keycloak客户端
my-nest-project的配置:确保Access Type为confidential(因为你使用了secret),并且Valid Redirect URIs/Web Origins设置正确(测试环境可暂时设为*)
内容的提问来源于stack exchange,提问作者Carolinedanslesnuages
相关产品推荐
相关产品推荐

