react hooks_使用Spring Boot,JHipster和React构建照片库PWA

react hooks

react hooks

“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证。

从本质上讲,React只是一个UI工具包,即ala GWT,但它周围有一个非常健康的生态系统,它提供构建踢踏式渐进式Web应用程序(PWA)所需的一切。 PWA很酷,因为如果操作正确,它们可以为您的用户提供类似本机的体验,允许他们安装您的应用程序,并在脱机时使用它。

但是,“为什么要使用React?” 您现在可能会问自己是什么,对吗? 好吧,您可能听说过Angular可以成为希望学习JavaScript的Java开发人员的入门药物。 如果您是一位熟悉Angular的经验丰富的Java开发人员,那么您很可能会从AngularJS开始。 AngularJS具有与Java MVC框架类似的概念,例如控制器,服务和指令(我相信它们类似于JSP标签恕我直言)。 如果您仍在进行Angular开发,则可能会沿途学习TypeScript。 您喜欢TypeScript,因为它具有Java之类的类型,并且它也是一种非常不错的语言!

我敢打赌,如果您已经了解Angular,那么您可能想了解React的主要竞争对手。 总是有几种编写Web应用程序的方法,而React提供了一种完全不同的方法来执行它,您也可以将TypeScript与它一起使用!

在这篇文章中,我将向您展示如何构建一个安全的PWA,该PWA可以上载和处理图像,将它们显示在类似Flickr的网格中,并使用Spring Boot作为其后端。

React和Spring Boot入门

开始使用React的最简单方法之一就是使用Create React App (CRA)。 您在本地安装它,然后运行create-react-app $projectName生成具有最小依赖性的框架React应用程序。 它使用秘密的webpack构建项目,启动Web服务器并运行其测试。

Spring Boot有一个类似的工具,称为Spring Initializr 。 Spring Initializer与CRA有所不同,因为它是您用来创建应用程序的网站(和API)。

这两个工具都值得研究,您可以通过阅读我的《 使用Spring Boot和React进行Bootiful开发》教程来学习如何使用它们创建基本应用。

今天,我将向您展示如何使用React和Spring Boot为照片构建CRUD应用程序。 但是,我要作弊。 我将使用JHipster而不是从头开始构建所有内容。 JHipster是一个应用程序生成器,最初仅支持Angular和Spring Boot。 在其5.0版本中,它增加了对React,webpack 4和Spring Boot 2的支持。

JHipster附带了每个应用程序都需要的许多功能,包括身份验证/授权,单元和端到端测试支持,以及使其易于部署到云中的工具。

JHipster 5入门

要开始使用JHipster,你需要有互联网连接和Node.js的安装。 该项目建议您使用最新的LTS(长期支持)版本,在撰写本文时为8.3.11。 您可以使用npm,但如果安装了JHipster,它将使用Yarn 。 要运行该应用程序,您需要安装Java 8 。 如果安装了Git,JHipster将在创建项目后自动提交您的项目,并允许您在版本之间进行升级。

运行以下命令来安装JHipster:

npm i -g generator-jhipster@5.0.1

要使用JHipster创建图库应用程序,请创建目录并在其中运行jhipster

mkdir gallery
cd gallery
jhipster

JHipster询问有关您要创建的应用程序类型以及要包括哪些技术的许多问题。 下表显示了您要进行的选择:

回答
应用类型? Monolithic application
名称? gallery
Java包名称? com.okta.developer
使用JHipster注册表? No
认证类型? OAuth 2.0 / OIDC
数据库类型? SQL
生产数据库? PostgreSQL
开发数据库? H2 with disk-based persistence
使用Spring缓存? Yes, with Ehcache
使用Hibernate 2级缓存? Yes
Maven还是Gradle? Maven
其他技术? <blank>
客户框架? React
启用S​​ASS支持? No
启用i18n? Yes
应用程序的母语? English
其他语言? French
其他测试框架? Protractor
安装其他发电机? No

回答完所有这些问题之后,JHipster将在当前目录中创建大量文件,然后运行yarn (或npm install )以安装package.json指定的所有依赖项。

验证一切适用于量角器和Keycloak

