guideSpring Boot

本指南介绍了在 Spring Boot 应用程序中集成 CKBox 的方法。如果你想直接跳到代码,可以在 GitHub 上找到本指南中描述的 应用程序的完整代码

# 先决条件

在开始之前,请确保你的系统中安装了最新版本的 JDK。如果命令行中没有此工具,请先按照 JDK 安装说明 进行操作。

# 创建应用程序

作为示例的基础,我们将使用 Spring Initializr 向导创建一个新的 Spring Boot 项目。本文档中的示例将使用 Maven。如果你想一步一步地遵循本指南,请将你的项目类型设置为“Maven”。你也可以使用其他项目类型,并将命令和配置替换为其他工具中的等效项。

在本指南中创建项目时使用的参数

  • 组:io.ckbox
  • 工件:spring-example
  • 名称:spring-example
  • 项目:Maven
  • 语言:Java
  • 依赖项:Spring Web

项目创建后,启动开发服务器

./mvnw spring-boot:run

# 创建授权控制器

首先,让我们创建一个控制器。控制器将负责服务令牌端点,用于在 CKBox 中对用户进行身份验证。

CKBox 以及其他 CKEditor 云服务使用 JWT 令牌进行身份验证和授权。所有这些令牌都在你的应用程序端生成,并使用你可以在 CKEditor 生态系统仪表板 中获取的共享密钥进行签名。有关如何创建访问凭据的信息,请参阅 身份验证指南 中的 创建访问凭据 部分。

现在我们有了所需的访问凭据,即:环境 ID 和访问密钥,让我们创建令牌端点。

首先,让我们将用于创建 JWT 令牌的 com.auth0.jwt 库添加到 pom.xml 文件中

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>4.4.0</version>
</dependency>

该依赖项将在下次应用程序启动时安装。

# 完整控制器代码

// src/main/java/io/ckbox/springexample/CKBoxController.java

package io.ckbox.springexample;

import java.time.Instant;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.*;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping;

import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.JWT;

@RestController
public class CKBoxController {
    @Value("${environment_id}")
    private String environmentId;

    @Value("${access_key}")
    private String accessKey;

    @GetMapping("/ckbox/auth")
    public String tokenEndpoint() throws Exception {
        Map<String, Object> authClaim = new HashMap<>() {{
            put("ckbox", new HashMap<>() {{
                put("role", "user");
            }});
        }};

        Algorithm algorithm = Algorithm.HMAC256(this.accessKey.getBytes("ASCII"));

        String token = JWT.create()
            .withAudience(this.environmentId)
            .withIssuedAt(Instant.now())
            .withSubject("user id")
            .withClaim("auth", authClaim)
            .sign(algorithm);

        return token;
    }
}

如上面的代码清单所示,签名 JWT 令牌所需的访问凭据是从环境变量中获取的。现在,你可以使用以下命令运行应用程序

environment_id=REPLACE-WITH-ENVIRONMENT-ID access_key=REPLACE-WITH-ACCESS-KEY ./mvnw spring-boot:run

# 将 CKBox 脚本添加到页面

CKBox 可以使用多种方式嵌入应用程序中,有多种方法。为了简单起见,我们将使用从 CDN 提供的 CKBox 脚本。

本指南中的示例将涵盖三种常见的 CKBox 使用场景

  • CKBox 与 CKEditor 5 集成。
  • CKBox 用作对话框模式下的文件选择器。
  • CKBox 用作全页面应用程序。

为了避免代码重复,让我们准备一个包含 CKBox 脚本的基本 Thymeleaf 布局模板,我们将在所有三个示例中重复使用它。首先,将 Thymeleaf 依赖项添加到 pom.xml 文件中

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
    <groupId>nz.net.ultraq.thymeleaf</groupId>
    <artifactId>thymeleaf-layout-dialect</artifactId>
</dependency>

然后,你可以创建一个布局文件

<!-- src/main/resources/templates/layout.html -->
<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<html>
<head>
    <meta charset="UTF-8"/>
    <title>CKBox example</title>
    <script src="https://cdn.ckbox.io/ckbox/2.1.0/ckbox.js"></script>
</head>
<body>
    <section layout:fragment="content">
        <p>Page content goes here</p>
    </section>
</body>
</html>

最后,为示例准备一个 MVP 控制器

// src/main/java/io/ckbox/springexample/CKBoxExampleWebpageController.java
package io.ckbox.springexample;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;

@Controller
public class CKBoxExampleWebpageController {
}

# CKBox 与 CKEditor 5

在本示例中,我们将使用最快速、最简单的方法运行 CKEditor 5 – 从 CDN 提供。有关更高级的集成场景,请参阅 CKEditor 5 文档

