1

我当前的 Spring3 REST JSON api 使用默认的 InMemory 属性文件/基本身份验证身份验证管理器进行身份验证。到目前为止效果很好,但我需要进一步验证是否允许为该用户发出传入请求。Role 概念似乎可以很好地作为进入特定控制器 url 的网关,但它还不足以验证是否允许用户请求所请求的数据。

在我的应用程序中,将为每个将向 API 发出请求的 B2B 合作伙伴分配一个 applicationId。该合作伙伴用户帐户只允许对该 applicationId 进行请求。applicationId 作为所有 POST API 消息的 RequestBody POJO 的属性传递。我想拒绝针对不正确的 applicationId 提出的请求。

如何验证经过身份验证的用户正在发出允许的请求?

我已经开始创建自定义 AuthenticationProvider,但我不知道如何访问尚未编组到 java bean 中的 RequestBody bean 中的 applicationId。

也许自定义 AuthenticationProvider 不是正确的解决方案,需要某种请求验证器。如果是这样,appId 属性上的验证器将如何访问主体(经过身份验证的用户对象)

对于任何解决方案,我希望它对控制器是不可见的,以便确实到达控制器的请求是允许的。此外,理想情况下,解决方案不应依赖工程师记住一些注释来使逻辑工作。

提前致谢, JasonV

编辑 1:通过在控制器中实现 InitBinder,并在 RequestBody 上使用 @Valid 注释,我能够验证请求。但是,这不是我正在寻找的 Droids(呃我的意思是解决方案)。我需要找到一种更通用的方法来处理它,而不需要所有那些绑定器和注释;太多了,无法记住并在数十个请求控制器的应用程序中传播,并且将来会被遗忘。

4

2 回答 2

1

实现这一点的常用方法是使用@PreAuthorize。

@PreAuthorize("hasRole('USER') and authentication.principal.approvedAppId == #dto.applicationId")
@RequestMapping...
public ... someMethod(@RequestBody Dto dto, ...)

如果您担心 SpEL 的重复,请定义一个新注释,如 @PreAuthorizeUser 并将 @PreAuthorize 设置为它的元注释。

于 2013-10-29T23:49:54.530 回答
0

我能够利用一个方面来一般地解决问题。

我仍然想看看是否可以执行以下操作:在 AuthenticationProvider 的上下文中从请求对象获取编组的 RequestBody。

这是未来帮助他人的方面代码。

@Pointcut("within(@org.springframework.stereotype.Controller *)")
public void controllerBean() {
}

@Pointcut(
        "execution(org.springframework.http.ResponseEntity *(.., @org.springframework.web.bind.annotation.RequestBody (*),..))")
public void methodPointcut() {
}

@Around("controllerBean() && methodPointcut()")
public Object beforeMethodInControllerClass(ProceedingJoinPoint jp) throws Throwable {

        Object[] args = jp.getArgs();
        long requestAppId = Long.parseLong(BeanUtils.getProperty(args[0], "applicationId"));

        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        User principal = (User) auth.getPrincipal();
        String username = principal.getUsername();
        long[] approvedAppIds = getApprovedAppIdsForUsername(username);

        for (long approvedAppId : approvedAppIds) {
            if (approvedAppId == requestAppId) {
                isAllowedAccess = true;
                break;
            }
        }

        if (isAllowedAccess) {
            return jp.proceed(args);
        } else {
            LOGGER.warn("There was an attempt by a user to access an appId they are not approved to access: username="+username+", attempted appId="+requestAppId);
            return new ResponseEntity(HttpStatus.FORBIDDEN);
        }
}
于 2013-10-29T20:28:58.633 回答