选择OAuth 2.0和OIDC进行身份验证时,用户存储在应用程序外部,而不是存储在应用程序中。 这意味着您需要拥有一个身份提供商(IdP),该身份提供商可以存储您的用户并允许您的应用检索有关他们的信息。 默认情况下,JHipster随附用于Docker Compose的Keycloak文件。 默认的用户和组集在启动时被导入,并且为您的JHipster应用程序注册了一个客户端。

这是应用程序的src/main/docker目录中的keycloak.yml样子:

version: '2'
services:
  keycloak:
    image: jboss/keycloak:4.0.0.Final
    command: ["-b", "0.0.0.0", "-Dkeycloak.migration.action=import", "-Dkeycloak.migration.provider=dir", "-Dkeycloak.migration.dir=/opt/jboss/keycloak/realm-config", "-Dkeycloak.migration.strategy=OVERWRITE_EXISTING", "-Djboss.socket.binding.port-offset=1000"]
    volumes:
      - ./realm-config:/opt/jboss/keycloak/realm-config
    environment:
      - KEYCLOAK_USER=admin
      - KEYCLOAK_PASSWORD=admin
    ports:
      - 9080:9080
      - 9443:9443
      - 10990:10990

要启动Keycloak,您需要安装Docker Compose 。 然后在终端窗口中运行以下命令:

docker-compose -f src/main/docker/keycloak.yml up

您可以使用Maven在一个终端中启动应用程序,从而从一开始就验证一切正常:

./mvnw

然后在另一个终端上运行所有的量角器测试:

yarn e2e

如果您的环境设置正确,您将看到类似以下的输出:

yarn run v1.7.0
$ protractor src/test/javascript/protractor.conf.js
(node:97048) [DEP0022] DeprecationWarning: os.tmpDir() is deprecated. Use os.tmpdir() instead.
[15:36:33] W/configParser - pattern ./e2e/entities/**/*.spec.ts did not match any files.
[15:36:33] I/launcher - Running 1 instances of WebDriver
[15:36:33] I/direct - Using ChromeDriver directly...

  Account
    ✓ should fail to login with bad password
    ✓ should login with admin account (2720ms)

  Administration
    ✓ should load metrics
    ✓ should load health
    ✓ should load configuration
    ✓ should load audits
    ✓ should load logs

  7 passing (10s)

[15:36:45] I/launcher - 0 instance(s) of WebDriver still running
[15:36:45] I/launcher - chrome #01 passed
✨  Done in 13.67s.

在Keycloak中为您的React + Spring Boot App启用用户注册

在JHipster中使用OIDC身份验证时,似乎缺少的功能之一是用户注册。 如果您使用会话或JWT身份验证,则主页上会提供一个注册链接。 使用OIDC,您需要在IdP中启用它。 对于Keycloak,您可以通过导航到http://localhost:9080并单击Administration Console来实现。 使用admin/admin登录,然后单击“登录”选项卡。 该屏幕允许您启用忘记密码,记住我以及通过电子邮件进行验证。

照片库

启用此设置后,您将在Keycloak的登录表单上看到“注册”链接。

照片库

您需要在Keycloak中为新用户配置默认角色。 导航到角色,然后单击默认角色选项卡。 选择ROLE_USER ,然后单击“添加所选内容” 。 要配置默认组,请转到“组” >“默认组” 。 单击Users然后添加。 添加默认组是必要的,因为JHipster希望用户将一个ROLE_USERROLE_ADMIN组(或角色)作为其ID令牌声明的一部分。

保存JPA关系的用户数据

我添加到JHipster的功能之一是我喜欢称之为“保存用户快照” 。 使用JPA时,很高兴能够与JHipster的User实体建立关系。 这样一来,您就可以说出“此用户拥有此相册”之类的内容,并根据该信息限制访问。

此功能默认情况下处于启用状态,其工作方式如下:

  1. 登录后, /api/account发出请求。
  2. AccountResource.javagetAccount()方法映射到此端点,并UserService#getUserFromAuthentication()UserService#getUserFromAuthentication()以提取用户的详细信息。
  3. getUserFromAuthentication()方法从Spring Security中提取用户的详细信息,将ID令牌中的组/角色映射到授权机构,并在数据库中添加/更新用户。

