Open Liberty使用指南及微服务开发示例(一)

Open Liberty 是一个轻量级、云原生的 Java 运行时,主要用于运行 Jakarta EE 和 MicroProfile应用。它的核心特点是模块化、快速启动和良好的可观测性。

一、 使用 Open Liberty + MicroProfile 进行微服务开发

 核心步骤

  1. 选择 MicroProfile 运行时(下载 Open Liberty microProfile 版)
  2. 配置 服务器(启用 MicroProfile 相关功能)
  3. 开发 RESTful 微服务
  4. 添加 配置、健康检查、监控、认证
  5. 运行并部署到 Docker/Kubernetes

1、安装 Open Liberty MicroProfile 版

你可以从官网下载 Open Liberty MicroProfile:

unzip openliberty-microProfile.zip -d /opt/openliberty
cd /opt/openliberty

 创建服务器:

bin/server create myMicroservice
bin/server start myMicroservice

2、 配置 MicroProfile 服务器

编辑 wlp/usr/servers/myMicroservice/server.xml,启用 MicroProfile 相关功能:

<server>
    <!-- 启用 MicroProfile 特性 -->
    <featureManager>
        <feature>microProfile-5.0</feature>
    </featureManager>

    <!-- 监听端口 -->
    <httpEndpoint id="defaultHttpEndpoint" httpPort="9080" httpsPort="9443" />

    <!-- 日志 -->
    <logging consoleLogLevel="INFO" />

    <!-- 监控支持 -->
    <mpMetrics />
    <mpHealth />
</server>

重启服务器:

bin/server stop myMicroservice
bin/server start myMicroservice

3、开发 MicroProfile REST 微服务

创建 src/main/java/com/example/HelloResource.java

package com.example;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/hello")
public class HelloResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String sayHello() {
        return "Hello from MicroProfile!";
    }
}

 配置 src/main/webapp/WEB-INF/beans.xml

<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                           https://jakarta.ee/xml/ns/jakartaee/beans_2_0.xsd"
       bean-discovery-mode="all">
</beans>

 打包并运行:

mvn clean package
cp target/your-app.war wlp/usr/servers/myMicroservice/apps/
bin/server restart myMicroservice

访问:
👉 http://localhost:9080/hello

4、添加 MicroProfile 关键特性

配置管理(MicroProfile Config)

支持 环境变量、配置文件、数据库 等方式配置应用参数。

创建 META-INF/microprofile-config.properties

greeting.message=Hello from Config!

 修改 HelloResource.java 读取配置:

import org.eclipse.microprofile.config.inject.ConfigProperty;
import jakarta.inject.Inject;

@Path("/hello")
public class HelloResource {

    @Inject
    @ConfigProperty(name = "greeting.message", defaultValue = "Hello Default")
    private String message;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String sayHello() {
        return message;
    }
}

健康检查(MicroProfile Health)

添加 /health 端点,提供 Kubernetes 监测

创建 HealthCheckResource.java

import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Liveness;
import jakarta.enterprise.context.ApplicationScoped;

@Liveness
@ApplicationScoped
public class HealthCheckResource implements HealthCheck {
    @Override
    public HealthCheckResponse call() {
        return HealthCheckResponse.up("Service is alive!");
    }
}

访问: 👉 http://localhost:9080/health

度量监控(MicroProfile Metrics)

提供 Prometheus 兼容/metrics 端点。

创建 MetricsResource.java

import org.eclipse.microprofile.metrics.annotation.Counted;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@Path("/metrics-demo")
public class MetricsResource {

    @GET
    @Counted(name = "hello_requests", description = "Number of hello requests")
    public String hello() {
        return "Hello with Metrics!";
    }
}

访问: 👉 http://localhost:9080/metrics

OpenAPI 文档(MicroProfile OpenAPI)

支持 Swagger 风格 API 文档。

添加 META-INF/microprofile-openapi.properties

mp.openapi.scan.disable=false
mp.openapi.servers=http://localhost:9080

 访问: 👉 http://localhost:9080/openapi 👉 http://localhost:9080/swagger-ui(如果 Swagger UI 已部署)

5、容器化部署(Docker + Kubernetes)

创建 Dockerfile

