如果你在互联网上搜索 "全栈Java",你可能会发现大量的招聘、课程和工作。作为一个全栈开发者可以是令人兴奋的,因为你可以独自创建一个应用程序的后端和前端。有商业逻辑和算法,以及像造型,使事情看起来很好,并确保一切安全。它的报酬也很高。今天,我将向你展示如何利用Spring Boot、React和JHipster成为一个全栈的Java开发者。
先决条件。
如果你是在Windows上,你可能需要安装Windows Subsystem for Linux,以便某些命令能够工作。
我建议使用SDKMAN来管理你的OpenJDK安装。只要运行 sdk install java 11.0.2-open来安装Java 11和 sdk install java 17-open来安装Java 17。
本教程不会提供关于如何用Java、React或Spring Boot编写代码的细枝末节。这是因为JHipster将为你编写大部分的代码然而,如果你是使用这些技术进行编程的新手,我推荐以下资源。
你可以使用下面的目录在本教程的各个部分之间跳过。
你也可以克隆已完成的例子,然后继续学习。
git clone https://github.com/oktadev/auth0-full-stack-java-example
如果你更喜欢视觉学习,你可以从OktaDev的YouTube频道观看下面的截屏。
使用React和Spring Boot的全栈开发
开始使用React的最简单的方法之一是使用Create React App(CRA)。你在本地安装它,然后运行 create-react-app <project>来生成一个具有最小依赖性的React应用。它使用webpack来构建项目,启动网络服务器,并运行其测试。
Spring Boot有一个类似的工具,叫做Spring Initializr。Spring Initializer与CRA有点不同,因为它是一个网站(和API),你可以用它创建应用程序。
今天,我将向你展示如何用React和Spring Boot建立一个Flickr的克隆。然而,我打算作弊。我没有使用上述的工具来构建一切,而是使用JHipster。JHipster是一个应用程序生成器,最初只支持Angular和Spring Boot。现在它支持Angular、React和Vue的前端。JHipster在后端也支持Kotlin、Micronaut、Quarkus、.NET和Node.js。
在本教程中,我们将使用React,因为它似乎是当今最流行的前端框架。
开始使用JHipster 7
如果你还没有听说过JHipster,那我可要好好犒劳一下你了。JHipster早在2013年就开始作为Yeoman应用程序生成器,现在已经发展成为一个开发平台。它允许你快速生成、开发和部署现代网络应用和微服务架构。今天,我将向你展示如何用JHipster建立一个Flickr克隆,并通过OAuth和OpenID Connect(OIDC)将其锁定。
要开始使用JHipster,你需要一个快速的网络连接和安装Node.js。该项目建议你使用最新的LTS(长期支持)版本,在写这篇文章的时候是14.7.6。要运行该应用,你需要安装Java 11。如果你安装了Git,JHipster会在创建你的项目后自动提交。这将允许你在不同版本之间进行升级。
运行下面的命令来安装JHipster。
npm i -g generator-jhipster@7
要用JHipster创建一个全栈应用程序,创建一个目录,并在其中运行jhipster 。
mkdir full-stack-java
cd full-stack-java
jhipster
JHipster会提示你要创建的应用程序的类型,以及你想包括哪些技术。对于本教程,请做出以下选择。
| 问题 | 回答 |
|---|---|
| 应用程序的类型? | Monolithic application |
| 名称? | flickr2 |
| Spring WebFlux? | No |
| Java包的名称? | com.auth0.flickr2 |
| 认证类型? | OAuth 2.0 / OIDC |
| 数据库的类型? | SQL |
| 生产数据库? | PostgreSQL |
| 开发数据库? | H2 with disk-based persistence |
| 哪种缓存? | Ehcache |
| 使用Hibernate二级缓存? | Yes |
| 使用Maven还是Gradle? | Maven |
| 使用JHipster注册表? | No |
| 其他技术? | <blank> |
| 客户端框架? | React |
| 管理界面? | Yes |
| Bootswatch主题? | United >Dark |
| 启用i18n? | Yes |
| 应用程序的本地语言? | English |
| 其他语言? | 您的选择! |
| 额外的测试框架? | Cypress |
| 安装其他生成器? | No |
按下Enter键,JHipster将在当前目录下创建你的应用程序,并运行npm install ,以安装所有指定在 package.json.
验证一切与Cypress和Keycloak的工作情况
当你选择OAuth 2.0和OIDC进行认证时,用户被存储在应用程序之外,而不是在其中。你需要配置一个身份提供者(IdP)来存储你的用户,并允许你的应用程序检索关于他们的信息。默认情况下,JHipster为Docker Compose提供了一个Keycloak文件。一组默认的用户和组在启动时被导入,并且它有一个为你的JHipster应用注册的客户端。
下面是 keycloak.yml在你的应用程序的 src/main/docker目录中的样子。
# This configuration is intended for development purpose; it's **your** responsibility
# to harden it for production
version: '3.8'
services:
keycloak:
image: jboss/keycloak:15.0.2
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',
'-Dkeycloak.profile.feature.upload_scripts=enabled',
]
volumes:
- ./realm-config:/opt/jboss/keycloak/realm-config
environment:
- KEYCLOAK_USER=admin
- KEYCLOAK_PASSWORD=admin
- DB_VENDOR=h2
# If you want to expose these ports outside your dev PC,
# remove the "127.0.0.1:" prefix
ports:
- 127.0.0.1:9080:9080
- 127.0.0.1:9443:9443
- 127.0.0.1:10990:10990
在你项目的根目录下用以下命令启动Keycloak。
docker-compose -f src/main/docker/keycloak.yml up -d
你可以通过用Maven启动你的应用程序来验证一切工作。
./mvnw
打开另一个终端,运行你的新应用的Cypress测试。
npm run e2e
你应该看到类似以下的输出。
(Run Finished)
Spec Tests Passing Failing Pending Skipped
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ ✔ administration/administration.spec. 00:12 5 5 - - - │
│ ts │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
✔ All specs passed! 00:12 5 5 - - -
将您的身份提供者改为Auth0
JHipster使用Spring Security的OAuth 2.0和OIDC支持来配置其使用的IdP。在Spring Boot中使用Spring Security时,你可以在属性文件中配置大多数设置。你甚至可以用环境变量重写属性。
要从Keycloak切换到Auth0,你只需要覆盖默认属性(对于Spring Security OAuth)。你甚至不需要写任何代码
为了了解它是如何工作的,请在你的项目根目录下创建一个 .auth0.env文件,并在其中填入以下代码以覆盖默认的OIDC设置。
export SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI=https://<your-auth0-domain>/
export SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID=<your-client-id>
export SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET=<your-client-secret>
export JHIPSTER_SECURITY_OAUTH2_AUDIENCE=https://<your-auth0-domain>/api/v2/
⚠️警告:修改你现有的 .gitignore文件,使其具有 *.env这样你就不会不小心检查到你的秘密了!你需要创建一个新的文件。
你需要在Auth0中创建一个新的网络应用程序,并填入 <...>占位符才行。
在Auth0上创建一个OpenID Connect应用程序
登录到你的Auth0账户(如果你没有账户,也可以注册)。你应该有一个独特的域名,如 dev-xxx.eu.auth0.com.
按应用程序部分的创建应用程序按钮。使用一个名称如 JHipster Baby!,选择Regular Web Applications ,然后点击创建。
切换到设置标签,配置你的应用程序设置。
- 允许的回调URL。
http://localhost:8080/login/oauth2/code/oidc - 允许的注销URLs。
http://localhost:8080/
滚动到底部,点击保存更改。
在角色部分,创建新的角色,名为 ROLE_ADMIN和 ROLE_USER.
在用户部分创建一个新的用户账户。点击角色标签,将你刚刚创建的角色分配给新账户。
在尝试登录之前,请确保你的新用户的电子邮件是经过验证的!
接下来,前往Auth Pipeline>Rules>Create。选择Empty rule 模板。提供一个有意义的名字,如Group claims ,并将脚本内容替换为以下内容。
function(user, context, callback) {
user.preferred_username = user.email;
const roles = (context.authorization || {}).roles;
function prepareCustomClaimKey(claim) {
return `https://www.jhipster.tech/${claim}`;
}
const rolesClaim = prepareCustomClaimKey('roles');
if (context.idToken) {
context.idToken[rolesClaim] = roles;
}
if (context.accessToken) {
context.accessToken[rolesClaim] = roles;
}
callback(null, user, context);
}
这段代码是将用户的角色添加到一个自定义的索赔(前缀为 https://www.jhipster.tech/roles).这个请求被映射到Spring Security权限中的 SecurityUtils.java.
public static List<GrantedAuthority> extractAuthorityFromClaims(Map<String, Object> claims) {
return mapRolesToGrantedAuthorities(getRolesFromClaims(claims));
}
@SuppressWarnings("unchecked")
private static Collection<String> getRolesFromClaims(Map<String, Object> claims) {
return (Collection<String>) claims.getOrDefault(
"groups",
claims.getOrDefault("roles", claims.getOrDefault(CLAIMS_NAMESPACE + "roles", new ArrayList<>()))
);
}
private static List<GrantedAuthority> mapRolesToGrantedAuthorities(Collection<String> roles) {
return roles.stream().filter(role -> role.startsWith("ROLE_")).map(SimpleGrantedAuthority::new).collect(Collectors.toList());
}
该 SecurityConfiguration.java类有一个Bean,它调用这个方法来配置用户的OIDC数据中的角色。
@Bean
public GrantedAuthoritiesMapper userAuthoritiesMapper() {
return authorities -> {
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
authorities.forEach(authority -> {
// Check for OidcUserAuthority because Spring Security 5.2 returns
// each scope as a GrantedAuthority, which we don't care about.
if (authority instanceof OidcUserAuthority) {
OidcUserAuthority oidcUserAuthority = (OidcUserAuthority) authority;
mappedAuthorities.addAll(SecurityUtils.extractAuthorityFromClaims(oidcUserAuthority.getUserInfo().getClaims()));
}
});
return mappedAuthorities;
};
}
单击 "保存更改"以继续。
ℹ️注意:想让所有这些步骤为你自动化吗?请在Auth0 CLI项目中为这个问题投票。
用Auth0运行你的JHipster应用程序
使用Ctrl+C停止你的JHipster应用程序,在Auth0中设置你的属性。 .auth0.env中设置Auth0属性,然后再次启动你的应用程序。
source .auth0.env
./mvnw
Voilà- 你的全栈应用程序现在正在使用Auth0!打开你最喜欢的浏览器到 http://localhost:8080.

你应该看到你的应用程序的主页上有一个登录的链接。点击登录,你将被重定向到Auth0进行登录。

输入你的证书后,你将被重定向到你的应用程序。

用Cypress测试你的全栈Java应用
JHipster内置了对Auth0的支持,因此你可以为Cypress测试指定你的凭证,并自动进行UI测试!
要做到这一点,打开一个新的终端窗口,指定你刚刚创建的Auth0用户的证书,然后运行npm run e2e 。
export CYPRESS_E2E_USERNAME=<new-username>
export CYPRESS_E2E_PASSWORD=<new-password>
npm run e2e
提示:如果你想用一个 .env文件来设置环境变量,你可以使用cypress-dotenv。你也可以把这些值放在 cypress.json,但由于这个文件将在源代码控制中,把你的秘密放在里面是一个不好的做法。
一切都应该在一分钟左右通过。
Spec Tests Passing Failing Pending Skipped
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ ✔ administration/administration.spec. 00:31 5 5 - - - │
│ ts │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
✔ All specs passed! 00:31 5 5 - - -
Execution time: 44 s.
关闭运行你的JHipster应用程序的进程--现在是为你的Flickr克隆创建一些数据处理的时候了。
创建实体以允许对照片进行CRUD
我已经谈了很多关于如何保护你的应用程序的问题,但我们还没有对照片做任何处理!我想说的是,JHipster有一个JDL实体。JHipster有一个JDL(JHipster Domain Language)功能,它允许你在你的应用程序中对数据进行建模并从中生成实体。你可以使用JDL工作室在线完成这项工作,完成后保存在本地。
我的这个应用程序的数据模型有Album,Photo, 和Tag 实体,并在它们之间建立了关系。下面是它在JDL Studio中的屏幕截图。

复制下面的JDL,并将其保存在你项目根目录下的一个 flickr2.jdl文件中,并保存在你项目的根目录下。
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和JJSX)。
jhipster jdl flickr2.jdl
当提示时,输入a ,以允许覆盖现有文件。
这个过程将创建Liquibase changelog文件(创建你的数据库表)、实体、资源库、Spring MVC控制器,以及创建、读取、更新和删除实体所需的所有React代码。它甚至会生成JUnit单元测试、Jest单元测试和Cypress端到端测试
在这个过程完成后,你可以重新启动你的应用程序,登录,并浏览实体菜单。尝试添加一些数据,以确认一切正常。
现在,你可以看到,JHipster是相当强大的。它认识到你有一个ImageBlob 类型的图像属性,并创建了必要的逻辑来上传和存储数据库中的图像Booyah!
在你的Spring Boot API中添加图像EXIF处理
Photo 实体有几个属性,可以通过读取上传照片的EXIF(可交换图像文件格式)数据来计算。你可能会问,你如何在Java中做到这一点?
值得庆幸的是,Drew Noakes创建了一个元数据提取器库来做这件事。把对Drew库的依赖性添加到你的 pom.xml:
<dependency>
<groupId>com.drewnoakes</groupId>
<artifactId>metadata-extractor</artifactId>
<version>2.16.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<Photo> 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(applicationName, true, 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;
}
...
}
因为你在提取信息,所以你可以从用户界面和测试中删除字段,这样用户就不能设置这些值。
在 src/main/webapp/app/entities/photo/photo-update.tsx中,隐藏元数据,这样用户就不能编辑它了。与其显示height,width,taken, 和uploaded 的值,不如隐藏它们。你可以通过搜索来做到这一点 photo-height,抓取这些元素(和它后面的三个元素),并将它们添加到metadata 常量中,就在 defaultValues()lambda函数。
const defaultValues = () =>
...
const metadata = (
<div>
<ValidatedField label={translate('flickr2App.photo.height')} id="photo-height" name="height" data-cy="height" type="text" />
<ValidatedField label={translate('flickr2App.photo.width')} id="photo-width" name="width" data-cy="width" type="text" />
<ValidatedField
label={translate('flickr2App.photo.taken')}
id="photo-taken"
name="taken"
data-cy="taken"
type="datetime-local"
placeholder="YYYY-MM-DD HH:mm"
/>
<ValidatedField
label={translate('flickr2App.photo.uploaded')}
id="photo-uploaded"
name="uploaded"
data-cy="uploaded"
type="datetime-local"
placeholder="YYYY-MM-DD HH:mm"
/>
</div>
);
const metadataRows = isNew ? '' : metadata;
return ( ... );
然后,在 return块中,删除image 属性和album 属性之间的JSX,并将其替换为 {metadataRows}.
<ValidatedBlobField
label={translate('flickr2App.photo.image')}
id="photo-image"
name="image"
data-cy="image"
isImage
accept="image/*"
validate={{
required: { value: true, message: translate('entity.validation.required') },
}}
/>
{metadataRows}
<ValidatedField id="photo-album" name="albumId" data-cy="album" label={translate('flickr2App.photo.album')} type="select">
<option value="" key="0" />
{albums
? albums.map(otherEntity => (
<option value={otherEntity.id} key={otherEntity.id}>
{otherEntity.title}
</option>
))
: null}
</ValidatedField>
在 src/test/javascript/cypress/integration/entity/photo.spec.ts中,删除设置这些字段数据的代码。
cy.get(`[data-cy="height"]`).type('99459').should('have.value', '99459');
cy.get(`[data-cy="width"]`).type('61514').should('have.value', '61514');
cy.get(`[data-cy="taken"]`).type('2021-10-11T16:46').should('have.value', '2021-10-11T16:46');
cy.get(`[data-cy="uploaded"]`).type('2021-10-11T15:23').should('have.value', '2021-10-11T15:23'););
停止你的Maven进程,运行 source .auth0.env,然后 ./mvnw再次运行。打开一个新的终端窗口,设置你的Auth0凭证,然后运行npm run e2e ,确保一切正常。
export CYPRESS_E2E_USERNAME=<auth0-username>
export CYPRESS_E2E_PASSWORD=<auth0-password>
npm run e2e
ℹ️注意:如果你在Cypress测试中遇到认证错误,可能是因为你违反了Auth0的速率限制政策。作为一种变通方法,我建议你使用Keycloak进行Cypress测试。你可以通过打开一个新的终端窗口,并在那里用以下方式启动你的应用程序 ./mvnw.然后,打开第二个终端窗口,运行npm run e2e 。
如果你上传一张你用智能手机拍摄的图片,高度、宽度和拍摄的数值都应该被填充。如果它们不是,很可能你的图片中没有这些数据。
需要一些带有EXIF数据的样本照片吗?你可以从Flickr上的相册下载我的1966年大众汽车的照片。
添加一个React图片库
你已经在你的后台添加了元数据提取,但你的照片仍然显示在一个列表中,而不是在一个网格中(像Flickr)。为了解决这个问题,你可以使用React Photo Gallery组件。使用npm安装它。
npm i react-photo-gallery@8 --force
在 src/main/webapp/app/entities/photo/photo.tsx中,为Gallery 添加一个导入。
import Gallery from 'react-photo-gallery';
然后在后面添加以下内容 const { match } = 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
}));
接下来,在结尾处添加一个 <Gallery>组件,紧随其后 </h2>.
return (
<div>
<h2 id="photo-heading" data-cy="PhotoHeading">
...
</h2>
<Gallery photos={photoSet} />
...
);
保存你所有的改动并重新启动你的应用程序。
source .auth0.env
./mvnw
登录并在顶部导航栏中浏览到实体>照片。你会看到由Liquibase和faker.js加载的大量的照片。为了制作一个没有这些数据的干净的截图,我修改了 src/main/resources/config/application-dev.yml以删除Liquibase的 "faker "上下文。
liquibase:
# Append ', faker' to the line below if you want sample data to be loaded automatically
contexts: dev
停止你的Spring Boot后端并运行 rm -r target/h2db来清除你的数据库(或者直接删除 target/h2db目录)。重新启动你的后端。
现在你应该可以上传照片,并在列表顶部的一个漂亮的网格中看到结果。