此功能使您可以与User实体创建关系。 唯一的缺点是,当您具有具有用户关系的实体时,“用户”下拉列表将仅包含已登录您的应用程序的用户。

将您的身份提供者更改为Okta

JHipster利用Spring Security的OAuth 2.0支持来配置应该从中获取用户信息的IDP。 将Spring Security与Spring Boot结合使用时,可以在属性文件中配置大多数配置设置。 您甚至可以使用环境变量覆盖属性。

要从Keycloak切换到Okta(或其他任何IdP),您可以覆盖默认属性(对于Spring Security OAuth)。

为什么用Okta代替Keycloak?

Keycloak在开发中表现出色,Okta具有免费的多因素身份验证,电子邮件支持以及出色的生产性能。 您可以在developer.okta.com/pricing上查看其他免费功能和我们透明的价格。

若要查看其工作原理,请创建具有以下属性的~/.okta.env文件:

export SECURITY_OAUTH2_CLIENT_ACCESS_TOKEN_URI="https://{yourOktaDomain}/oauth2/default/v1/token"
export SECURITY_OAUTH2_CLIENT_USER_AUTHORIZATION_URI="https://{yourOktaDomain}/oauth2/default/v1/authorize"
export SECURITY_OAUTH2_RESOURCE_USER_INFO_URI="https://{yourOktaDomain}/oauth2/default/v1/userinfo"
export SECURITY_OAUTH2_CLIENT_CLIENT_ID="{clientId}"
export SECURITY_OAUTH2_CLIENT_CLIENT_SECRET="{clientSecret}"

您需要在Okta中创建一个新的OIDC客户端,然后填写变量,然后才能运行。 完成此操作后,可以运行以下命令来设置这些环境变量。

source ~/.okta.env

重新启动您的应用程序,-您现在正在使用Okta!

如果您不知道如何在Okta上设置OIDC应用程序,这里有一个简短的摘要。

在Okta上设置OIDC应用

登录到您的1563开发者帐户(或者注册,如果你没有一个帐户)并导航到应用程序>添加应用程序。 单击“ Web” ,然后单击“下一步” 。 给应用程序起一个您会记住的名称,并指定http://localhost:8080/login作为登录重定向URI。 单击“完成”,并记下客户端ID和密码。 您需要在一分钟内将它们复制/粘贴到文件中。

创建一个ROLE_ADMINROLE_USER组(“用户” >“组” >“添加组” )并将用户添加到其中。 我建议将您注册时使用的帐户添加到ROLE_ADMIN并创建一个新用户(“用户” >“添加人” )以添加到ROLE_USER

导航到API >授权服务器,然后单击一个名为default的名称进行编辑。 点击索赔标签,然后添加索赔。 将其命名为“角色”,并将其包含在ID令牌中。 将值类型设置为“ Groups”,并将过滤器设置为.*的正则表达式。 单击创建以完成该过程。

什么是Okta?

简而言之,我们使身份管理比您通常使用的更加轻松,安全和可扩展。 Okta是一项云服务,允许开发人员创建,编辑和安全地存储用户帐户和用户帐户数据,并将它们与一个或多个应用程序连接。 我们的API使您能够:

想要每月免费提供一千个用户吗? 注册一个免费的开发人员帐户,完成后再回来,这样您就可以了解有关使用Spring Boot 2.0和JHipster构建React PWA的更多信息!

在Okta启用自助服务注册

要在Okta中启用自助服务注册,您需要从Okta Developer仪表板导航到Classic UI。 屏幕的左上角有一个链接可在两者之间切换。

照片库

然后导航至目录>自我注册,然后单击启用注册。 将默认组设置为ROLE_USER ,将默认组重定向到以http://localhost:8080作为其值的自定义URL,然后单击保存

注意:如果收到错误消息'http://localhost:8080' is not a valid redirect URI ,那是因为需要在安全性> API >可信来源下将http://localhost:8080为可信重定向。 进行更改后,导航至目录>自助服务注册,然后编辑设置以再次配置自定义URL。 这次应该可以了。

照片库

