Spring Boot rest -限制用户使用其自己数据的方法

vwhgwdsa  于 2023-01-26  发布在  Spring
关注(0)|答案(2)|浏览(215)

我遇到了一个困难,我需要将请求的范围限制为仅针对特定用户的数据。有了一个API,以前通过身份验证的用户可以检索数据列表,我首先需要验证请求的数据是否为请求者所有。
例如,ID为1的用户只能检索数据的子集,实际上,id为1的元素。使用以前经过身份验证的用户调用/api/elements/{elementId}时,应仅返回具有所提供的elementId的元素如果执行请求的用户是其所有者,或者如果执行请求的用户具有ADMIN角色(或任何其他授予的角色)。在任何其他情况下,应返回null404 HTTP状态。
研究了一段时间后,我有了这样的方法:

**在每个CRUD操作中使用用户ID:**此方法强制检查当前元素是否由执行请求的用户拥有。
**优点:**我可以保证只有该用户拥有的数据才会被读/写
**缺点:**我需要在所有查询中添加用户ID,并限制返回的数据,从这个条件中排除角色为ADMIN的用户,该用户可以不受任何限制地查询所有内容。

我使用Sping Boot 和Spring Security以及JWT进行身份验证/授权,一切都很好,但是有了这个限制,我开始考虑用某种框架解决方案(如果可能的话)来代替上面提到的方法。
在这些条件下,你将如何处理这个问题来解决这个限制?
与此问题相关但没有针对我的案例的具体解决方案的链接:

先谢谢你!

wvmv3b1j

wvmv3b1j1#

经过几次尝试,并在@alan-hay的帮助下,我决定采用一种变通方法,即在访问特定资源之前,检查用户(已经过身份验证)是否有足够的权限访问该资源。
因此我创建了一个名为AuthorizationService的服务类,如下所示:

@Component
public class AuthorizationService {
    @Autowired
    private AuthenticationUser authenticationUser;

    public boolean hasAccess(Long elementId, AccessValidatorBo accessValidatorBo) {
        return accessValidatorBo.hasAccess(elementId, authenticationUser.getAuthenticatedUser());
    }
}

其中elementId是尝试访问的元素的ID,accessValidatorBo是执行逻辑以确保当前用户是否有权访问前面提到的资源的业务对象的 Package 器,authenticationUser是Spring安全验证用户。
因此,在控制器中,我添加了一个@PreAuthorize注解,以便首先验证请求访问资源的用户是否被允许这样做。

@RestController
@RequestMapping(path = ScriptUrlDefinition.BASE)
public class ScriptController {
    @Autowired
    private ScriptBo scriptBo;
    @Autowired
    private AuthenticationUser authenticationUser;

    @GetMapping(path = ScriptUrlDefinition.GET)
    @PreAuthorize("(" + ServiceSecurityConstants.HAS_ROLE_ADMIN + " or " + ServiceSecurityConstants.HAS_ROLE_STANDARD + ") "
            + "and @authorizationService.hasAccess(#scriptId, @scriptBoImpl)")
    @ResponseBody
    public ViewScript get(@PathVariable(name = ScriptUrlDefinition.PATH_VARIABLE_ID) Long scriptId) {
        return scriptBo.getScript(scriptId);
    }
}

如果用户具有访问权限,则服务返回响应;否则,@PreAuthorize注解将引发org.springframework.security.access.AccessDeniedException异常

q3qa4bjr

q3qa4bjr2#

如果请求包含识别信息(如用户名),那么这可以用一种更简单的方式解决。请看这个stackoverflow answer。我粘贴的信息和文档如下。
您还可以使用Spring Security的@PreAuthorize来完成此操作,它支持以下表达式:

@PreAuthorize("#userId == principal.id")
public void doSomething(@PathVariable String userId);

参见Spring文档:
使用@PreAuthorize和@PostAuthorize进行访问控制
如果方法的至少一个参数上存在Spring Data的@Param注解,则将使用该值。这对于使用JDK 8之前的JDK编译的接口非常有用,因为这些接口不包含有关参数名称的任何信息。例如:

@PreAuthorize("#n == authentication.name")
Contact findContactByName(@Param("n") String name);

在幕后,使用AnnotationParameterNameDiscoverer实现了这种用法,可以定制AnnotationParameterNameDiscoverer以支持任何指定注解的value属性。
在表达式中可以使用任何Spring-EL功能,因此您也可以访问参数的属性,例如,如果您希望某个特定方法只允许访问用户名与联系人用户名匹配的用户,则可以编写

@PreAuthorize("#contact.name == authentication.name")
public void doSomething(Contact contact);

这里我们访问另一个内置表达式authentication,它是存储在安全上下文中的Authentication。您还可以使用表达式principal直接访问其“principal”属性。该值通常是UserDetails示例,因此您可以使用principal.username或principal. enabled这样的表达式。

相关问题