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

“我喜欢编写身份验证和授权代码。” 〜从来没有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.Finalcommand: ["-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-configenvironment:- KEYCLOAK_USER=admin- KEYCLOAK_PASSWORD=adminports:- 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 logs7 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启用用户注册

将OIDC身份验证与JHipster一起使用时,似乎缺少的功能之一是用户注册。 如果您使用会话或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中添加图像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变量,以及在添加照片时将其隐藏和将其更新时为只读的逻辑。 在下面的代码块中找到第一行,并将其替换为以下代码。

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><AvInputid="photo-taken"type="datetime-local"className="form-control"name="taken"readOnlyvalue={isNew ? null : convertDateTimeFromServer(this.props.photoEntity.taken)}/></AvGroup><AvGroup><Label id="uploadedLabel" for="uploaded"><Translate contentKey="galleryApp.photo.uploaded">Uploaded</Translate></Label><AvInputid="photo-uploaded"type="datetime-local"className="form-control"name="uploaded"readOnlyvalue={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 -> masterYour app should now be live. To view it runheroku open
And you can view the logs with this commandheroku logs --tail
After application modification, redeploy it withjhipster 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的更多信息,建议您检查以下资源:

  • 使用Spring Boot和React进行Bootiful开发
  • 在JHipster中使用OpenID Connect支持
  • 什么是OAuth 2.0授权码授予类型?
  • 从JHipster Docs使用React(与Redux)
  • 使用OAuth 2.0和JHipster开发微服务架构

如有任何疑问,请在下面发表评论,在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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/346199.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

names文件和data文件_data.table让你的读取速度提升百倍

不知道大家有没有用read.table和read.csv读取过文件&#xff0c;当文件不大的时候你可能还感觉不出读取速度&#xff0c;但是当文件比较大的时候&#xff0c;比如有上万行的时候&#xff0c;你就会感觉到等待时间明显变长&#xff0c;甚至无法忍受。今天小编给大家安利一个实用…

差分放大电路差模共模公式_提高差分放大器的共模抑制比,电阻的选择很关键...

在各种应用领域&#xff0c;采用模拟技术时都需要使用差分放大器电路。例如测量技术&#xff0c;根据其应用的不同&#xff0c;可能需要极高的测量精度。为了达到这一精度&#xff0c;尽可能减少典型误差源(例如失调和增益误差&#xff0c;以及噪声、容差和漂移)至关重要。为此…

label标签 html5,HTML5 label 标签

实例HTML5 标签用于为 input 元素做出标记。带有两个输入字段和相关标记的简单 HTML 表单&#xff1a;MaleFemale尝试一下 浏览器支持目前大多数浏览器支持 标签。标签定义及使用说明标签为 input 元素定义标注(标记)。label 元素不会向用户呈现任何特殊效果。不过&#xff0c;…

me shy是什么歌 抖音make_抖音星河滚烫你是人间的理想下一句是什么歌 星河歌词完整版...

最近在抖音上听到一段话&#xff0c;这段话的开头是“星河滚烫&#xff0c;你是人间的理想……”。抖音星河滚烫你是人间的理想下一句是什么歌&#xff1f;星河歌词介绍&#xff1f;星河&#xff0c;是非常好听的一首歌曲&#xff0c;是很多人都喜欢的一首歌曲。抖音星河滚烫你…

修改使用计算机的名称,Windows 10中修改计算机名称的方法,你知道几种?

当用户新入手一台电脑时&#xff0c;通常做的第一件事就是做一些个性化设置。一般来说个性化的第一步就是给计算机起一个自己满意的名字&#xff0c;下面介绍几种修改计算机名字的方法。通过设置更改计算机名字按WinI键进入设置,在设置窗口中找到“系统”&#xff0c;点击它。在…

apollo 参数传递_使用Apollo通过WebSocket通过STOMP轻松进行消息传递

apollo 参数传递在我以前的文章中&#xff0c;我介绍了几个有趣的用例&#xff0c;这些用例使用著名的消息代理HornetQ和ActiveMQ通过Websockects实现STOMP消息传递。 但是我没有介绍的是Apollo&#xff0c;因为我个人认为它的API是冗长的&#xff0c;并且不像Java开发人员那样…

使用IntelliJ调试Java流

流功能非常强大&#xff0c;只需几行就可以捕获您预期功能的要点。 但是&#xff0c;就像它们在一切正常工作时一样平滑&#xff0c;就像它们在表现不正常时一样令人痛苦。 了解如何使用IntelliJ调试Java Streams并深入了解Stream的中间操作。 在本文中&#xff0c;我将在示例…

echart 饼图每一块间隙_花10分钟做酱香饼,简单方便又美味,酥脆咸香比买的还要好吃...

花10分钟做酱香饼&#xff0c;简单方便又美味&#xff0c;酥脆咸香比买的还要好吃前几年&#xff0c;在单位附近有一家只做酱香饼的小店&#xff0c;刚开张的时候排很长队&#xff0c;我为了一块酱香饼排队半小时。后来买酱香饼的人少了&#xff0c;我三天两头买一块酱香饼解馋…

404 单页应用 报错 路由_通过 Laravel 创建一个 Vue 单页面应用(五)

文章转发自专业的Laravel开发者社区&#xff0c;原始链接&#xff1a;https://learnku.com/laravel/t/34858我们在第4部分完成了编辑用户的功能&#xff0c;并且学习了如何使用 v-model 来监听视图组件中用户信息的更改。现在我们可以开始构思删除用户功能&#xff0c;以及删除…

使用计算机求解雷达方程,关于雷达方程

满意答案zjit99402014.01.27采纳率&#xff1a;41% 等级&#xff1a;12已帮助&#xff1a;2803人带宽校正系数是Co而不是Do.我们知道雷达接收到回波信号后&#xff0c;必须进行信号处理&#xff0c;使中频接收机的输出端(检波器的输入端)输出的信号和噪声功率比(简称信噪比)…

7500 cpuz跑分 i5_小米Pro笔记本亮相跑分网站:处理器包含AMD Zen3、11代酷睿

2020年已经接近尾声&#xff0c;随之而来的将会是一大波新品发布会&#xff0c;不仅是手机&#xff0c;笔记本电脑、显卡等产品也会有新品亮相。一年一度的CES 2021也即将开幕&#xff0c;届时英特尔、英伟达、AMD等知名厂商也都将出席活动。据悉&#xff0c;英特尔将推出11代酷…

2能不用cuda_洗洁精不用花钱买,自己在家就能做,成本2毛钱,比买的更好用

点击上面“妙招姐”免费关注图文版&#xff1a;哈喽大家好&#xff0c;我是妙招姐&#xff0c;洗洁精是我们生活中的必须品&#xff0c;但用洗洁精来清洗餐具或水果等和食物相关的东西多多少少都会有残留&#xff0c;那么今天妙招姐就来教大家用橘子皮来自己制作洗洁精&#xf…

centos模拟post请求_java模拟post和get请求(2019/10/25)

一、http含义的介绍1.http协议是超文本传输协议--具体含义请百度2.基于tcp/ip协议--注意和udp的区别3.无状态---本次请求记不住以往请求的状态4.无连接--每次连接只处理一个请求5.媒体独立二、请求报文的组成部分请求行 请求方式 请求url http协议版本请求头关于请求头和响应头…

浙江计算机二级报名步骤,浙江2016年9月计算机二级考生报名流程

导读&#xff1a;2016年9月浙江全国计算机二级考试网上报名时间为2016年6月1日-6月19日;现场缴费确认为6月1日6月20日&#xff0c;具体报名流程请看如下信息。想了解更多相关信息请持续关注我们应届毕业生考试网!(一)注册账号和登录1)考生首次登录系统需要注册登录通行证&#…

单开双控_卧室或者楼梯灯怎么安装单联双控

点击上面蓝色字体关注&#xff01;装修图例 | 别墅装修 | 装潢装饰 | 样板楼梯 | 装修设计在改电和布线前我们在这两个开关之间预埋3根线就够了。如果一头接电源&#xff0c;另一头接控制线的话&#xff0c;预埋2根线就够了&#xff0c;双控开关有三个接线柱&#xff0c;只要记…

Apache Ignite变得简单:第一个Java应用程序

在本文中&#xff0c;我们将更进一步&#xff0c;让您完成第一个Ignite应用程序的创建&#xff0c;以从分布式缓存中进行读写操作。 作为第一个示例&#xff0c;我们将尽可能简单地向您展示如何用Java编写用于处理Apache Ignite集群数据的应用程序。 可从GitHub存储库&#xf…

华测数据导出方法_输电线路塔基测量成图方法研究与应用

1.前言塔基测量成图软件是EATool第一个电力勘测模块&#xff0c;也是笔者个人刚参加工作时开发的第一个与工作直接相关的软件。该模块开发开始于2010年&#xff0c;于2012基本成型。记得第一次参加线路工程终勘定位&#xff0c;对于老员工的塔基断面测量模式有着诸多疑惑&#…

MQ在计算机组成,[转载]计算机组成框图-计算机运行流程(例子)

计算机组成框图-计算机运行流程ACC(Accumulator)&#xff1a; 累加器MQ(Multiplier-Quotient Register)&#xff1a;乘商寄存器ALU&#xff1a;算术逻辑单元X&#xff1a;操作数寄存器CU&#xff1a;控制单元IR(Instruction Register)&#xff1a;指令寄存器(用于存放当前指令)…

计算机硬盘怎么设置ntfs,Windows7系统如何把磁盘格式转换为NTFS的方法

今天朋友匆匆忙忙来找我&#xff1a;他的电脑因为某种情况&#xff0c;需要吧磁盘转换成ntfs格式&#xff0c;该怎么转换?Win7系统、利用系统自带工具转换1.我们打开我们的电脑磁盘&#xff0c;大家发现小编的电脑G盘是FAT32格式的&#xff0c;小编现在要把G盘转换为NTFS格式。…

ace unlck工具下载_压缩工具:WinRAR 曝出代码执行漏洞,该升级了

作为享誉全球的装机必备工具&#xff0c;「压缩软件」这四个字基本可以认为是 WinRAR 的专有名字了。只要有 PC 的地方&#xff0c;几乎都能看到 WinRAR 的身影。不过这款软件最近却被曝出了一个有19年历史的代码执行漏洞。WinRAR 宣称在全球拥有超过5亿的用户&#xff0c;目前…