提示:部署应用程序后,您需要将默认重定向更改为生产URL。

Okta自定义选项

除了允许自我注册外,Okta还允许您自定义其登录屏幕的外观,以及使用自定义域和电子邮件。 您可以在“ 登录窗口小部件指南”中阅读有关此内容的更多信息。

您也可以尝试使用我们方便的实时窗口小部件页面实时自定义窗口小部件

创建实体以允许在您的图库上使用CRUD

我花了很多时间讨论如何保护您的应用程序,现在让我们开始构建它! JHipster具有JDL(JHipster域语言)功能,可让您在应用程序中建模数据并从中生成实体。 您可以使用其JDL Studio功能在线完成此操作,并在完成后将其保存在本地。

我为此应用创建了一个数据模型,该数据模型具有一个AlbumPhotoTag实体,并在它们之间建立了关系。 下面是JDL Studio的屏幕截图。

照片库

为了方便起见,您可以复制下面的JDL并将其保存在项目根目录下的gallery.jh文件中。

entity Album {
    title String required,
    description TextBlob,
    created Instant
}

entity Photo {
    title String required,
    description TextBlob,
    image ImageBlob required,
    height Integer,
    width Integer,
    taken Instant,
    uploaded Instant
}

entity Tag {
    name String required minlength(2)
}

relationship ManyToOne {
    Album{user(login)} to User,
    Photo{album(title)} to Album
}

relationship ManyToMany {
    Photo{tag(name)} to Tag{photo}
}

paginate Album with pagination
paginate Photo, Tag with infinite-scroll

您可以使用以下命令生成实体和CRUD代码(用于Spring Boot的Java;用于React的TypeScript和JSX):

jhipster import-jdl gallery.jh

出现提示时,键入a以允许覆盖现有文件。

该过程将创建Liquibase changelog文件(以创建数据库表),实体,存储库,Spring MVC控制器以及创建,读取,更新和删除数据对象所需的所有React代码。 它甚至会生成Jest单元测试和量角器端到端测试!

该过程完成后,您可以重新启动应用程序(按住Ctrl + C的./mvnw进程并重新启动),然后再次运行yarn e2e ,以快速确认所有内容./mvnw正确生成。

现在,您可以看到JHipster非常强大。 它认识到您具有ImageBlob类型的image属性,并自动创建了在数据库中上传和存储图像所需的管道! 晕!

在Spring Boot API中添加Image EXIF处理

Photo实体具有一些属性,可以通过从上载的照片中读取EXIF(可交换图像文件格式)数据来计算。 您可能会问,如何用Java做到这一点?

值得庆幸的是,Drew Noakes创建了一个元数据提取程序库来完成此任务。 在您的pom.xml添加对Drew库的依赖:

<dependency>
    <groupId>com.drewnoakes</groupId>
    <artifactId>metadata-extractor</artifactId>
    <version>2.11.0</version>
</dependency>

然后,修改PhotoResource#createPhoto()方法以设置上载图像时的元数据。

import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.metadata.Metadata;
import com.drew.metadata.MetadataException;
import com.drew.metadata.exif.ExifSubIFDDirectory;
import com.drew.metadata.jpeg.JpegDirectory;

import javax.xml.bind.DatatypeConverter;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import java.time.Instant;
import java.util.Date;

public class PhotoResource {
    ...

