[英]How to handle the multitenancy problem in Spring Boot applications?
假設我有一個具有以下要求的SpringBoot
應用程序。
User
類(實體)Workspaces
(一對多實體關系)Workspace
有零個或多個WorkItems
(一對多實體關系)有一個 CRUD REST API 控制器來管理所有實體,即我們有
UserController
-> User
實體的 CRUD 操作WorkspaceController
-> Workspace
實體的 CRUD 操作WorkItemContoller
-> WorkItem
實體的 CRUD 操作現在有要求...
User
只能創建/編輯/刪除他自己的Workspace
實體User
只能在他自己的Workspaces
創建/編輯/刪除WorkItem
實體此外,假設User
實體與SpringSecurity
集成,並且我們知道控制器和服務中的當前用戶。
那么問題來了...
實現用戶權限檢查的最優雅/干凈/可維護的方式是什么? 我們如何編寫代碼,該代碼在Service
類中將檢查用戶是否有權對給定資源執行操作。
我現在這樣做的方式是有一個這樣的類,它檢查每個Service
調用中的權限。
class PermissionManager {
void checkUserAllowedToUseWorkspace(User u, Workspace w);
void checkUserAlloweToUseWorkitem(User u, WorkItem)
}
正如您所看到的……隨着作用域資源的數量不斷增加……這個類將變得非常臃腫且難以維護。
有沒有人知道以干凈和可維護的方式進行此范圍資源訪問的更好方法?
最干凈和可維護的解決方案是利用Spring Security AOP 來完成任務。
您可以使用@PreAuthorize
注釋,與您的PermissionManager
服務配對,以利用Spring Expression Language的強大功能在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
。
您可以在官方Spring文檔 中了解有關基於表達式的訪問控制的更多信息。
Workspace Entity 的一些數據庫查詢:
// 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());
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.