让我们创建扩展我们之前创建的布局的子视图

<!-- src/main/resources/templates/ckbox-example-ckeditor.html -->
<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
    layout:decorate="~{layout}">
<head>
    <script type="importmap">
        {
            "imports": {
                "ckeditor5": "https://cdn.ckeditor.com/ckeditor5/42.0.0/ckeditor5.js",
                "ckeditor5/": "https://cdn.ckeditor.com/ckeditor5/42.0.0/"
            }
        }
    </script>
    <link rel="stylesheet" href="https://cdn.ckeditor.com/ckeditor5/42.0.0/ckeditor5.css" />
</head>
<body>
    <section layout:fragment="content">
        <textarea id="editor"><h1>Example</h1><p>Hello world</p></textarea>

        <script type="module">
            import {
                ClassicEditor,
                CKBox,
                Essentials,
                Bold,
                Italic,
                Font,
                Paragraph
            } from 'ckeditor5';

            ClassicEditor
                .create( document.querySelector( '#editor' ), {
                    plugins: [ CKBox, Essentials, Bold, Italic, Font, Paragraph ],
                    ckbox: {
                        tokenUrl: 'https://your.token.url',
                        theme: 'lark',
            allowExternalImagesEditing: [ /^data:/, 'origin', /ckbox/ ]
                    },
                    toolbar: [
                        'ckbox', '|', 'undo', 'redo', '|', 'bold', 'italic', '|',
                        'fontSize', 'fontFamily', 'fontColor', 'fontBackgroundColor'
                    ],
                } )
                .catch( error => {
                    console.error( error );
                } );
        </script>
    </section>
</body>
</html>

并将视图添加到控制器中

@Controller
public class CKBoxExampleWebpageController {
    @GetMapping("/ckbox-example-ckeditor")
    public String ckeditorExample(Model model) {
        return "ckbox-example-ckeditor";
    }
}

上面的示例包含从 CDN 提供的 预定义的 CKEditor 5 版本,其中包括所需的 CKBox 插件。然后,通过设置 ckbox 属性的必需参数,将 CKEditor 5 配置为使用 CKBox。请注意,在 ckbox.tokenUrl 配置选项中,我们传递了之前步骤中创建的令牌端点的 URL。它将用于获取用于在 CKBox 中对用户进行身份验证的 JWT 令牌。

# CKBox 作为文件选择器

常见的场景之一是使用 CKBox 作为文件选择器,用户可以选择文件管理器中存储的文件之一。选择文件后,我们要获取有关所选文件的信息,特别是它们的 URL。这可以通过使用作为 CKBox 配置选项传递的 assets.onChoose 回调来实现

<!-- src/main/resources/templates/ckbox-example-modal.html -->
<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
    layout:decorate="~{layout}">
<body>
    <section layout:fragment="content">
        <input type="text" id="file-url"></input><button id="choose">Choose file</button>
        <div id="ckbox"></div>
        <script>
            document.getElementById('choose').addEventListener('click', function () {
                CKBox.mount(document.querySelector('#ckbox'), {
                    tokenUrl: `${ location.origin }/ckbox/auth`,
                    dialog: true,
                    assets: {
                        // Callback executed after choosing assets
                        onChoose: (assets) => {
                            document.getElementById('file-url').value = assets[0].data.url;

                            assets.forEach((asset) => {
                                console.log(asset.data.url);
                            })
                        }
                    }
                });
            });
        </script>
    </section>
</body>
</html>

@Controller
public class CKBoxExampleWebpageController {
    // ...

    @GetMapping("/ckbox-example-modal")
    public String modalExample(Model model) {
        return "ckbox-example-modal";
    }
}

# CKBox 在全屏模式下

要以全屏模式启动 CKBox,你可以将它附加到 document.body 并调整所需的 CSS 样式

<!-- src/main/resources/templates/ckbox-example-full-page.html -->
<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
    layout:decorate="~{layout}">
    <style>
        html, body {
            margin: 0;
            padding: 0;
            height: 100vh;
        }
    </style>
<body>
    <section layout:fragment="content">
        <script>
            CKBox.mount(document.body, {
                tokenUrl: `${ location.origin }/ckbox/auth`
            });
        </script>
    </section>
</body>
</html>
@Controller
public class CKBoxExampleWebpageController {
    // ...

    @GetMapping("/ckbox-example-full-page")
    public String fullPageExample(Model model) {
        return "ckbox-example-full-page";
    }
}

# 完整代码

您可以在 GitHub 上找到本指南中描述的 应用程序的完整代码