    public ResponseEntity createPhoto(@Valid @RequestBody Photo photo) throws Exception {
        log.debug("REST request to save Photo : {}", photo);
        if (photo.getId() != null) {
            throw new BadRequestAlertException("A new photo cannot already have an ID", ENTITY_NAME, "idexists");
        }

        try {
            photo = setMetadata(photo);
        } catch (ImageProcessingException ipe) {
            log.error(ipe.getMessage());
        }

        Photo result = photoRepository.save(photo);
        return ResponseEntity.created(new URI("/api/photos/" + result.getId()))
            .headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString()))
            .body(result);
    }

    private Photo setMetadata(Photo photo) throws ImageProcessingException, IOException, MetadataException {
        String str = DatatypeConverter.printBase64Binary(photo.getImage());
        byte[] data2 = DatatypeConverter.parseBase64Binary(str);
        InputStream inputStream = new ByteArrayInputStream(data2);
        BufferedInputStream bis = new BufferedInputStream(inputStream);
        Metadata metadata = ImageMetadataReader.readMetadata(bis);
        ExifSubIFDDirectory directory = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class);

        if (directory != null) {
            Date date = directory.getDateDigitized();
            if (date != null) {
                photo.setTaken(date.toInstant());
            }
        }

        if (photo.getTaken() == null) {
            log.debug("Photo EXIF date digitized not available, setting taken on date to now...");
            photo.setTaken(Instant.now());
        }

        photo.setUploaded(Instant.now());

        JpegDirectory jpgDirectory = metadata.getFirstDirectoryOfType(JpegDirectory.class);
        if (jpgDirectory != null) {
            photo.setHeight(jpgDirectory.getImageHeight());
            photo.setWidth(jpgDirectory.getImageWidth());
        }

        return photo;
    }
    ...
}

由于要提取信息,因此可以从UI中删除字段并进行测试,以便用户无法设置这些值。

src/main/webapp/app/entities/photo/photo-update.tsx ,添加metadatametadataRows变量,以及在添加照片时将其隐藏的逻辑和在更新照片时使其src/main/webapp/app/entities/photo/photo-update.tsx只读的逻辑。 在下面的代码块中找到第一行,并将其替换为以下代码。

const { description, image, imageContentType } = photoEntity;

const metadata = (
  <div>
    <AvGroup>
      <Label id="heightLabel" for="height">
        <Translate contentKey="galleryApp.photo.height">Height</Translate>
      </Label>
      <AvField id="photo-height" type="number" className="form-control" name="height" readOnly />
    </AvGroup>
    <AvGroup>
      <Label id="widthLabel" for="width">
        <Translate contentKey="galleryApp.photo.width">Width</Translate>
      </Label>
      <AvField id="photo-width" type="number" className="form-control" name="width" readOnly />
    </AvGroup>
    <AvGroup>
      <Label id="takenLabel" for="taken">
        <Translate contentKey="galleryApp.photo.taken">Taken</Translate>
      </Label>
      <AvInput
        id="photo-taken"
        type="datetime-local"
        className="form-control"
        name="taken"
        readOnly
        value={isNew ? null : convertDateTimeFromServer(this.props.photoEntity.taken)}
      />
    </AvGroup>
    <AvGroup>
      <Label id="uploadedLabel" for="uploaded">
        <Translate contentKey="galleryApp.photo.uploaded">Uploaded</Translate>
      </Label>
      <AvInput
        id="photo-uploaded"
        type="datetime-local"
        className="form-control"
        name="uploaded"
        readOnly
        value={isNew ? null : convertDateTimeFromServer(this.props.photoEntity.uploaded)}
      />
    </AvGroup>
  </div>
);
const metadataRows = isNew ? '' : metadata;

然后,在return块中,删除image属性和album属性之间的JSX并将其替换为{metadataRows}

<input id="file_image" type="file" onChange={this.onBlobChange(true, 'image')} accept="image/*" />
  </AvGroup>
</AvGroup>
{metadataRows}
<AvGroup>
  <Label for="album.title">
    <Translate contentKey="galleryApp.photo.album">Album</Translate>
  </Label>

src/test/javascript/e2e/entities/photo/photo.spec.ts ,删除在以下字段中设置数据的代码:

photoUpdatePage.setHeightInput('5');
expect(await photoUpdatePage.getHeightInput()).to.eq('5');
photoUpdatePage.setWidthInput('5');
expect(await photoUpdatePage.getWidthInput()).to.eq('5');
photoUpdatePage.setTakenInput('01/01/2001' + protractor.Key.TAB + '02:30AM');
expect(await photoUpdatePage.getTakenInput()).to.contain('2001-01-01T02:30');
photoUpdatePage.setUploadedInput('01/01/2001' + protractor.Key.TAB + '02:30AM');
expect(await photoUpdatePage.getUploadedInput()).to.contain('2001-01-01T02:30');

您还可以在src/test/javascript/e2e/entities/photo/photo-update.page-object.ts删除这些字段的所有getter和setter:

