如何伪造 keycloack 调用以在本地开发中使用?

Nel*_*ira 11 javascript keycloak angular

我的公司使用 Keycloak 进行与 LDAP 连接的身份验证并返回一个填充了公司数据的用户对象。然而,在此期间,我们都在家工作,在我的日常工作中,每次重新加载应用程序时都必须在我的公司服务器中进行身份验证,这已被证明是一项昂贵的开销。尤其是间歇性的互联网连接。

如何伪造 Keycloak 调用并使 keycloak.protect() 成功运行?

我可以在我的机器上安装一个 Keyclock 服务器,但我宁愿不这样做,因为除了 vagrant VM、Postgres 服务器、be 服务器以及我保持打开的所有其他东西之外,它还会是另一台服务器在其中运行。最好进行模拟调用并返回固定的硬编码对象。

我的项目的 app-init.ts 是这样的:

import { KeycloakService } from 'keycloak-angular';
import { KeycloakUser } from './shared/models/keycloakUser';
<...>

export function initializer(
    keycloak: KeycloakService,
    <...>
): () => Promise<any> {
    return (): Promise<any> => {
        return new Promise(async (res, rej) => {
            <...>    
            await keycloak.init({
                config: environment.keycloakConfig,
                initOptions: {
                    onLoad: 'login-required',
                    // onLoad: 'check-sso',
                    checkLoginIframe: false
                },
                bearerExcludedUrls: [],
                loadUserProfileAtStartUp: false
            }).then((authenticated: boolean) => {
                if (!authenticated) return;
                keycloak.getKeycloakInstance()
                    .loadUserInfo()
                    .success(async (user: KeycloakUser) => {
                       // ...
                       // load authenticated user data
                       // ...
                    })    
            }).catch((err: any) => rej(err));
            res();
        });
    };
Run Code Online (Sandbox Code Playgroud)

我只需要一个固定的登录用户。但它必须返回一些固定的自定义数据。像这样的东西:

{ username: '111111111-11', name: 'Whatever Something de Paula',
  email: 'whatever@gmail.com', department: 'sales', employee_number: 7777777 }
Run Code Online (Sandbox Code Playgroud)

编辑

我试图查看@BojanKogoj 的想法,但是来自 Angular Interceptor 页面和其他示例和教程的 AFAIU,它必须被注入到一个组件中。Keycloak 初始化在应用程序初始化时调用,而不是在组件中调用。Keycloak 的返回也不是 init() 方法的直接返回。它通过.getKeycloakInstance().loadUserInfo().success()序列中的其他对象。或者也许只是我没有完全理解它。如果有人能提供一个拦截器的例子,它可以拦截调用并返回正确的结果,这可能是一种可能性。

编辑2

只是为了补充我需要的是让整个 keycloak 的系统正常工作。请注意,该(user: KeycloakUser) => {函数传递给successkeycloak 内部系统的方法。正如我上面所说,路由有一个必须工作的 keycloak.protect()。因此,这不仅仅是向用户返回承诺的简单案例。整个 .getKeycloakInstance().loadUserInfo().success() 链必须被模拟。或者至少我是这么理解的。

我根据@yurzui 的回答在解决方案中包含了一个答案

将等待几天来授予赏金,看看是否有人可以提出更好的解决方案(我对此表示怀疑)。

yur*_*zui 8

您可以利用 Angular 环境(甚至process.env)变量在真实和模拟实现之间切换。

这是一个如何做到这一点的简单示例:

app-init.ts

...
import { environment } from '../environments/environment';

export function initializer(
  keycloak: KeycloakService
): () => Promise<any> {

  function authenticate() {
    return keycloak
      .init({
        config: {} as any,
        initOptions: {onLoad: 'login-required', checkLoginIframe: false},
        bearerExcludedUrls: [],
        loadUserProfileAtStartUp: false
      })
      .then(authenticated => {
        return authenticated ? keycloak.getKeycloakInstance().loadUserInfo() : Promise.reject();
      });
  }

  // we use 'any' here so you don't have to define keyCloakUser in each environment    
  const { keyCloakUser } = environment as any; 

  return () => {
    return (keyCloakUser ? Promise.resolve(keyCloakUser) : authenticate()).then(user => {
      // ...
      // do whatever you want with user
      // ...
    });
  };
}
Run Code Online (Sandbox Code Playgroud)

环境.ts

export const environment = {
  production: false,
  keyCloakUser: {
    username: '111111111-11',
    name: 'Whatever Something de Paula',
    email: 'whatever@gmail.com',
  }
};
Run Code Online (Sandbox Code Playgroud)

环境.prod.ts

export const environment = {
  production: true,
};
Run Code Online (Sandbox Code Playgroud)

更新

如果你想KeycloakService在客户端模拟,那么你可以告诉 Angular 依赖注入来处理:

app.module.ts

import { environment } from '../environments/environment';
import { KeycloakService, KeycloakAngularModule } from 'keycloak-angular';
import { MockedKeycloakService } from './mocked-keycloak.service';

@NgModule({
  ...
  imports: [
    ...
    KeycloakAngularModule
  ],
  providers: [
    {
      provide: KeycloakService,
      useClass: environment.production ? KeycloakService : MockedKeycloakService
    },
    {
      provide: APP_INITIALIZER,
      useFactory: initializer,
      multi: true,
      deps: [KeycloakService]
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }
Run Code Online (Sandbox Code Playgroud)

模拟keycloak.service.ts

import { Injectable} from '@angular/core';
import { KeycloakService } from 'keycloak-angular';

@Injectable()
class MockedKeycloakService extends KeycloakService {
  init() {
    return Promise.resolve(true);
  }

  getKeycloakInstance() {
    return {
      loadUserInfo: () => {
        let callback;
        Promise.resolve().then(() => {
          callback({
            userName: 'name'
          });
        });
        return {
          success: (fn) => callback = fn
        };
      }
    } as any;
  }
}
Run Code Online (Sandbox Code Playgroud)


dch*_*cik 5

尽管您明确指出您认为模拟是最好的选择,但我建议重新考虑它,转而使用 docker 设置本地 Keycloak 实例。当您提供一个领域来引导您的环境时,事情就会变得很容易。两年多以来,我一直使用这种方法成功开发与 Keycloak 配合使用的应用程序。这种方法将让您“替代对公司服务器的调用”,因此我将其发布在这里。

假设您安装了 docker 和 docker-compose,您将需要:

1.docker-compose.yaml

version: '3.7'

services:
  keycloak:
    image: jboss/keycloak:10.0.1
    environment:
      KEYCLOAK_USER: admin
      KEYCLOAK_PASSWORD: admin
      KEYCLOAK_IMPORT: /tmp/dev-realm.json
    ports:
      - 8080:8080
    volumes:
      - ./dev-realm.json:/tmp/dev-realm.json
Run Code Online (Sandbox Code Playgroud)

2. dev-realm.json(确切的内容取决于所需的设置,这是您在问题中提到的最小值)

{
  "id": "dev",
  "realm": "dev",
  "enabled": true,
  "clients": [
    {
      "clientId": "app",
      "enabled": true,
      "redirectUris": [
        "*"
      ],
      "bearerOnly": false,
      "consentRequired": false,
      "standardFlowEnabled": true,
      "implicitFlowEnabled": false,
      "directAccessGrantsEnabled": false,
      "secret": "mysecret",
      "publicClient": false,
      "protocol": "openid-connect",
      "fullScopeAllowed": false,
      "protocolMappers": [
        {
          "name": "department",
          "protocol": "openid-connect",
          "protocolMapper": "oidc-usermodel-attribute-mapper",
          "consentRequired": false,
          "config": {
            "user.attribute": "department",
            "id.token.claim": "true",
            "access.token.claim": "true",
            "claim.name": "department",
            "userinfo.token.claim": "true"
          }
        },
        {
          "name": "employee_number",
          "protocol": "openid-connect",
          "protocolMapper": "oidc-usermodel-attribute-mapper",
          "consentRequired": false,
          "config": {
            "user.attribute": "employee_number",
            "id.token.claim": "true",
            "access.token.claim": "true",
            "claim.name": "employee_number",
            "userinfo.token.claim": "true"
          }
        }
      ]
    }
  ],
  "users": [
    {
      "username": "111111111-11",
      "enabled": true,
      "firstName": "Whatever Something de Paula",
      "email": "whatever@gmail.com",
      "credentials": [{
        "type": "password",
        "value": "demo"
      }],
      "attributes": {
        "department": "sales",
        "employee_number": 7777777
      }
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

3. 创建专用的 Angular 环境,该环境将使用“ http://localhost:8080/auth ”和领域“dev”进行本地开发

这种方法相对于模拟的优点:

  • 所有 OIDC 和 keycloak 功能均正常工作。我承认这取决于您是否需要它们,但您可以自由使用领域/客户端角色、组、带有令牌刷新的“真实”OIDC 流程。这可以保证您的本地设置也适用于公司服务
  • 此设置可以存储在存储库中(与 Keycloak 服务器的手动设置相反),并可用于 Web 应用程序和后端服务

默认情况下,Keycloak 使用 H2 内存数据库,需要大约 600MB 的 RAM,因此我认为它的占用空间相对较低。