乐都企业网站建设哪家好成都专业seo公司
续上篇
七、实现动态权限分配
目前,我们的系统基于 角色(Role) 进行权限控制,但角色权限是固定的。
现在,我们要实现:
✅ 用户可动态分配权限(而不是仅靠角色)
✅ 每个用户可以拥有不同的权限集(CRUD 操作可灵活授权)
✅ 管理员可管理用户权限
方案设计
💡 采用基于权限的访问控制(PBAC - Permission-Based Access Control)
- 用户 → 拥有多个权限(READ, CREATE, UPDATE, DELETE)
- 管理员 → 可以动态调整用户权限
1、数据模型
定义 Permission
实体
@Entity
public class Permission {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Enumerated(EnumType.STRING)private PermissionType type;@ManyToMany(mappedBy = "permissions")private List<Account> accounts;
}
定义 PermissionType
枚举
public enum PermissionType {READ, CREATE, UPDATE, DELETE
}
修改 Account
,增加权限字段
@Entity
public class Account {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String username;private String password;@Enumerated(EnumType.STRING)private Role role; // 仍保留角色,但权限可单独管理@ManyToMany@JoinTable(name = "account_permissions",joinColumns = @JoinColumn(name = "account_id"),inverseJoinColumns = @JoinColumn(name = "permission_id"))private List<Permission> permissions;
}
2、动态权限管理 API
获取用户权限
@Path("/permissions")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class PermissionResource {@PersistenceContextprivate EntityManager em;@GET@Path("/{userId}")public List<PermissionType> getUserPermissions(@PathParam("userId") Long userId) {Account account = em.find(Account.class, userId);return account.getPermissions().stream().map(Permission::getType).collect(Collectors.toList());}
}
管理员给用户分配权限
@POST
@Path("/{userId}/assign")
public Response assignPermission(@PathParam("userId") Long userId, PermissionType permissionType) {Account account = em.find(Account.class, userId);Permission permission = em.createQuery("SELECT p FROM Permission p WHERE p.type = :type", Permission.class).setParameter("type", permissionType).getSingleResult();account.getPermissions().add(permission);em.merge(account);return Response.ok().build();
}
移除用户权限
@DELETE
@Path("/{userId}/remove/{permissionType}")
public Response removePermission(@PathParam("userId") Long userId, @PathParam("permissionType") PermissionType permissionType) {Account account = em.find(Account.class, userId);account.getPermissions().removeIf(p -> p.getType() == permissionType);em.merge(account);return Response.ok().build();
}
3、修改权限拦截器
📌 检查用户权限,而不仅仅是角色
@Provider
@Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter {@PersistenceContextprivate EntityManager em;@Contextprivate SecurityContext securityContext;@Overridepublic void filter(ContainerRequestContext requestContext) {String username = securityContext.getUserPrincipal().getName();Account account = em.createQuery("SELECT a FROM Account a WHERE a.username = :username", Account.class).setParameter("username", username).getSingleResult();PermissionType requiredPermission = getRequiredPermission(requestContext.getMethod());boolean hasPermission = account.getPermissions().stream().anyMatch(p -> p.getType() == requiredPermission);if (!hasPermission) {requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access Denied").build());}}private PermissionType getRequiredPermission(String method) {switch (method) {case "POST": return PermissionType.CREATE;case "PUT": return PermissionType.UPDATE;case "DELETE": return PermissionType.DELETE;default: return PermissionType.READ;}}
}
4、前端 UI - 动态权限管理
1️⃣ 获取用户权限
📌 前端调用 API
export const getUserPermissions = async (userId) => {const token = localStorage.getItem("token");return axios.get(`http://localhost:8000/api/permissions/${userId}`, {headers: { Authorization: `Bearer ${token}` }});
};
2️⃣ 管理员分配权限
📌 前端调用 API
export const assignPermission = async (userId, permissionType) => {const token = localStorage.getItem("token");return axios.post(`http://localhost:8000/api/permissions/${userId}/assign`, { permissionType }, {headers: { Authorization: `Bearer ${token}` }});
};
3️⃣ UI 角色管理界面
📌 创建 Permissions.js
import { useEffect, useState } from "react";
import { getUserPermissions, assignPermission } from "./api";export default function Permissions({ userId }) {const [permissions, setPermissions] = useState([]);useEffect(() => {getUserPermissions(userId).then(response => setPermissions(response.data));}, [userId]);const handleAssign = (permissionType) => {assignPermission(userId, permissionType).then(() => {setPermissions([...permissions, permissionType]);});};return (<div><h2>User Permissions</h2><ul>{permissions.map(perm => <li key={perm}>{perm}</li>)}</ul><button onClick={() => handleAssign("CREATE")}>Grant Create</button><button onClick={() => handleAssign("UPDATE")}>Grant Update</button><button onClick={() => handleAssign("DELETE")}>Grant Delete</button></div>);
}
4️⃣ 在 App.js
中添加权限管理路由
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Permissions from "./Permissions";export default function App() {return (<Router><Routes><Route path="/permissions/:userId" element={<Permissions />} /></Routes></Router>);
}
📌 管理员可以访问 http://localhost:5173/permissions/1
来管理用户权限
总结
✅ 用户权限可动态管理
✅ 管理员可随时授予/撤销权限
✅ 前端 UI 自动适配不同权限
八、 实现用户组(User Group)功能
当前系统已经支持 动态权限分配,但每个用户需要单独管理权限,不够高效。
现在,我们要实现:
✅ 用户可以加入多个用户组(每个组有一套权限)
✅ 用户自动继承用户组权限
✅ 管理员可以动态管理用户组
方案设计
💡 采用基于用户组(User Group)的权限管理
- 用户组(UserGroup):一组用户共享相同的权限(如 "人事部"、"财务部")。
- 用户(Account):可以加入多个组,并自动继承组的权限。
- 权限继承:
- 用户权限 = 个人权限 + 所属用户组权限
- 组权限变更时,所有成员的权限自动更新
1、数据模型
定义 UserGroup
实体
@Entity
public class UserGroup {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String name;@ManyToMany@JoinTable(name = "group_permissions",joinColumns = @JoinColumn(name = "group_id"),inverseJoinColumns = @JoinColumn(name = "permission_id"))private List<Permission> permissions;@ManyToMany(mappedBy = "groups")private List<Account> members;
}
修改 Account
,增加用户组关联
@Entity
public class Account {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String username;private String password;@ManyToMany@JoinTable(name = "account_groups",joinColumns = @JoinColumn(name = "account_id"),inverseJoinColumns = @JoinColumn(name = "group_id"))private List<UserGroup> groups;@ManyToMany@JoinTable(name = "account_permissions",joinColumns = @JoinColumn(name = "account_id"),inverseJoinColumns = @JoinColumn(name = "permission_id"))private List<Permission> permissions;
}
2. 动态用户组管理 API
创建用户组
@Path("/groups")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class UserGroupResource {@PersistenceContextprivate EntityManager em;@POSTpublic Response createGroup(UserGroup group) {em.persist(group);return Response.status(Response.Status.CREATED).entity(group).build();}
}
给用户组分配权限
@POST
@Path("/{groupId}/assign")
public Response assignPermission(@PathParam("groupId") Long groupId, PermissionType permissionType) {UserGroup group = em.find(UserGroup.class, groupId);Permission permission = em.createQuery("SELECT p FROM Permission p WHERE p.type = :type", Permission.class).setParameter("type", permissionType).getSingleResult();group.getPermissions().add(permission);em.merge(group);return Response.ok().build();
}
添加用户到用户组
@POST
@Path("/{groupId}/addUser/{userId}")
public Response addUserToGroup(@PathParam("groupId") Long groupId, @PathParam("userId") Long userId) {UserGroup group = em.find(UserGroup.class, groupId);Account user = em.find(Account.class, userId);group.getMembers().add(user);user.getGroups().add(group);em.merge(group);em.merge(user);return Response.ok().build();
}
移除用户组中的用户
@DELETE
@Path("/{groupId}/removeUser/{userId}")
public Response removeUserFromGroup(@PathParam("groupId") Long groupId, @PathParam("userId") Long userId) {UserGroup group = em.find(UserGroup.class, groupId);Account user = em.find(Account.class, userId);group.getMembers().remove(user);user.getGroups().remove(group);em.merge(group);em.merge(user);return Response.ok().build();
}
3、修改权限拦截器
📌 用户权限 = 个人权限 + 所属用户组的权限
@Provider
@Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter {@PersistenceContextprivate EntityManager em;@Contextprivate SecurityContext securityContext;@Overridepublic void filter(ContainerRequestContext requestContext) {String username = securityContext.getUserPrincipal().getName();Account account = em.createQuery("SELECT a FROM Account a WHERE a.username = :username", Account.class).setParameter("username", username).getSingleResult();PermissionType requiredPermission = getRequiredPermission(requestContext.getMethod());boolean hasPermission = account.getPermissions().stream().anyMatch(p -> p.getType() == requiredPermission)|| account.getGroups().stream().flatMap(g -> g.getPermissions().stream()).anyMatch(p -> p.getType() == requiredPermission);if (!hasPermission) {requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access Denied").build());}}private PermissionType getRequiredPermission(String method) {switch (method) {case "POST": return PermissionType.CREATE;case "PUT": return PermissionType.UPDATE;case "DELETE": return PermissionType.DELETE;default: return PermissionType.READ;}}
}
4、前端 UI
1️⃣ 获取用户组
export const getUserGroups = async () => {const token = localStorage.getItem("token");return axios.get(`http://localhost:8000/api/groups`, {headers: { Authorization: `Bearer ${token}` }});
};
2️⃣ 添加用户到组
export const addUserToGroup = async (groupId, userId) => {const token = localStorage.getItem("token");return axios.post(`http://localhost:8000/api/groups/${groupId}/addUser/${userId}`, {}, {headers: { Authorization: `Bearer ${token}` }});
};
3️⃣ UI - 用户组管理界面
📌 创建 UserGroups.js
import { useEffect, useState } from "react";
import { getUserGroups, addUserToGroup } from "./api";export default function UserGroups({ userId }) {const [groups, setGroups] = useState([]);useEffect(() => {getUserGroups().then(response => setGroups(response.data));}, []);const handleAddUser = (groupId) => {addUserToGroup(groupId, userId).then(() => alert("User added!"));};return (<div><h2>User Groups</h2><ul>{groups.map(group => (<li key={group.id}>{group.name} <button onClick={() => handleAddUser(group.id)}>Add User</button></li>))}</ul></div>);
}
总结
✅ 用户组管理(创建/删除)
✅ 组权限管理(动态修改)
✅ 用户继承组权限
✅ 前端 UI 适配
九、实现多租户(Multi-Tenancy)支持
目前,我们的系统支持 用户组(User Group) 和 动态权限分配,但所有用户共享同一数据库数据。
现在,我们要实现:
✅ 每个组织(Organization)拥有自己的独立数据
✅ 用户只能访问自己所属组织的数据
✅ 管理员可以管理自己组织的用户,但不能管理其他组织
✅ 支持多租户架构(Schema 分离 / 数据库分离 / 逻辑分离)
方案设计
💡 多租户支持的三种方案:
- 独立数据库(Database-per-Tenant):每个组织拥有自己的数据库
- 共享数据库,独立 Schema(Schema-per-Tenant):每个组织使用不同的 Schema
- 共享数据库,逻辑分离(Discriminator Column):同一表使用
tenant_id
进行数据隔离
选择方案: ✅ 方式 3(逻辑分离) 是最佳方案,适用于 SaaS 平台,管理成本低,数据隔离性强。
1、数据模型
修改 Organization
增加租户标识
@Entity
public class Organization {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String name;@OneToMany(mappedBy = "organization")private List<Account> accounts;
}
修改 Account
关联 Organization
@Entity
public class Account {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String username;private String password;@ManyToOne@JoinColumn(name = "organization_id")private Organization organization;
}
2、数据访问限制
📌 修改 EntityManager
,强制过滤 tenant_id
@Provider
public class TenantFilter implements ContainerRequestFilter {@PersistenceContextprivate EntityManager em;@Contextprivate SecurityContext securityContext;@Overridepublic void filter(ContainerRequestContext requestContext) {String username = securityContext.getUserPrincipal().getName();Account account = em.createQuery("SELECT a FROM Account a WHERE a.username = :username", Account.class).setParameter("username", username).getSingleResult();Long tenantId = account.getOrganization().getId();requestContext.setProperty("tenant_id", tenantId);}
}
3、多租户数据查询
📌 修改 OrganizationResource
,确保查询数据时只返回当前租户的数据
@Path("/organizations")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class OrganizationResource {@PersistenceContextprivate EntityManager em;@Contextprivate SecurityContext securityContext;@GETpublic List<Organization> getOrganizations() {Long tenantId = (Long) securityContext.getProperty("tenant_id");return em.createQuery("SELECT o FROM Organization o WHERE o.id = :tenantId", Organization.class).setParameter("tenantId", tenantId).getResultList();}
}
4、限制用户只能管理自己组织的账户
📌 修改 AccountResource
@Path("/accounts")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class AccountResource {@PersistenceContextprivate EntityManager em;@Contextprivate SecurityContext securityContext;@GETpublic List<Account> getAccounts() {Long tenantId = (Long) securityContext.getProperty("tenant_id");return em.createQuery("SELECT a FROM Account a WHERE a.organization.id = :tenantId", Account.class).setParameter("tenantId", tenantId).getResultList();}@POSTpublic Response createAccount(Account account) {Long tenantId = (Long) securityContext.getProperty("tenant_id");account.setOrganization(em.find(Organization.class, tenantId));em.persist(account);return Response.status(Response.Status.CREATED).entity(account).build();}
}
5、前端 UI 适配
1️⃣ 获取当前租户信息
export const getTenantOrganizations = async () => {const token = localStorage.getItem("token");return axios.get(`http://localhost:8000/api/organizations`, {headers: { Authorization: `Bearer ${token}` }});
};
2️⃣ 组织管理员只能查看本组织用户
export const getTenantUsers = async () => {const token = localStorage.getItem("token");return axios.get(`http://localhost:8000/api/accounts`, {headers: { Authorization: `Bearer ${token}` }});
};
3️⃣ UI - 限制不同租户的数据可见性
📌 修改 Organizations.js
import { useEffect, useState } from "react";
import { getTenantOrganizations } from "./api";export default function Organizations() {const [organizations, setOrganizations] = useState([]);useEffect(() => {getTenantOrganizations().then(response => setOrganizations(response.data));}, []);return (<div><h2>Organizations</h2><ul>{organizations.map(org => (<li key={org.id}>{org.name}</li>))}</ul></div>);
}
📌 修改 Users.js
import { useEffect, useState } from "react";
import { getTenantUsers } from "./api";export default function Users() {const [users, setUsers] = useState([]);useEffect(() => {getTenantUsers().then(response => setUsers(response.data));}, []);return (<div><h2>Users</h2><ul>{users.map(user => (<li key={user.id}>{user.username}</li>))}</ul></div>);
}
6、支持超级管理员(Super Admin)
📌 如果用户是 SUPER_ADMIN
,可以管理所有租户
@Path("/organizations")
@GET
public List<Organization> getOrganizations() {String role = securityContext.getUserPrincipal().getName();if ("SUPER_ADMIN".equals(role)) {return em.createQuery("SELECT o FROM Organization o", Organization.class).getResultList();}Long tenantId = (Long) securityContext.getProperty("tenant_id");return em.createQuery("SELECT o FROM Organization o WHERE o.id = :tenantId", Organization.class).setParameter("tenantId", tenantId).getResultList();
}
总结
✅ 每个组织(Organization)数据独立
✅ 用户只能访问自己组织的数据
✅ 管理员只能管理自己组织的用户
✅ 超级管理员可管理所有组织
未完,待续