setHeightInput(height) {
  this.heightInput.sendKeys(height);
}

getHeightInput() {
  return this.heightInput.getAttribute('value');
}

setWidthInput(width) {
  this.widthInput.sendKeys(width);
}

getWidthInput() {
  return this.widthInput.getAttribute('value');
}

setTakenInput(taken) {
  this.takenInput.sendKeys(taken);
}

getTakenInput() {
  return this.takenInput.getAttribute('value');
}

setUploadedInput(uploaded) {
  this.uploadedInput.sendKeys(uploaded);
}

getUploadedInput() {
  return this.uploadedInput.getAttribute('value');
}

停止您的Maven进程,运行yarn webpack:build ,再次启动Maven,然后运行yarn e2e以确保一切仍然正常。 如果上传使用智能手机拍摄的图像,则高度,宽度和拍摄值均应填充。 如果不是,则很可能您的图像中没有数据。

需要一些带有EXIF数据的样本照片吗? 您可以从Flickr上的相册中下载我的1966 VW Bus的图片。

将React照片库添加到您的React PWA中

您已将元数据提取添加到后端,但是您的照片仍显示在列表中,而不是显示在网格中(例如Flickr)。 要解决此问题,您可以使用React Photo Gallery组件。 使用Yarn安装它:

yarn add react-photo-gallery@6.0.28

或npm:

npm i --save-exact react-photo-gallery@6.0.28

注意:我首先尝试使用Leisan Kazberova的react-photo-feed ,但是在将其添加到我的项目后发现它导致了编译错误。

src/main/webapp/app/entities/photo/photo.tsx ,为Gallery添加一个导入:

import Gallery from 'react-photo-gallery';

然后在结束</h2>之后,在render()方法和<Gallery>组件中添加一个photoSet变量。

render() {
  const { photoList, match } = this.props;
  const photoSet = photoList.map(photo => ({
    src: `data:${photo.imageContentType};base64,${photo.image}`,
    width: photo.height > photo.width ? 3 : photo.height === photo.width ? 1 : 4,
    height: photo.height > photo.width ? 4 : photo.height === photo.width ? 1 : 3
  }));

  return (
    <div>
      <h2 id="photo-heading">
        ...
      </h2>
      <Gallery photos={photoSet} />
      ...
  );
}

由于您只修改了前端代码,因此您可以运行yarn start启动webpack-dev-server实例,该实例代理对后端的请求,并在每次更改任何React文件时自动刷新浏览器(使用Browsersync)。

登录并导航到顶部导航栏中的实体>照片。 您应该可以上传照片,并在列表顶部的漂亮网格中查看结果。

照片库

您还可以在网格中添加“灯箱”功能,以便单击照片并放大。ReactPhoto Gallery文档显示了如何执行此操作。 我已经将其集成到本文的示例中,但是为了简洁起见,这里不会显示代码。 您可以在GitHub上看到添加了Lightbox最终photo.tsx所需更改差异

使您的React + Spring Boot App成为PWA

成为PWA需要具备以下三个功能:

  1. 您的应用必须通过HTTPS提供
  2. 您的应用必须注册服务工作者,才能缓存请求并脱机工作
  3. 您的应用必须具有包含安装信息和图标的webapp清单

对于HTTPS,您可以为本地主机设置证书,或者(甚至更好)将其部署到生产环境! 像Heroku和Cloud Foundry这样的云提供商将为您提供现成的HTTPS,但他们不会强制使用HTTPS。 要强制使用HTTPS,请打开src/main/java/com/okta/developer/config/SecurityConfiguration.java并添加一条规则,以在发送X-Forwarded-Proto标头时强制使用安全通道。

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        ...
    .and()
        .headers()
        .frameOptions()
        .disable()
    .and()
        .requiresChannel()
        .requestMatchers(r -> r.getHeader("X-Forwarded-Proto") != null)
        .requiresSecure()
    .and()
        .authorizeRequests()
        ...
}

已经对workbox-webpack-plugin进行了配置,以生成服务工作者,但是仅在使用生产配置文件运行您的应用程序时它才有效。 很好,因为这意味着在开发时不会在浏览器中缓存数据。