FROM openliberty/open-liberty:latest
COPY your-app.war /config/apps/
EXPOSE 9080 9443
CMD ["/opt/ol/wlp/bin/server", "run", "defaultServer"]

 构建并运行:

docker build -t my-microservice .
docker run -p 9080:9080 my-microservice

总结

你已经成功使用 Open Liberty + MicroProfile 开发了一个 微服务,并实现了: ✅ RESTful API
配置管理(MicroProfile Config)
健康检查(MicroProfile Health)
监控指标(MicroProfile Metrics)
OpenAPI 文档

二、本地开发 Open Liberty + MicroProfile 的最佳实践

本地运行 Open Liberty 进行快速测试
使用 Dev Mode 进行热部署(代码变更自动生效)
结合 Docker 进行依赖管理
使用 VS Code 或 IntelliJ 进行调试


1、启用 Dev Mode(热部署)

Liberty 开发模式(Dev Mode) 可以让你修改代码后自动生效,而无需重启服务器。

📌 启动 Dev Mode(需使用 mvn 构建项目):

mvn liberty:dev

🔄 代码变更后,无需手动重启,Liberty 会自动热加载!🔥
📎 测试 API:访问 http://localhost:9080/hello

2、本地调试(Remote Debugging)

如果你想使用 VS Code/IntelliJ 进行断点调试,启动 Liberty 时加上 --debug 选项:

mvn liberty:dev --debug

🔗 调试端口:默认 7777,可以用 IDE 远程连接调试。

3、本地运行 Open Liberty 容器

如果你想用 Docker 在本地运行 Open Liberty,而不是直接运行 jar/war:

docker run -d -p 9080:9080 -v $(pwd)/target:/config/apps openliberty/open-liberty:latest

 这样你的本地应用会直接在容器内运行,适合测试不同的环境。

4、结合数据库开发

如果你的微服务需要数据库,可以本地运行 PostgreSQLMySQL

docker run -d --name postgres -e POSTGRES_PASSWORD=mysecretpassword -p 5432:5432 postgres:latest

 然后在 microprofile-config.properties 中添加数据库连接:

db.url=jdbc:postgresql://localhost:5432/mydb
db.user=postgres
db.password=mysecretpassword

并在 Java 代码中读取:

@Inject
@ConfigProperty(name = "db.url")
private String dbUrl;

三、组织人员账号管理的示例

项目将包括以下关键模块:

组织管理(Organization)
人员管理(Person)
账号管理(Account)
IBM DB2 数据存储(使用容器运行 IBM DB2 Community Edition)
身份认证(JWT + MicroProfile JWT)

项目技术栈

  • 运行时:Open Liberty + MicroProfile
  • 数据库:IBM DB2 Community Edition(容器化)
  • 身份认证:MicroProfile JWT
  • API 设计:Jakarta RESTful Web Services (JAX-RS)
  • 数据库访问:Jakarta Persistence API (JPA) + Hibernate
  • 配置管理:MicroProfile Config

1、启动 IBM DB2 容器

非官方方法

首先,拉取并运行 IBM DB2 Community Edition:

docker run -d --name db2 \
  -e DB2INST1_PASSWORD=mysecretpassword \
  -e DBNAME=mydb \
  -p 50000:50000 \
  ibmcom/db2

 ⚡ 检查 DB2 是否运行: 

docker logs -f db2

 ⚡ 进入 DB2 容器并连接数据库:

docker exec -it db2 bash
su - db2inst1
db2 connect to mydb user db2inst1 using mysecretpassword

 IBM官方方法

1.从命令行,为 Docker 映像创建新目录:
mkdir Docker
2.通过输入以下命令转至此目录: 
cd Docker

注: Docker 将配置文件 config.json用于凭证的未加密存储。 因此,在输入用户名和密码之前,您可能会收到消息 WARNING: Error loading config file....,后跟 config.json 文件的预期缺省位置。

此消息不会阻止您输入凭证和访问 Docker 环境。 在您登录后,Docker 会在缺省位置创建 config.json 文件,并将您的凭证存储在该文件中。 请参阅 docker login 以获取有关创建 Docker 凭证的安全存储的信息。

3.登录 Docker 容器:
sudo docker login
4.从 ICR 中拉取 Db2 Docker 映像:
docker pull icr.io/db2_community/db2
5.为 Db2 Community Edition 映像创建环境变量文件 .env_list:
vi .env_list
 6.将以下内容粘贴到环境变量文件中:
LICENSE=accept
DB2INSTANCE=db2inst1
DB2INST1_PASSWORD=password
DBNAME=testdb
BLU=false
ENABLE_ORACLE_COMPATIBILITY=false
UPDATEAVAIL=NO
TO_CREATE_SAMPLEDB=false
REPODB=false
IS_OSXFS=false
PERSISTENT_HOME=true
HADR_ENABLED=false
ETCD_ENDPOINT=
ETCD_USERNAME=
ETCD_PASSWORD=

其中

  • LICENSE 接受此映像中包含的 Db2 软件的条款和条件
  • DB2INSTANCE 指定 Db2 实例名称
  • DB2INST1_PASSWORD 指定 Db2 实例的密码
  • DBNAME 使用提供的名称创建初始数据库(如果不需要数据库,请保留为空)
  • BLU 将 Db2 实例的 BLU Acceleration 设置为已启用 (true) 或已禁用 (false)
  • ENABLE_ORACLE_COMPATIBILITY 将该实例的 Oracle 兼容性设置为启用 (true) 或禁用 (false)
  • 如果存在运行具有更高 Db2 级别的新容器的现有实例,那么可以将 UPDATEAVAIL 设置为 YES
  • TO_CREATE_SAMPLEDB 创建样本(预填充)数据库 (true)
  • REPODB 创建 Data Server Manager 存储库数据库 (true)
  • IS_OSXFS 将操作系统标识为 macOS (true)
  • 缺省情况下,PERSISTENT_HOME 设置为 true,仅当运行 Docker for Windows 时,才应该将此项指定为 false
  • HADR_ENABLED 为实例配置 Db2 HADR (true)。 以下三个环境变量依赖于 HADR_ENABLED 设置为 true
    • ETCD_ENDPOINT 指定您自己提供的 ETCD 键值存储。 输入端点,以逗号(并且没有空格)作为分隔符。 如果 HADR_ENABLED 设置为 true,那么需要此环境变量
    • ETCD_USERNAME 指定 ETCD 的用户名凭证。 如果留空,那么将使用 Db2 实例
    • ETCD_PASSWORD 指定 ETCD 的密码凭证。 如果留空,那么将使用 Db2 实例密码
7.通过以下方式保存该文件:按 ESCAPE (ESC) 键,然后输入:
:wq!
8.输入并运行以下命令以进入 Docker 容器:
docker run -h db2server --name db2server --restart=always --detach --privileged=true 
-p 50000:50000 --env-file .env_list -v /Docker:/database icr.io/db2_community/db2

其中
-h 将名称 db2server 分配给 Docker 容器。
-p 指定要使用的端口号。
--privileged 以特权方式启动容器。
-v 定义用于 Db2 Docker 映像的卷。

9.输入以下命令以访问 Docker 容器中正在运行的 Db2 实例:
docker exec -ti db2server bash -c "su - db2inst1"

 其中 db2inst1 是与 .env_list 文件中的 DB2INSTANCE 变量相关联的值。

10.现在,您可以在活动实例中创建 Db2 数据库。

下面继续接上述命令测试 Linux 系统上的 Db2 Community Edition Docker 映像安装

11.输入以下命令以创建样本数据库并开始执行 SQL 命令:
db2sampl -force -sql i

此命令可能会导致发生以下错误:

Creating database "SAMPLE"...
  Connecting to database "SAMPLE"...
  Creating tables and data in schema "DB2INST1"...

--ERROR------------------

  SQLSTATE          = 38503
  Native Error Code = -1131

[IBM][CLI Driver][DB2/LINUXX8664] SQL1131N  A stored procedure process 
has been terminated abnormally. Routine name: "SYSPROC.CREATE_SQL_SAMPLE". 
Specific name: "CREATE_SQL_SAMPLE".  SQLSTATE=38503

-------------------------

--ERROR------------------

  SQLSTATE          = 08007
  Native Error Code = -1224

[IBM][CLI Driver] SQL1224N  The database manager is not able to accept 
new requests, has terminated all requests in progress, or has terminated 
the specified request because of an error or a forced interrupt.  SQLSTATE=55032

-------------------------

  'db2sampl' processing complete.

 如果发生此错误,请输入以下命令:

db2 deactivate db sample
db2 drop db sample
db2 connect to sample

输出现在应该是:

SQL1013N The database alias name or database name "SAMPLE" could not be found. SQLSTATE=42705" 4

 重新输入 db2sampl -force -sql i 以生成样本数据库。

12.运行 db2 connect to sample

在连接至样本数据库后,可以开始执行 SQL 语句,例如,执行以下 SQL 语句:

db2 "select * from department" 

从表 "department" 中选择所有行 (* 表示所有行) ,或

db2 "select * from department fetch first 1 rows only"

 以从表“department”中选择第一行。 要更改检索的行数,只需在命令中增大该数目。

13.如果数据库返回预期结果,那么可以输入 db2 connect reset 9 以终止 Db2 数据库连接。
14.输入 logout 以结束 Docker 会话。

2、配置数据库连接

META-INF/persistence.xml 配置 JPA 连接:

<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">
    <persistence-unit name="orgPU">
        <jta-data-source>jdbc/db2DS</jta-data-source>
        <properties>
            <property name="jakarta.persistence.jdbc.driver" value="com.ibm.db2.jcc.DB2Driver"/>
            <property name="jakarta.persistence.jdbc.url" value="jdbc:db2://localhost:50000/mydb"/>
            <property name="jakarta.persistence.jdbc.user" value="db2inst1"/>
            <property name="jakarta.persistence.jdbc.password" value="mysecretpassword"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.DB2Dialect"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
        </properties>
    </persistence-unit>
</persistence>

 🔹 确保 server.xml 也启用了 JDBC:

<dataSource id="db2DS" jndiName="jdbc/db2DS">
    <jdbcDriver libraryRef="db2Lib"/>
    <properties databaseName="mydb" user="db2inst1" password="mysecretpassword" />
</dataSource>
<library id="db2Lib">
    <file name="/path/to/db2jcc4.jar"/>
</library>

3、设计实体类

组织管理(Organization)

@Entity
public class Organization {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = "organization")
    private List<Person> people;
}

 人员管理(Person)

@Entity
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String email;

    @ManyToOne
    @JoinColumn(name = "organization_id")
    private Organization organization;

    @OneToOne(mappedBy = "person")
    private Account account;
}

 账号管理(Account)

@Entity
public class Account {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;
    private String password; // 需要加密存储

    @OneToOne
    @JoinColumn(name = "person_id")
    private Person person;
}

4、 设计 REST API

组织管理 API

@Path("/organizations")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class OrganizationResource {

    @PersistenceContext
    private EntityManager em;

    @POST
    public Response createOrganization(Organization org) {
        em.persist(org);
        return Response.status(Response.Status.CREATED).entity(org).build();
    }

    @GET
    public List<Organization> getAllOrganizations() {
        return em.createQuery("SELECT o FROM Organization o", Organization.class).getResultList();
    }
}

 人员管理 API

@Path("/people")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class PersonResource {

    @PersistenceContext
    private EntityManager em;

    @POST
    public Response createPerson(Person person) {
        em.persist(person);
        return Response.status(Response.Status.CREATED).entity(person).build();
    }

    @GET
    public List<Person> getAllPeople() {
        return em.createQuery("SELECT p FROM Person p", Person.class).getResultList();
    }
}

 账号管理 API

@Path("/accounts")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class AccountResource {

    @PersistenceContext
    private EntityManager em;

    @POST
    public Response createAccount(Account account) {
        account.setPassword(hashPassword(account.getPassword())); // 存储加密密码
        em.persist(account);
        return Response.status(Response.Status.CREATED).entity(account).build();
    }

    private String hashPassword(String password) {
        return BCrypt.withDefaults().hashToString(12, password.toCharArray());
    }
}

5、JWT 认证

server.xml 添加 JWT 配置:

<featureManager>
    <feature>mpJwt-1.2</feature>
</featureManager>
<mpJwt loginModuleName="JWTLoginModule"/>

 创建 JWT 认证过滤器:

@Provider
@Priority(Priorities.AUTHENTICATION)
public class JWTAuthFilter implements ContainerRequestFilter {
    @Override
    public void filter(ContainerRequestContext requestContext) {
        String token = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
        if (token == null || !validateJWT(token)) {
            requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
        }
    }

