Te doy la bienvenida a nuestra página web, en este sitio vas a encontrar la resolución de lo que estás buscando.
Solución:
Puede aprovechar el entorno angular (o incluso process.env
) variable para cambiar entre implementaciones reales y simuladas.
Aquí hay un ejemplo simple de cómo hacer eso:
app-init.ts
...
import environment from '../environments/environment';
export function initializer(
keycloak: KeycloakService
): () => Promise
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
// ...
);
;
environment.ts
export const environment =
production: false,
keyCloakUser:
username: '111111111-11',
name: 'Whatever Something de Paula',
email: '[email protected]',
;
environment.prod.ts
export const environment =
production: true,
;
Actualizar
Si quieres burlarte KeycloakService
en el lado del cliente, entonces puede decirle a la inyección de dependencia Angular que se encargue de eso:
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
mocked-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;
Aunque declaras explícitamente que crees que burlarse es la mejor opción, te sugiero que lo reconsideres a favor de configurar una instancia local de Keycloak usando la ventana acoplable. Se vuelve fácil cuando proporciona un dominio para iniciar su entorno. He estado usando este enfoque con éxito durante más de 2 años desarrollando aplicaciones que funcionan con Keycloak. Este enfoque le permitirá “sustituir llamadas a su servidor corporativo”, por lo tanto, lo publico aquí.
Suponiendo que tiene Docker & Docker-compose instalados, necesitará:
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
2. dev-realm.json (el contenido exacto depende de la configuración requerida, este es el mínimo que ha mencionado en su pregunta)
"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": "[email protected]",
"credentials": [
"type": "password",
"value": "demo"
],
"attributes":
"department": "sales",
"employee_number": 7777777
]
3. Cree un entorno Angular dedicado que utilizará “http: // localhost: 8080 / auth” y realm “dev” para su desarrollo local
Las ventajas de este enfoque sobre la burla:
- todas las funciones de OIDC y keycloak están funcionando. Admito que depende de si los necesita, pero puede usar roles de dominio / cliente, grupos, flujo de OIDC ‘real’ con actualización de token. Esto le da la garantía de que su configuración local funcionará también con el servicio corporativo.
- esta configuración se puede almacenar en el repositorio (a diferencia de la configuración manual del servidor Keycloak) y se puede utilizar tanto para trabajar en aplicaciones web como en servicios de backend
De forma predeterminada, Keycloak usa una base de datos H2 en memoria y necesita alrededor de 600 MB de RAM, por lo que yo diría que ocupa un espacio relativamente bajo.
Solución
Pude simular el servicio Keycloak usando el método que sugirió @yurzui. Lo documentaré aquí, ya que puede ser útil para alguien.
Inicialmente había publicado una solución en la que exportaba condicionalmente las clases simuladas o reales del módulo simulado. Todo funcionó bien en el modo de desarrollo, pero cuando intenté construir la aplicación para publicar en el servidor de producción, recibí un error, así que tuve que volver a la solución de 2 clases. Explico el problema en detalle esta pregunta.
Este es el código de trabajo (hasta ahora).
Interfaz:
Con un poco de ayuda de la respuesta de @ kev en esta pregunta y @yurzui (nuevamente: D) en esta, creé una clase MockKeycloakService:
import Injectable from '@angular/core';
import KeycloakService from 'keycloak-angular';
import environment from '../../../environments/environment';
@Injectable( providedIn: 'root' )
export default class MockKeycloakService
init()
console.log('[KEYCLOAK] Mocked Keycloak call');
return Promise.resolve(true);
getKeycloakInstance()
return
loadUserInfo: () =>
let callback : any;
Promise.resolve().then(() =>
callback(
username: '77363698953',
NOME: 'Nelson Teixeira',
FOTO: 'assets/usuarios/nelson.jpg',
LOTACAOCOMPLETA: 'DIOPE/SUPOP/OPSRL/OPSMC (local)',
);
);
return success: fn=>callback = fn ;
as any;
login()
logout()
const KeycloakServiceImpl =
environment.production ? KeycloakService : MockKeycloakService
export KeycloakServiceImpl, KeycloakService, MockKeycloakService ;
luego lo sustituí en app.module:
<...>
import KeycloakAngularModule from 'keycloak-angular';
import KeycloakServiceImpl from 'src/app/shared/services/keycloak-mock.service';
import initializer from './app-init';
<...>
imports: [
KeycloakAngularModule,
<...>
],
providers: [
<...>,
provide: APP_INITIALIZER,
useFactory: initializer,
multi: true,
deps: [KeycloakServiceImpl, <...>]
,
<...>
],
bootstrap: [AppComponent]
})
export class AppModule
Luego cambié el tipo de variable de servicio de keycloak en app-init, ese fue el único cambio, pero luego pude eliminar la importación de KeycloackService ya que se proporciona en app.module:
import KeycloakUser from './shared/models/keycloakUser';
<...>
export function initializer(
keycloakService: any,
<...>
): () => Promise {
return (): Promise =>
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) =>
<...>
)
).catch((err: any) => rej(err));
res();
);
;
Pero en el componente todavía tengo que verificar en qué entorno estoy e instanciar la clase correctamente:
<...>
import MockKeycloakService from '../../shared/services/keycloak.mock.service';
import environment from '../../../environments/environment';
<...>
export class MainComponent implements OnInit, OnDestroy
<...>
keycloak: any;
constructor(
<...>
)
this.keycloak = (environment.production) ? KeycloakServiceImpl : new KeycloakServiceImpl();
async doLogout()
await this.keycloak.logout();
async doLogin()
await this.keycloak.login();
<...>
Backend:
Eso fue más fácil, nuevamente creé una clase KeycloakMock:
import KeyCloack from 'keycloak-connect';
class KeycloakMock
constructor(store, config)
//ignore them
middleware()
return (req, res, next) =>
next();
protect(req, res, next)
return (req, res, next) =>
next();
const exportKeycloak =
(process.env.NODE_ENV == 'local') ? KeycloakMock : KeyCloack;
export default exportKeycloak;
Luego sustituí la importación ‘keycloak-connect’ en app.js por esta clase, y todo funcionó bien. Se conecta al servicio real si configuro producción = true y se burla de ello con producción = false.
Solución muy fresca. Si alguien tiene algo que decir sobre mi implementación de la idea de @yurzui, me gustaría saber de usted.
Algunas notas:
-
Todavía no puedo deshacerme de tener que verificar el entorno en la clase del componente principal, como si hiciera esto en el módulo de clase simulada:
const KeycloakServiceImpl = environment.production ? KeycloakService : new MockKeycloakService()
app.module ya no funciona. y si hago esto en el componente principal:
constructor( <...> keycloakService: KeyclockServiceImpl; )
La compilación falla con un “KeyclockServiceImpl se refiere a un valor pero se usa como un tipo aquí”;
-
Tuve que exportar todas las clases o la compilación falla.
export KeycloakServiceImpl, KeycloakService, MockKeycloakService ;
valoraciones y comentarios
Si guardas algún atascamiento o capacidad de progresar nuestro noticia puedes añadir un exégesis y con gusto lo observaremos.