要注册服务工作者,请打开src/main/webapp/index.html并取消注释以下代码块。

<script>
    if ('serviceWorker' in navigator) {
         navigator.serviceWorker
            .register('./service-worker.js')
            .then(function() { console.log('Service Worker Registered'); });
    }
</script>

src/main/webapp/manifest.webapp包含了最后一个功能src/main/webapp/manifest.webapp 。 它定义了应用程序名称,颜色和图标。 您可能需要调整这些值以适合您的应用程序。

将React + Spring Boot应用程序部署到Heroku

要将应用程序部署到Heroku,首先需要安装Heroku CLI 。 您可以通过运行heroku --version确认其安装。

如果您没有Heroku帐户,请访问heroku.com并注册。 不用担心,它是免费的,而且您很可能会喜欢这种体验。

运行heroku login以登录到您的帐户,然后使用JHipster启动部署过程:

jhipster heroku

这将启动Heroku子生成器,该生成器会向您询问有关您的应用程序的几个问题:您要为其命名的名称以及是否要将其部署到美国地区或欧盟。 然后,它将提示您选择在本地构建还是在Heroku的服务器上使用Git进行选择。 选择Git,这样您就不必上载繁琐的JAR,部署过程就会开始。

如果您拥有稳定且快速的互联网连接,则您的应用程序应在六分钟左右的时间内在互联网上启动!

remote: -----> Compressing...
remote:        Done: 134.5M
remote: -----> Launching...
remote:        Released v5
remote:        https://gallery-pwa.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/gallery-pwa.git
 * [new branch]      HEAD -> master

Your app should now be live. To view it run
    heroku open
And you can view the logs with this command
    heroku logs --tail
After application modification, redeploy it with
    jhipster heroku
Congratulations, JHipster execution is complete!
Execution time: 5 min. 31 s. sec

配置Okta的React + Spring Boot应用程序并使用Lighthouse分析您的PWA分数

要将您的应用程序配置为在Heroku上与Okta一起使用,请运行以下命令,将与Okta相关的本地环境变量传输到Heroku。

heroku config:set \
  SECURITY_OAUTH2_CLIENT_ACCESS_TOKEN_URI="$SECURITY_OAUTH2_CLIENT_ACCESS_TOKEN_URI" \
  SECURITY_OAUTH2_CLIENT_USER_AUTHORIZATION_URI="$SECURITY_OAUTH2_CLIENT_USER_AUTHORIZATION_URI" \
  SECURITY_OAUTH2_RESOURCE_USER_INFO_URI="$SECURITY_OAUTH2_RESOURCE_USER_INFO_URI" \
  SECURITY_OAUTH2_CLIENT_CLIENT_ID="$SECURITY_OAUTH2_CLIENT_CLIENT_ID" \
  SECURITY_OAUTH2_CLIENT_CLIENT_SECRET="$SECURITY_OAUTH2_CLIENT_CLIENT_SECRET"

Heroku重新启动您的应用程序后,登录,然后使用Lighthouse对其进行测试。 看起来不错,是吗? 💯

照片库

了解有关React,Spring Boot,JHipster和OAuth的更多信息

本教程向您展示了如何使用Spring Boot,React,JHipster和OIDC开发照相馆PWA。 它向您展示了一些有用的开放源代码库,这些库可以简化实现甚至生成测试。

您可以在GitHub上oktadeveloper / okta-react-photo-gallery-example回购中找到本文中创建的示例的源代码。 我录制了一个截屏视频,以逐步完成所有步骤。 在下面或在YouTube上观看嵌入式视频。

如果您想了解有关React,Spring Boot或OAuth 2.0的更多信息,建议您检查以下资源:

如有任何疑问,请在下面发表评论,在Twitter @mraible上ping我,或发布到我们的开发人员论坛。 如果您喜欢这篇文章,请在其他类似的人发表时关注@oktadev

``使用React,Spring Boot和JHipster构建图库PWA''最初于2018年6月25日发布在Okta开发人员博客上。

“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证。

翻译自: https://www.javacodegeeks.com/2018/07/react-spring-boot-photo-gallery-pwa.html

react hooks

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值