    private boolean validateJWT(String token) {
        try {
            Algorithm algorithm = Algorithm.HMAC256("secret"); // 真实项目请存入配置
            JWT.require(algorithm).build().verify(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

6、运行应用

1.启动 Open Liberty:

mvn liberty:dev

 2.测试 API:

curl -X POST http://localhost:9080/api/organizations -H "Content-Type: application/json" -d '{"name":"OpenAI Inc"}'
curl -X POST http://localhost:9080/api/people -H "Content-Type: application/json" -d '{"name":"William", "email":"william@example.com", "organization":{"id":1}}'

总结

支持组织、人员、账号管理
连接 IBM DB2
使用 JWT 认证
使用 Open Liberty + MicroProfile 进行微服务开发

四、用前端 UI 或 API Gateway 来整合这些服务

 系统现在包括:

微服务后端(Open Liberty + MicroProfile + DB2)
身份认证(JWT)
需要前端 UI
需要 API Gateway 进行统一管理

方案设计

💡 完整架构

[ 前端 UI ]  <-->  [ API Gateway ]  <-->  [ 微服务后端 (Liberty + DB2) ]
  • 前端 UI:使用 React(或 Vue.js)实现一个管理界面,提供登录、组织、人员、账号管理功能。
  • API Gateway:使用 Kong 或 Spring Cloud Gateway 统一管理微服务 API,提供身份认证和路由转发。
  • 后端服务:继续使用 Open Liberty 运行微服务并连接 IBM DB2。

1、API Gateway 选型

推荐 Kong Gateway,因为它:
✅ 轻量级,支持 Docker 运行
✅ 内置 JWT 认证
✅ 可用于负载均衡、日志、限流

📌 使用 Docker 启动 Kong

docker run -d --name kong \
  -e KONG_DATABASE=off \
  -e KONG_DECLARATIVE_CONFIG=/kong/kong.yml \
  -v $(pwd)/kong.yml:/kong/kong.yml \
  -p 8000:8000 -p 8001:8001 kong

 📌 配置 kong.yml

_format_version: "2.1"

services:
  - name: user-service
    url: http://microservice:9080
    routes:
      - name: user-api
        paths:
          - /api

plugins:
  - name: jwt
    service: user-service

👉 这样,前端 UI 可以通过 http://localhost:8000/api/ 访问微服务。

2、前端 UI

我们可以使用 React + Vite + TailwindCSS 作为前端框架。

📌 安装 React + Vite:

npm create vite@latest frontend --template react
cd frontend
npm install
npm install axios react-router-dom

 📌 配置 API 访问

import axios from "axios";

const API_URL = "http://localhost:8000/api";

export const login = async (username, password) => {
    const response = await axios.post(`${API_URL}/auth/login`, { username, password });
    localStorage.setItem("token", response.data.token);
};

export const getOrganizations = async () => {
    const token = localStorage.getItem("token");
    return axios.get(`${API_URL}/organizations`, {
        headers: { Authorization: `Bearer ${token}` }
    });
};

📌 创建登录页面 Login.js

import { useState } from "react";
import { login } from "./api";

export default function Login() {
    const [username, setUsername] = useState("");
    const [password, setPassword] = useState("");

    const handleSubmit = async (e) => {
        e.preventDefault();
        await login(username, password);
        window.location.href = "/dashboard";
    };

    return (
        <div className="flex flex-col items-center">
            <h2>Login</h2>
            <form onSubmit={handleSubmit}>
                <input type="text" placeholder="Username" onChange={(e) => setUsername(e.target.value)} />
                <input type="password" placeholder="Password" onChange={(e) => setPassword(e.target.value)} />
                <button type="submit">Login</button>
            </form>
        </div>
    );
}

 📌 创建组织管理页面 Organizations.js

import { useEffect, useState } from "react";
import { getOrganizations } from "./api";

export default function Organizations() {
    const [organizations, setOrganizations] = useState([]);

    useEffect(() => {
        getOrganizations().then(response => setOrganizations(response.data));
    }, []);

    return (
        <div>
            <h2>Organizations</h2>
            <ul>
                {organizations.map(org => (
                    <li key={org.id}>{org.name}</li>
                ))}
            </ul>
        </div>
    );
}

📌 配置路由 App.js

import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Login from "./Login";
import Organizations from "./Organizations";

export default function App() {
    return (
        <Router>
            <Routes>
                <Route path="/" element={<Login />} />
                <Route path="/dashboard" element={<Organizations />} />
            </Routes>
        </Router>
    );
}

📌 启动前端 UI

npm run dev

👉 访问 http://localhost:5173 进行登录 🎉

3、Open Liberty 端 JWT 认证

在 Open Liberty 服务器端的 server.xml,添加 JWT 认证:

<featureManager>
    <feature>mpJwt-1.2</feature>
</featureManager>

<mpJwt loginModuleName="JWTLoginModule"/>

 📌 创建 AuthResource.java 进行身份认证:

@Path("/auth")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class AuthResource {

    @POST
    @Path("/login")
    public Response login(UserCredentials credentials) {
        String token = generateJWT(credentials.getUsername());
        return Response.ok().entity(Collections.singletonMap("token", token)).build();
    }

    private String generateJWT(String username) {
        Algorithm algorithm = Algorithm.HMAC256("secret");
        return JWT.create()
                  .withSubject(username)
                  .withExpiresAt(new Date(System.currentTimeMillis() + 3600000))
                  .sign(algorithm);
    }
}

📌 创建 JWTAuthFilter.java 保护 API

@Provider
@Priority(Priorities.AUTHENTICATION)
public class JWTAuthFilter implements ContainerRequestFilter {
    @Override
    public void filter(ContainerRequestContext requestContext) {
        String token = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
        if (token == null || !validateJWT(token)) {
            requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
        }
    }

    private boolean validateJWT(String token) {
        try {
            Algorithm algorithm = Algorithm.HMAC256("secret");
            JWT.require(algorithm).build().verify(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

4、运行整个系统

1. 启动 DB2

docker start db2

2. 启动 Open Liberty

mvn liberty:dev

 ✅ 3. 启动 API Gateway

docker start kong

4. 启动前端 UI

npm run dev

🔗 访问 http://localhost:5173 进行登录和管理组织、人员、账号!🎉

总结

前端 UI:React + Vite + Tailwind
API Gateway:Kong 进行路由和身份认证
后端微服务:Open Liberty + MicroProfile + IBM DB2
身份认证:JWT

五、 增加角色管理、数据权限控制示例

将在现有的 组织人员账号管理 微服务中增加:

角色管理(Role):定义 管理员、普通用户 等角色
数据权限控制:限制用户 只能访问自己权限范围内的数据
前端 UI 调整:支持 基于角色的页面显示


方案设计

1.新增角色管理

  • 角色(Role):定义不同用户权限级别(Admin / User)
  • 权限控制
    • 管理员:可以管理 组织、人员、账号
    • 普通用户:只能管理 自己的账号

2.数据访问权限

  • 普通用户 只能查询、修改自己的信息
  • 管理员 可以查看所有用户

3.角色认证

  • 登录后返回 JWT,JWT 内嵌用户角色信息
  • 拦截器解析 JWT 并检查权限
  • 不同角色访问不同 API

1、角色管理 - 数据模型

修改 Account 实体,增加 Role
@Entity
public class Account {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;
    private String password;

    @Enumerated(EnumType.STRING)
    private Role role;

    @OneToOne
    @JoinColumn(name = "person_id")
    private Person person;
}
 新增 Role 枚举
public enum Role {
    ADMIN,
    USER
}

2、角色管理 - API

修改登录 API,返回用户角色
@Path("/auth")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class AuthResource {

    @PersistenceContext
    private EntityManager em;

    @POST
    @Path("/login")
    public Response login(UserCredentials credentials) {
        Account account = em.createQuery("SELECT a FROM Account a WHERE a.username = :username", Account.class)
                            .setParameter("username", credentials.getUsername())
                            .getSingleResult();
        
        if (!BCrypt.verifyer().verify(credentials.getPassword().toCharArray(), account.getPassword()).verified) {
            return Response.status(Response.Status.UNAUTHORIZED).build();
        }

        String token = generateJWT(account.getUsername(), account.getRole());
        return Response.ok().entity(Collections.singletonMap("token", token)).build();
    }

    private String generateJWT(String username, Role role) {
        Algorithm algorithm = Algorithm.HMAC256("secret");
        return JWT.create()
                  .withSubject(username)
                  .withClaim("role", role.name())  // 角色信息
                  .withExpiresAt(new Date(System.currentTimeMillis() + 3600000))
                  .sign(algorithm);
    }
}
JWT 拦截器 - 解析用户角色
@Provider
@Priority(Priorities.AUTHENTICATION)
public class JWTAuthFilter implements ContainerRequestFilter {
    @Override
    public void filter(ContainerRequestContext requestContext) {
        String token = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
        if (token == null || !validateJWT(token)) {
            requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
        }
    }

    private boolean validateJWT(String token) {
        try {
            Algorithm algorithm = Algorithm.HMAC256("secret");
            DecodedJWT jwt = JWT.require(algorithm).build().verify(token);
            requestContext.setProperty("role", jwt.getClaim("role").asString()); // 解析角色
            requestContext.setProperty("username", jwt.getSubject());  // 解析用户名
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

3、数据权限控制

修改 API,控制不同角色权限

📌 普通用户只能访问自己的数据管理员可以访问所有人

组织管理 API

@Path("/organizations")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class OrganizationResource {

    @PersistenceContext
    private EntityManager em;

    @Context
    private SecurityContext securityContext;

    @GET
    public List<Organization> getOrganizations() {
        String role = securityContext.getUserPrincipal().getName();
        if ("ADMIN".equals(role)) {
            return em.createQuery("SELECT o FROM Organization o", Organization.class).getResultList();
        } else {
            return em.createQuery("SELECT o FROM Organization o WHERE o.id = :userOrg", Organization.class)
                     .setParameter("userOrg", getCurrentUserOrganization())
                     .getResultList();
        }
    }

    private Long getCurrentUserOrganization() {
        return em.createQuery("SELECT p.organization.id FROM Person p WHERE p.account.username = :username", Long.class)
                 .setParameter("username", securityContext.getUserPrincipal().getName())
                 .getSingleResult();
    }
}

4、前端 UI - 角色控制

📌 前端解析 JWT,基于角色显示不同页面
修改 api.js,解析用户角色:

export const getUserRole = () => {
    const token = localStorage.getItem("token");
    if (!token) return null;
    
    const payload = JSON.parse(atob(token.split(".")[1]));
    return payload.role;
};

 📌 修改 Organizations.js,不同角色显示不同功能

import { useEffect, useState } from "react";
import { getOrganizations, getUserRole } from "./api";

export default function Organizations() {
    const [organizations, setOrganizations] = useState([]);
    const role = getUserRole();

    useEffect(() => {
        getOrganizations().then(response => setOrganizations(response.data));
    }, []);

    return (
        <div>
            <h2>Organizations</h2>
            {role === "ADMIN" && <button>Add Organization</button>}
            <ul>
                {organizations.map(org => (
                    <li key={org.id}>{org.name}</li>
                ))}
            </ul>
        </div>
    );
}

📌 修改 App.js,根据角色重定向

import { BrowserRouter as Router, Routes, Route, Navigate } from "react-router-dom";
import Login from "./Login";
import Organizations from "./Organizations";
import { getUserRole } from "./api";

export default function App() {
    const role = getUserRole();

    return (
        <Router>
            <Routes>
                <Route path="/" element={<Login />} />
                <Route path="/dashboard" element={role ? <Organizations /> : <Navigate to="/" />} />
            </Routes>
        </Router>
    );
}

总结

新增角色管理(管理员 / 普通用户)
JWT 认证返回用户角色信息
不同角色访问不同 API(管理员可查看所有数据,普通用户仅限自身)
前端 UI 根据角色显示不同功能

六、更细粒度的 CRUD(增删改查)权限控制

方案设计

💡 基于角色的权限控制(RBAC)

角色查询 (Read)创建 (Create)更新 (Update)删除 (Delete)
管理员 (ADMIN)
普通用户 (USER)✅(仅限自己)✅(仅限自己)

1、更新 Role 枚举

我们为角色添加权限:

public enum Role {
    ADMIN(Set.of(Permission.READ, Permission.CREATE, Permission.UPDATE, Permission.DELETE)),
    USER(Set.of(Permission.READ, Permission.UPDATE)); // 普通用户只能读和更新自己的数据

    private final Set<Permission> permissions;

    Role(Set<Permission> permissions) {
        this.permissions = permissions;
    }

    public boolean hasPermission(Permission permission) {
        return permissions.contains(permission);
    }
}

定义 权限枚举

public enum Permission {
    READ, CREATE, UPDATE, DELETE;
}

2、增加权限拦截器

拦截 API 请求,检查用户是否有权限执行操作

@Provider
@Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter {

    @Context
    private SecurityContext securityContext;

    @Override
    public void filter(ContainerRequestContext requestContext) {
        String role = (String) requestContext.getProperty("role");
        Role userRole = Role.valueOf(role);

        String method = requestContext.getMethod();
        Permission requiredPermission = getRequiredPermission(method);

        if (!userRole.hasPermission(requiredPermission)) {
            requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access Denied").build());
        }
    }

    private Permission getRequiredPermission(String method) {
        switch (method) {
            case "POST": return Permission.CREATE;
            case "PUT": return Permission.UPDATE;
            case "DELETE": return Permission.DELETE;
            default: return Permission.READ;
        }
    }
}

3、在 API 端点中检查权限

组织管理 API

📌 管理员可以管理所有组织,普通用户只能查看自己的组织

@Path("/organizations")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class OrganizationResource {

    @PersistenceContext
    private EntityManager em;

    @Context
    private SecurityContext securityContext;

    @GET
    public List<Organization> getOrganizations() {
        String role = securityContext.getUserPrincipal().getName();
        if ("ADMIN".equals(role)) {
            return em.createQuery("SELECT o FROM Organization o", Organization.class).getResultList();
        } else {
            return em.createQuery("SELECT o FROM Organization o WHERE o.id = :userOrg", Organization.class)
                     .setParameter("userOrg", getCurrentUserOrganization())
                     .getResultList();
        }
    }

    @POST
    public Response createOrganization(Organization org) {
        checkPermission(Permission.CREATE);
        em.persist(org);
        return Response.status(Response.Status.CREATED).entity(org).build();
    }

    @PUT
    @Path("/{id}")
    public Response updateOrganization(@PathParam("id") Long id, Organization org) {
        checkPermission(Permission.UPDATE);
        Organization existing = em.find(Organization.class, id);
        existing.setName(org.getName());
        em.merge(existing);
        return Response.ok(existing).build();
    }

    @DELETE
    @Path("/{id}")
    public Response deleteOrganization(@PathParam("id") Long id) {
        checkPermission(Permission.DELETE);
        Organization org = em.find(Organization.class, id);
        em.remove(org);
        return Response.noContent().build();
    }

    private void checkPermission(Permission permission) {
        String role = securityContext.getUserPrincipal().getName();
        Role userRole = Role.valueOf(role);
        if (!userRole.hasPermission(permission)) {
            throw new WebApplicationException("Access Denied", Response.Status.FORBIDDEN);
        }
    }

    private Long getCurrentUserOrganization() {
        return em.createQuery("SELECT p.organization.id FROM Person p WHERE p.account.username = :username", Long.class)
                 .setParameter("username", securityContext.getUserPrincipal().getName())
                 .getSingleResult();
    }
}

4、 修改前端 UI

📌 根据角色隐藏不允许的按钮
修改 Organizations.js

import { useEffect, useState } from "react";
import { getOrganizations, getUserRole } from "./api";

export default function Organizations() {
    const [organizations, setOrganizations] = useState([]);
    const role = getUserRole();

    useEffect(() => {
        getOrganizations().then(response => setOrganizations(response.data));
    }, []);

    return (
        <div>
            <h2>Organizations</h2>
            {role === "ADMIN" && <button>Add Organization</button>}
            <ul>
                {organizations.map(org => (
                    <li key={org.id}>
                        {org.name} 
                        {role === "ADMIN" && <button>Edit</button>}
                        {role === "ADMIN" && <button>Delete</button>}
                    </li>
                ))}
            </ul>
        </div>
    );
}

 📌 修改 api.js,检查权限

import { canUser } from "./api";

export default function Organizations() {
    return (
        <div>
            <h2>Organizations</h2>
            {canUser("CREATE") && <button>Add Organization</button>}
        </div>
    );
}

总结

基于角色的权限控制(RBAC)
CRUD 权限检查(API 端限制)
前端 UI 只显示允许的操作

未完,待续......

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深海科技服务

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值