尝试使用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サーバOIDC OP8080AngularクライアントサイドアプリケーションOIDC RP(クライアント)4200

Keycloak服务器配置

添加和设置客户端

    1. 登录Keycloak管理控制台。

 

    1. 从左侧菜单栏选择demo领域。

 

    1. 在左侧菜单栏中点击“客户端”。将显示客户端列表。

 

    1. 点击右上角的“创建”按钮。

 

    1. 输入以下客户端设置值,并点击“保存”按钮。

客户端ID:kc-angular
客户端协议:openid-connect

2017-12-01_150702.png
2017-12-05_201708.png

“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的登录页面。

2017-12-01_151706.png

请使用「动作确认用的服务器配置」中的用户user001登录。如果之前的设置正确,您将看到以下屏幕显示。

2017-12-01_151905.png

通过这种方式,在屏幕上显示登录用户名,并成功登录到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日本語ドキュメント

广告
将在 10 秒后关闭
bannerAds