如何处理 Spring Boot 应用程序中的多租户问题?
How to handle the multitenancy problem in Spring Boot applications?
假设我有一个具有以下要求的 SpringBoot
应用程序。
- 它有一个
User
class(实体)
- 每个用户有零个或多个
Workspaces
(一对多实体关系)
- 每个
Workspace
有零个或多个WorkItems
(一对多实体关系)
有一个 CRUD REST API 控制器来管理所有实体,即我们有
UserController
-> User
实体的 CRUD 操作
WorkspaceController
-> Workspace
实体的 CRUD 操作
WorkItemContoller
-> WorkItem
实体的 CRUD 操作
现在有要求...
- 一个
User
只能create/edit/delete他自己的Workspace
个实体
- 一个
User
在他自己的Workspaces
中只能create/edit/deleteWorkItem
个实体
此外,假设 User
实体与 SpringSecurity
集成并且我们知道控制器和服务中的当前用户。
那么问题是...
最elegant/clean/maintainable实现用户权限检查的方法是什么?我们如何编写代码,在 Service
classes 中将检查用户是否有权对给定资源执行操作。
我现在的做法是有一个像这样的 class,它会在每个 Service
调用中检查权限。
class PermissionManager {
void checkUserAllowedToUseWorkspace(User u, Workspace w);
void checkUserAlloweToUseWorkitem(User u, WorkItem)
}
如您所见...随着作用域资源数量的增加...此 class 将变得非常臃肿且难以维护。
有没有人知道以干净且可维护的方式执行此范围资源访问的更好方法?
工作区实体的一些数据库查询:
// DELETE
@Transactional
void deleteByIdAndUserId(String workspaceId, String userId);
userRepository.deleteByIdAndUserId(workspaceId, userId);
// UPDATE
Optional<Workspace> workspace = workspace.findByIdAndUserId(String workspaceId, String userId);
Workspace workspace = workspace.orElseThrow(() -> new RuntimeException("You don't have an workspace under user "))
workspace.setName("MyWorkspace2");
//CREATE
User user = userRepository.findById(SecurityContextHolder...getPriciple().getId())
.orElseThrow(() -> new RuntimeException("User can not be found by given id"));
workspaceRepository.save(WorkspaceBuilder.builder().user(user).name("firstWorkspace").build());
最干净和可维护的解决方案是利用 Spring 安全性 AOP 来完成任务。
您可以使用 @PreAuthorize
注释,与您的 PermissionManager
服务配对,利用 [=35 的力量允许或拒绝 Controller
级别的访问=] 表达语言.
定义了一个 Service
构造型来检查用户对特定资源(您的示例中的工作区)的访问权限:
@Service
public class PermissionManagerImpl implements PermissionManager {
@Autowired
private UserRepository userRepository;
/**
* @param authentication the current authenticated user following your authentication scheme
* @param workspaceId the workspace (or other resource) identifier
*/
@Override
public boolean checkUserAllowedToUseWorkspace(Authentication authentication, Long workspaceId) {
return authentication != null
/* check that the `authentication` has access to the argument workspace: e.g. userRepository.findWorkspaceByUserNameAndWorkspaceId(authentication.getName(), workspaceId) != null */;
}
}
您可以在 Controller
方法上定义基于表达式的控制策略,如下所示:
@RestController
public class WorkspaceController {
// the DIed `permissionManagerImpl` service will be called prior to your endpoint invocation with the current `authentication` and workspace `id` injected
@PreAuthorize("@permissionManagerImpl.checkUserAllowedToUseWorkspace(authentication, #id)")
@RequestMapping("/workspaces/{id}")
public List<Workspace> getWorkspaces(@PathVariable Long id) {
// retrieve the user workspaces once authorized
}
}
这将导致可读和可重用的解决方案作用于顶级资源,Controller
。
您可以在 Expression-based Access Control in the official Spring docs 上了解更多信息。
假设我有一个具有以下要求的 SpringBoot
应用程序。
- 它有一个
User
class(实体) - 每个用户有零个或多个
Workspaces
(一对多实体关系) - 每个
Workspace
有零个或多个WorkItems
(一对多实体关系)
有一个 CRUD REST API 控制器来管理所有实体,即我们有
UserController
->User
实体的 CRUD 操作WorkspaceController
->Workspace
实体的 CRUD 操作WorkItemContoller
->WorkItem
实体的 CRUD 操作
现在有要求...
- 一个
User
只能create/edit/delete他自己的Workspace
个实体 - 一个
User
在他自己的Workspaces
中只能create/edit/delete
WorkItem
个实体
此外,假设 User
实体与 SpringSecurity
集成并且我们知道控制器和服务中的当前用户。
那么问题是...
最elegant/clean/maintainable实现用户权限检查的方法是什么?我们如何编写代码,在 Service
classes 中将检查用户是否有权对给定资源执行操作。
我现在的做法是有一个像这样的 class,它会在每个 Service
调用中检查权限。
class PermissionManager {
void checkUserAllowedToUseWorkspace(User u, Workspace w);
void checkUserAlloweToUseWorkitem(User u, WorkItem)
}
如您所见...随着作用域资源数量的增加...此 class 将变得非常臃肿且难以维护。
有没有人知道以干净且可维护的方式执行此范围资源访问的更好方法?
工作区实体的一些数据库查询:
// DELETE
@Transactional
void deleteByIdAndUserId(String workspaceId, String userId);
userRepository.deleteByIdAndUserId(workspaceId, userId);
// UPDATE
Optional<Workspace> workspace = workspace.findByIdAndUserId(String workspaceId, String userId);
Workspace workspace = workspace.orElseThrow(() -> new RuntimeException("You don't have an workspace under user "))
workspace.setName("MyWorkspace2");
//CREATE
User user = userRepository.findById(SecurityContextHolder...getPriciple().getId())
.orElseThrow(() -> new RuntimeException("User can not be found by given id"));
workspaceRepository.save(WorkspaceBuilder.builder().user(user).name("firstWorkspace").build());
最干净和可维护的解决方案是利用 Spring 安全性 AOP 来完成任务。
您可以使用 @PreAuthorize
注释,与您的 PermissionManager
服务配对,利用 [=35 的力量允许或拒绝 Controller
级别的访问=] 表达语言.
定义了一个 Service
构造型来检查用户对特定资源(您的示例中的工作区)的访问权限:
@Service
public class PermissionManagerImpl implements PermissionManager {
@Autowired
private UserRepository userRepository;
/**
* @param authentication the current authenticated user following your authentication scheme
* @param workspaceId the workspace (or other resource) identifier
*/
@Override
public boolean checkUserAllowedToUseWorkspace(Authentication authentication, Long workspaceId) {
return authentication != null
/* check that the `authentication` has access to the argument workspace: e.g. userRepository.findWorkspaceByUserNameAndWorkspaceId(authentication.getName(), workspaceId) != null */;
}
}
您可以在 Controller
方法上定义基于表达式的控制策略,如下所示:
@RestController
public class WorkspaceController {
// the DIed `permissionManagerImpl` service will be called prior to your endpoint invocation with the current `authentication` and workspace `id` injected
@PreAuthorize("@permissionManagerImpl.checkUserAllowedToUseWorkspace(authentication, #id)")
@RequestMapping("/workspaces/{id}")
public List<Workspace> getWorkspaces(@PathVariable Long id) {
// retrieve the user workspaces once authorized
}
}
这将导致可读和可重用的解决方案作用于顶级资源,Controller
。
您可以在 Expression-based Access Control in the official Spring docs 上了解更多信息。