Java框架快速入门: Spring Security+OAuth2之工程结构与开发环境配置

纲要

  • 项目源码的章节划分与工程结构演变
    • 前五章:单一 UAA 后端工程
    • 第六章起:uaauaa-ui 双模块工程
    • 第八章:多服务角色工程(uaatodo-servicedata-plan
    • 第九章:集成 Keycloak 授权服务器,基于 Docker Compose 的依赖环境
  • Maven 多模块项目 POM 配置与版本管理
  • 外部依赖与环境变量
    • .env 文件管理敏感信息(密钥、Secret)
    • .env.sample 样例文件的作用
    • Docker Compose 快速启动依赖服务
  • IntelliJ IDEA 工程导入与日常操作
    • 通过 pom.xml 导入 Maven 工程
    • 启动 Spring Boot 应用
    • 配置 EnvFile 插件加载 .env 环境变量
    • 单元测试与集成测试的启动及环境配置差异
  • 完整可运行的示例代码
    • 多模块父 POM
    • .env.sample 模板
    • 单元测试与集成测试样例

项目工程结构演进

本系列教程的源码按章节顺序提供,从后端单体逐渐过渡到多模块、多服务架构,最终集成 Keycloak 实现单点登录。不同阶段的工程结构如下:

阶段1

仅包含一个 uaa 工程(Java 后端项目),所有功能均通过 REST 接口进行测试。

dir 复制代码
stage2/
├── uaa/          (后端 Spring Boot 工程)
└── pom.xml       (父 POM,管理子模块与版本)

阶段2

引入前端工程,目录结构变为:

dir 复制代码
stage2/
├── uaa/          (后端 Spring Boot 工程)
├── uaa-ui/       (前端工程)
└── pom.xml       (父 POM,管理子模块与版本)

阶段3

为演示 OAuth2 的多服务器角色环境,增加了两个服务模块:

dir 复制代码
stage3/
├── uaa/
├── todo-service/
├── data-plan/
└── pom.xml

阶段4

使用 Keycloak 作为授权服务器,演示与 Keycloak 的集成以及单点登录特性。此时工程结构变为:

dir 复制代码
stage4/
├── sso-client-1
├── sso-client-2
├── docker-compose.yml   (以 Docker 形式启动 Keycloak、MySQL 等依赖)
└── pom.xml

对于不熟悉 Docker 的用户,仍可手动下载并安装 MySQL、Keycloak 等组件。不过推荐使用 Docker Compose,它能极大简化依赖服务的启动流程。

每个需要外部依赖的章节都会提供对应的 docker-compose.yml 文件。

Maven 多模块配置要点

从第六章开始,项目采用标准的多模块 Maven 工程结构。父 POM 集中管理版本号与子模块声明,子模块的 pom.xml 无需重复定义版本号。

父 POM 示例:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>chapter-06-parent</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>

    <modules>
        <module>uaa</module>
        <module>uaa-ui</module>
    </modules>

    <properties>
        <java.version>17</java.version>
        <spring-boot.version>3.2.0</spring-boot.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

子模块 uaa/pom.xml 只需声明对父 POM 的继承即可,依赖版本由父 POM 统一控制。

环境变量与敏感信息管理

项目中的密钥、第三方服务 Secret(例如阿里云短信、Authing 等)均通过环境变量注入,不会硬编码在代码中,也不会提交到 Git 仓库。所有需要用户自定义的环境变量均存放在 .env 文件中,该文件已被添加到 .gitignore,不会出现在版本库中。

为了方便快速配置,仓库中提供了一份 .env.sample 样例文件,列出了所有必填的键值对模板。使用者需要将其复制为 .env 并根据自身申请的 Key/Secret 填写真实值。

.env.sample 示例内容:

properties 复制代码
# 数据库配置
DB_HOST=localhost
DB_PORT=3306
DB_NAME=uaa_db
DB_USER=root
DB_PASSWORD=your_db_password

# 阿里云短信
ALIBABA_CLOUD_ACCESS_KEY_ID=your_access_key
ALIBABA_CLOUD_ACCESS_KEY_SECRET=your_access_secret

# Authing 配置
AUTHING_CLIENT_ID=your_client_id
AUTHING_CLIENT_SECRET=your_client_secret
AUTHING_ISSUER=https://your-tenant.authing.cn

生产环境中,这些敏感信息通常由运维人员在服务器上直接设置系统环境变量,开发人员无需知晓最终生产值。团队内部可以自行维护一份测试专用的 .env 文件,但严禁将其提交至代码仓库。

如果未正确配置这些环境变量,启动应用时可能因缺少必要配置而失败,此时应优先检查 .env 文件是否已就位。

IDEA 工程导入与启动

导入工程

  1. 打开 IntelliJ IDEA,点击 Open or Import
  2. 定位到对应章节的工程目录,选中 pom.xml 文件,点击 Open
  3. 在弹出的对话框中选择 Open as Project,IDEA 将自动识别 Maven 结构并下载依赖。

启动 Spring Boot 应用

通常每个后端模块都会有一个 *Application.java 入口类,类名左侧会显示绿色启动箭头。直接点击箭头,选择 RunDebug 即可启动。

但若应用依赖 .env 中的环境变量,直接启动很可能会失败。此时需要借助 EnvFile 插件。

配置 EnvFile 插件

  1. 确保 IDEA 已安装 EnvFile 插件(可在 Plugin Marketplace 中搜索安装)。
  2. 打开 Run/Debug Configurations,选中需要运行的 Spring Boot 启动配置。
  3. 在配置界面的 EnvFile 标签页中,勾选 Enable EnvFile
  4. 点击 + 添加文件,选择项目根目录下的 .env 文件。

完成上述配置后,启动应用时插件会自动将 .env 中的键值对加载为系统环境变量,确保程序能够正常读取。

若插件安装后找不到 EnvFile 标签页,可尝试向上滚动配置面板,检查是否被隐藏在下方。

测试的启动与环境配置

测试代码位于 src/test/java 目录下,包结构通常与主代码对应。测试分为两类:

单元测试

不依赖 Spring 上下文,不进行任何自动装配,所有依赖均为手工构造的对象。这类测试可以直接运行,无需额外的环境变量。

java 复制代码
package com.example.uaa.service;

import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;

public class UserServiceUnitTest {

    @Test
    void shouldFormatUsername() {
        UserService userService = new UserService();
        String formatted = userService.formatUsername("John");
        assertThat(formatted).isEqualTo("JOHN");
    }
}

集成测试

需要启动 Spring 应用上下文,通常会用到 @SpringBootTest@Autowired 等注解。这类测试属于系统级别的验证,启动时必须保证环境变量齐全,否则会初始化失败。

java 复制代码
package com.example.uaa.controller;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerIntegrationTest {

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    void shouldReturnDefaultUser() {
        String body = this.restTemplate.getForObject("/api/users/1", String.class);
        assertThat(body).contains("username");
    }
}

集成测试同样需要借助 EnvFile 插件加载 .env 文件。在对应的运行配置中按照相同方式添加 .env 文件即可。若希望一次运行整个测试类,可直接点击类名旁的启动箭头,或使用 Maven 命令 mvn test

工程结构可视化

以下 Mermaid 图展示了多模块 Maven 工程的依赖与层次关系:
#mermaid-svg-Uo5DrpTKKec8f4wD{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Uo5DrpTKKec8f4wD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Uo5DrpTKKec8f4wD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Uo5DrpTKKec8f4wD .error-icon{fill:#552222;}#mermaid-svg-Uo5DrpTKKec8f4wD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Uo5DrpTKKec8f4wD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Uo5DrpTKKec8f4wD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Uo5DrpTKKec8f4wD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Uo5DrpTKKec8f4wD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Uo5DrpTKKec8f4wD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Uo5DrpTKKec8f4wD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Uo5DrpTKKec8f4wD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Uo5DrpTKKec8f4wD .marker.cross{stroke:#333333;}#mermaid-svg-Uo5DrpTKKec8f4wD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Uo5DrpTKKec8f4wD p{margin:0;}#mermaid-svg-Uo5DrpTKKec8f4wD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Uo5DrpTKKec8f4wD .cluster-label text{fill:#333;}#mermaid-svg-Uo5DrpTKKec8f4wD .cluster-label span{color:#333;}#mermaid-svg-Uo5DrpTKKec8f4wD .cluster-label span p{background-color:transparent;}#mermaid-svg-Uo5DrpTKKec8f4wD .label text,#mermaid-svg-Uo5DrpTKKec8f4wD span{fill:#333;color:#333;}#mermaid-svg-Uo5DrpTKKec8f4wD .node rect,#mermaid-svg-Uo5DrpTKKec8f4wD .node circle,#mermaid-svg-Uo5DrpTKKec8f4wD .node ellipse,#mermaid-svg-Uo5DrpTKKec8f4wD .node polygon,#mermaid-svg-Uo5DrpTKKec8f4wD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Uo5DrpTKKec8f4wD .rough-node .label text,#mermaid-svg-Uo5DrpTKKec8f4wD .node .label text,#mermaid-svg-Uo5DrpTKKec8f4wD .image-shape .label,#mermaid-svg-Uo5DrpTKKec8f4wD .icon-shape .label{text-anchor:middle;}#mermaid-svg-Uo5DrpTKKec8f4wD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Uo5DrpTKKec8f4wD .rough-node .label,#mermaid-svg-Uo5DrpTKKec8f4wD .node .label,#mermaid-svg-Uo5DrpTKKec8f4wD .image-shape .label,#mermaid-svg-Uo5DrpTKKec8f4wD .icon-shape .label{text-align:center;}#mermaid-svg-Uo5DrpTKKec8f4wD .node.clickable{cursor:pointer;}#mermaid-svg-Uo5DrpTKKec8f4wD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Uo5DrpTKKec8f4wD .arrowheadPath{fill:#333333;}#mermaid-svg-Uo5DrpTKKec8f4wD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Uo5DrpTKKec8f4wD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Uo5DrpTKKec8f4wD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Uo5DrpTKKec8f4wD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Uo5DrpTKKec8f4wD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Uo5DrpTKKec8f4wD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Uo5DrpTKKec8f4wD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Uo5DrpTKKec8f4wD .cluster text{fill:#333;}#mermaid-svg-Uo5DrpTKKec8f4wD .cluster span{color:#333;}#mermaid-svg-Uo5DrpTKKec8f4wD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Uo5DrpTKKec8f4wD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Uo5DrpTKKec8f4wD rect.text{fill:none;stroke-width:0;}#mermaid-svg-Uo5DrpTKKec8f4wD .icon-shape,#mermaid-svg-Uo5DrpTKKec8f4wD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Uo5DrpTKKec8f4wD .icon-shape p,#mermaid-svg-Uo5DrpTKKec8f4wD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Uo5DrpTKKec8f4wD .icon-shape .label rect,#mermaid-svg-Uo5DrpTKKec8f4wD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Uo5DrpTKKec8f4wD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Uo5DrpTKKec8f4wD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Uo5DrpTKKec8f4wD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} modules
modules
dependencyManagement
依赖
依赖
读取
注入
通过插件
父POM
uaa模块
uaa-ui模块
Spring Boot BOM
Node/前端构建
.env 环境变量
密钥/Secret
IDEA Run Configuration

总结

本文梳理了 Spring Security + OAuth2 实战教程所涉及的项目工程结构演变、Maven 多模块配置、环境变量管理以及 IDEA 中的开发与测试环境搭建。掌握这些基础配置,是后续顺利开发 OAuth2 认证授权流程的前提。

相关推荐
lzfshub1 小时前
Open-DIS Python发送DIS实体状态PDU:实现坦克炮塔与主炮部件参数
java·网络·python·dis
魏 无羡1 小时前
springboot 拦截器
java·spring boot·后端
步行cgn1 小时前
Spring Boot 绑定嵌套 Bean 详解
java·spring boot·后端
phltxy10 小时前
C语言操作符详解
java·c语言·算法
步行cgn12 小时前
@Configuration 详解:Spring 配置类的核心注解
java·后端·spring
sunshine22 girl12 小时前
Java学习一 环境配置2 安装和基本使用Idea
java·学习·intellij-idea
嘿嘿-6613 小时前
Windows 一键使用 GPT-6 Astra:Codex CLI 配置教程
java·人工智能·windows·gpt·chatgpt·web
策案案案14 小时前
YOLO: 将AI Agents嵌入到IntelliJ IDEA
java·大数据·人工智能·笔记·yolo·microsoft·intellij-idea
辰烨chenye16 小时前
LeetCode Hot 100 题解 · 堆篇
java·算法·leetcode