Open Liberty 是一个轻量级、云原生的 Java 运行时,主要用于运行 Jakarta EE 和 MicroProfile应用。它的核心特点是模块化、快速启动和良好的可观测性。
一、 使用 Open Liberty + MicroProfile 进行微服务开发
核心步骤:
- 选择 MicroProfile 运行时(下载 Open Liberty
microProfile
版) - 配置 服务器(启用 MicroProfile 相关功能)
- 开发 RESTful 微服务
- 添加 配置、健康检查、监控、认证
- 运行并部署到 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、结合数据库开发
如果你的微服务需要数据库,可以本地运行 PostgreSQL 或 MySQL:
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 只显示允许的操作
未完,待续......