尝试使用Keycloak的客户端适配器(Angular版)
首先
在Keycloak的第六个圣诞日活动中,我们将尝试使用JavaScript(客户端)的客户端适配器(OpenID Connect版)。最近,除了iOS和Android的原生应用程序,还出现了使用客户端端MVC框架如Angular等的趋势,并且认为需要增加对安全地使用客户端应用程序的用例。这次我们将尝试在使用Angular的客户端应用程序中引入JavaScript(客户端)的客户端适配器(OpenID Connect版),并尝试连接到Keycloak服务器的方法。
用于动作确认的服务器配置。
本次试验中作为 OpenID Connect 的 RP(依赖方),我们选择使用 Angular 开发客户端应用程序。具体的开发环境如下所示。
-
- Node.js:8.9.1
-
- Angualr CLI:1.5.3
- Angular:5.0.3
此外,Keycloak服务器中创建了一个名为demo的领域(请参阅第3天的文章),并且假定用户user001作为可登录的用户存在。
尽管我们的试验环境都在本地计算机上运行,但每个服务器的角色和使用的端口如下:
Keycloak服务器配置
添加和设置客户端
-
- 登录Keycloak管理控制台。
-
- 从左侧菜单栏选择demo领域。
-
- 在左侧菜单栏中点击“客户端”。将显示客户端列表。
-
- 点击右上角的“创建”按钮。
-
- 输入以下客户端设置值,并点击“保存”按钮。
客户端ID:kc-angular
客户端协议:openid-connect
“Access Type”需要设置为public,因为在客户端应用程序中,由于没有安全地保存客户端凭据的方法,所以需要这样设置。
“有效的重定向URI”是指设置允许用作登录请求的重定向值的URI。如果Keycloak服务器收到了其他重定向URI的请求,将拒绝登录请求并返回错误信息。
“Web来源”用于设置允许进行CORS(跨域资源共享)请求的主机。如果没有设置Web来源,所有来自浏览器的JavaScript请求都会因为缺少CORS头而失败。
使用Angular创建客户端应用程序
创建一个Angular应用程序。
在本次的试验环境中,我们将创建一个只有登录用户才能访问的客户端应用程序。首先,执行以下 Angular CLI 命令,创建一个基于 Angular 的客户端应用程序。
ng new frontend
JavaScript客户端适配器的配置设置
为了设置JavaScript(客户端)的客户端适配器,在index.html文件的头部标签内,插入以下script标签。
<script src="//localhost:8080/auth/js/keycloak.js"></script>
插入标签的结果如下所示。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Frontend</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<script src="//localhost:8080/auth/js/keycloak.js"></script>
</head>
<body>
<app-root></app-root>
</body>
</html>
在这里,我们将JavaScript(客户端)的客户端适配器存储位置指定为localhost:8080/auth/js/keycloak.js,但要指定Keycloak服务器内的客户端适配器。这是因为我们在本地机器上运行了此次试用环境,所以在正式环境等情况下,请适当修改客户端适配器的存储位置。
创建Keycloak服务
使用Angular创建一个服务,以便从客户端应用程序连接到Keycloak服务器。请执行以下命令以创建服务。
ng generate service keycloak
在这项服务中,我们将进行服务初始化和获取登录用户信息的操作。
使用 JavaScript 客户端适配器,我们将创建一个在服务初始化时调用的 init() 函数,以便只有登录的用户才能访问该服务。在这个 init() 函数中,我们将从环境配置文件中调用 Keycloak 服务器的设置,并在以下代码中设置 Keycloak 服务器的 URL、Realm 和 Client。
const keycloakAuth: any = Keycloak({
url: environment.KEYCLOAK_URL,
realm: environment.KEYCLOAK_REALM,
clientId: environment.KEYCLOAK_CLIENTID
});
在这个init()函数中,我们调用了登录功能。在下面的代码中,如果未登录,将会重定向到Keycloak服务器,如果已登录,则执行客户端认证。由于这段代码没有设置flow参数,所以将使用默认的Authorization Code flow进行操作。在这种情况下,您需要在客户端的详细设置页面中将”Standard Flow的有效”设为”开启”。有关这些API的详细规范,请参阅Keycloak文档。
keycloakAuth.init({ onLoad: 'login-required' })
此外,为了获取登录用户的信息,我们将在init()函数中添加以下代码,并创建一个getUser()函数来从其他类中获取用户信息。
KeycloakService.auth.authz.loadUserProfile()
实施这些措施的结果如下。
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { User } from './user.model';
import { environment } from '../environments/environment';
declare var Keycloak: any;
@Injectable()
export class KeycloakService {
static auth: any = {};
static user: User;
static init(): Promise<any> {
const keycloakAuth: any = Keycloak({
url: environment.KEYCLOAK_URL,
realm: environment.KEYCLOAK_REALM,
clientId: environment.KEYCLOAK_CLIENTID
});
KeycloakService.auth.loggedIn = false;
return new Promise((resolve, reject) => {
keycloakAuth
.init({ onLoad: 'login-required' })
.success(() => {
KeycloakService.auth.loggedIn = true;
KeycloakService.auth.authz = keycloakAuth;
KeycloakService.auth.logoutUrl =
keycloakAuth.authServerUrl +
'/realms/' +
environment.KEYCLOAK_REALM +
'/protocol/openid-connect/logout?redirect_uri=' +
document.baseURI;
KeycloakService.auth.authz.loadUserProfile().success(data => {
this.user = new User();
this.user.username = data.username;
resolve();
});
})
.error(() => {
reject();
});
});
}
getUser(): User {
return KeycloakService.user;
}
}
为了能够使用创建的服务,我们需要将服务添加到src/app/app.module.ts文件中。
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { KeycloakService } from './keycloak.service';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule
],
providers: [ KeycloakService ],
bootstrap: [AppComponent]
})
export class AppModule { }
为了让JavaScript客户端适配器和Keycloak服务器进行通信,我们需要在环境配置文件中添加Keycloak服务器配置的客户端信息(Keycloak服务器的URL、领域和客户端)。
export const environment = {
production: false,
KEYCLOAK_URL: 'http://localhost:8080/auth',
KEYCLOAK_REALM: 'demo',
KEYCLOAK_CLIENTID: 'kc-angular'
};
为了使用用户登录信息,我们需要定义一个用户模型。
export class User {
username: string;
}
Keycloak服务的初始化
为了只允许登录的用户访问,我们将初始化Keycloak服务。在这里,只有当用户成功登录时,才会启动Angular模块。
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import { KeycloakService } from './app/keycloak.service';
if (environment.production) {
enableProdMode();
}
KeycloakService.init()
.then(() => platformBrowserDynamic().bootstrapModule(AppModule))
.catch(e => {
console.error(e);
});
获取用户信息
最后,我们要获取已登录用户的信息。由于登录已在启动过程中完成,所以我们将在应用程序组件的onInit阶段直接获取用户信息。
import { Component, OnInit } from '@angular/core';
import { User } from './user.model';
import { KeycloakService } from './keycloak.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
profile: User;
constructor(private keycloakService: KeycloakService ) {}
public ngOnInit(): void {
this.profile = this.keycloakService.getUser();
}
}
为了将获取到的用户信息输出到屏幕上,我们需要修改客户端应用程序的HTML界面。
<div style="text-align:center">
<h1>Welcome {{profile.username}}!</h1>
</div>
确认行动
使用以下命令启动由Angular前端应用程序创建的客户端应用。
ng serve
访问http://localhost:4200时,将被重定向到Keycloak的登录页面。
请使用「动作确认用的服务器配置」中的用户user001登录。如果之前的设置正确,您将看到以下屏幕显示。
通过这种方式,在屏幕上显示登录用户名,并成功登录到Keycloak,可以确认已从Keycloak服务器成功获取到用户信息。
总结
在今天的Keycloak圣诞日历中,我们尝试了JavaScript(客户端)的客户端适配器(OpenID Connect版)。除此之外,Keycloak的文档还记录了与iOS和Android原生应用程序的集成方法,所以如果你感兴趣,请参考官方文档。
可以提供以下资料供参考
Keycloak Documentation
1.2. Supported Platforms
2.2. Javascript Adapter
Angular Quick Start
Angular Tutorial: Tour of Heroes
Authentication with Spring Boot, Angular 2 and Keycloak
NRI OpenStandia Keycloak日本語ドキュメント