“我喜欢编写身份验证和授权代码。” 〜从来没有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 |
启用SASS支持? | 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启用用户注册
将OIDC身份验证与JHipster一起使用时,似乎缺少的功能之一是用户注册。 如果您使用会话或JWT身份验证,则主页上会提供一个注册链接。 使用OIDC,您需要在IdP中启用它。 对于Keycloak,您可以通过导航到http://localhost:9080
并单击Administration Console来实现 。 使用admin/admin
登录,然后单击“ 登录”选项卡。 该屏幕允许您启用忘记密码,记住我以及通过电子邮件进行验证。
启用此设置后,您将在Keycloak的登录表单上看到“ 注册”链接。
您需要在Keycloak中为新用户配置默认角色。 导航到角色 ,然后单击默认角色选项卡。 选择ROLE_USER
,然后单击“ 添加所选内容” 。 要配置默认组,请转到“ 组” >“ 默认组” 。 单击Users
然后添加 。 添加默认组是必要的,因为JHipster希望用户将ROLE_USER
或ROLE_ADMIN
组(或角色)作为其ID令牌声明的一部分。
保存用于JPA关系的用户数据
我添加到JHipster的功能之一是我喜欢称之为“ 保存用户快照” 。 使用JPA时,很高兴能够与JHipster的User
实体建立关系。 这样一来,您就可以说出“此用户拥有此相册”之类的字眼,并根据该信息限制访问权限。
该功能默认情况下处于启用状态,其工作方式如下:
- 登录后,
/api/account
发出请求。 -
AccountResource.java
的getAccount()
方法被映射到该端点,并UserService#getUserFromAuthentication()
给UserService#getUserFromAuthentication()
以提取用户的详细信息。 -
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_ADMIN
和ROLE_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功能在线完成此操作,并在完成后将其保存在本地。
我为此应用创建了一个数据模型,该数据模型具有一个Album
, Photo
和Tag
实体,并在它们之间建立了关系。 下面是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
,添加metadata
和metadataRows
变量,以及在添加照片时将其隐藏和将其更新时为只读的逻辑。 在下面的代码块中找到第一行,并将其替换为以下代码。
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需要具备以下三个功能:
- 您的应用必须通过HTTPS进行投放
- 您的应用必须注册服务工作者,才能缓存请求并脱机工作
- 您的应用必须具有包含安装信息和图标的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的更多信息,建议您检查以下资源:
- 使用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