有很多方法可以解决这个问题,但是我选择了三种方法来解决这个问题。
自定义安全表达式
我建议使用自定义的基于安全性的注释方法。这将涉及实现自定义安全表达式、相关的表达式处理程序和方法安全配置。如果这对您来说工作量太大,那么下一种方法就简单一点。
public class UserIdentityMethodSecurityExpressionRoot
extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
public UserIdentityMethodSecurityExpressionRoot(Authentication authentication) {
super(authentication);
}
public boolean userIdentity(Long userId) {
User user = ((UserPrincipal) this.getPrincipal()).getUser();
return user.getId() == userId;
}
}
然后,可以使用新创建的安全表达式对Rest端点或服务方法进行注释:
@PreAuthorize("userIdentity(#userId)")
@GetMapping
@ResponseBody
public Resource fineOne(@PathVariable Long userId) {
return resourceService.findOne(id);
}
请注意
userId
必须提供
在某处
@PathVariable
或
@RequestParam
用户ID
403
否则
here
并且在这个问题中已经根据您的目的进行了修改。
斯佩尔
您还可以使用SpEL,它稍微简单一点:
@PreAuthorize("#userId == principal.getId()")
@GetMapping
@ResponseBody
public Resource fineOne(@PathVariable Long userId) {
return resourceService.findOne(id);
}
其他考虑
您还可以自己完成所有工作并获得更快的结果,而无需使用定义自定义表达式
SecurityContextHolder
public static void checkUserIdentity(Long userId) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
// user did not provide a token
if(auth == null) {
throw new AccessDeniedException();
}
UserDetails details = (UserDetails) auth.getPrincipal();
if(userId != details.getId()) {
throw new AccessDeniedException();
}
}
并且像这样使用它:
@GetMapping
@ResponseBody
public Resource fineOne(@PathVariable Long userId) {
SecurityUtils.checkUserIdentity(userId)
return resourceService.findOne(id);
}
为什么这样做有效?这个
SecurityContextHolder