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 的回答在解决方案中包含了一个答案
将等待几天来授予赏金,看看是否有人可以提出更好的解决方案(我对此表示怀疑)。
您可以利用 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)
尽管您明确指出您认为模拟是最好的选择,但我建议重新考虑它,转而使用 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”进行本地开发
这种方法相对于模拟的优点:
默认情况下,Keycloak 使用 H2 内存数据库,需要大约 600MB 的 RAM,因此我认为它的占用空间相对较低。
| 归档时间: |
|
| 查看次数: |
2730 次 |
| 最近记录: |