你还可以在网格上添加一个 "灯箱 "功能,这样你就可以点击照片并放大。React Photo Gallery的文档显示了如何做到这一点。我已经把它整合到这篇文章的例子中,但为了简洁起见,我不会在这里展示代码。你可以在GitHub上看到添加了Lightbox的最终photo.tsx ,也可以看到必要修改的差异。
把你的全栈Java应用变成一个PWA
渐进式网络应用程序,又称PWA,是开发者使其网络应用程序加载更快、性能更强的最佳方式。简而言之,PWA是使用最新网络标准的网站,允许安装在用户的电脑或设备上,并向这些用户提供类似于应用程序的体验。要把一个网络应用变成PWA。
- 你的应用必须通过HTTPS提供服务
- 你的应用程序必须注册一个服务工作者,以便它可以缓存请求并离线工作
- 你的应用程序必须有一个包含安装信息和图标的webapp清单
对于HTTPS,你可以为localhost设置一个证书,或者(甚至更好),把它部署到生产中去像Heroku这样的云服务提供商将为你提供开箱即用的HTTPS,但他们不会强制HTTPS。要强制使用HTTPS,请打开 src/main/java/com/auth0/flickr2/config/SecurityConfiguration.java并添加一条规则,在发送头信息时强制使用安全通道。 X-Forwarded-Proto头的时候强制使用安全通道。
@Override
protected void configure(HttpSecurity http) throws Exception {
http
...
.and()
.frameOptions()
.deny()
.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) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('/service-worker.js').then(function () {
console.log('Service Worker Registered');
});
});
}
</script>
最后一个功能--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。当被提示使用Okta进行OIDC时,选择No 。然后,部署过程将开始。
你会被提示覆盖 pom.xml-typea 以允许覆盖所有文件。
如果你有一个稳定和快速的网络连接,你的应用程序应该在6分钟内就可以在互联网上运行了
remote: -----> Compressing...
remote: Done: 120.9M
remote: -----> Launching...
remote: Released v7
remote: https://flickr-2.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/flickr-2.git
* [new branch] HEAD -> main
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!
Sponsored with ❤️ by @oktadev.
Execution time: 6 min. 19 s.
为Auth0配置并使用Lighthouse分析你的PWA得分
要将你的应用程序配置为Heroku上的Auth0,请运行以下命令来设置Heroku上的Auth0变量。
heroku config:set \
SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI="https://<your-auth0-domain>/" \
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID="<your-client-id>" \
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET="<your-client-secret>" \
JHIPSTER_SECURITY_OAUTH2_AUDIENCE="https://<your-auth0-domain>/api/v2/"
然后,登录你的Auth0账户,导航到你的应用程序,并将你的Heroku URLs添加为有效的重定向URI。
- 允许的回调URLs:
https://flickr-2.herokuapp.com/login/oauth2/code/oidc - Allowed Logout URLs:
https://flickr-2.herokuapp.com
在Heroku重新启动你的应用后,用heroku open ,并登录。

然后,用Lighthouse进行测试(使用Chrome开发工具中的Lighthouse标签)。看起来很不错,是吧!?💯

它也很安全,至少根据securityheaders.com的说法。

了解更多关于全栈式Java开发的信息
本教程告诉你如何用JHipster简化全栈Java开发。你开发了一个带有React前端和Spring Boot后端的工作应用。你可以在GitHub的auth0-full-stack-java-example仓库中找到本教程创建的应用程序。
你可能还会喜欢这些相关的博文。