spring引导-确保数据属于当前登录的用户

kx5bkwkv  于 2021-09-30  发布在  Java
关注(0)|答案(2)|浏览(276)

我正在构建一个SpringBootRESTAPI。我有点拘泥于正确的方式来设计我的api,以保护每个用户的数据。例如,考虑以下数据库关系:
用户->(有许多)项目->(有许多)任务(一个用户有许多项目,一个项目有许多任务)。
例如,如果我按以下方式设计端点:

GET /api/v1/projects/{projectId}

POST /api/v1/projects/{projectId}/tasks

正如上面的一个简单示例,在为某个项目创建新任务时,如何确保该项目属于登录用户?
目前,我通过spring security使用jwt令牌作为身份验证策略,并将其包含在令牌的有效负载中,我有我的用户id。因此,每次请求我都可以检索用户,但向数据库发出这么多请求并检查用户是否真的有给定的项目,这肯定是非常低效的。
我所考虑的一些解决方案是简单地将端点设计成这样:

/api/v1/users/{userId}/projects/{projectId}/tasks

然后我可以在jwt负载中使用用户id,并将其与请求参数中的用户id进行比较。但这意味着我的数据库中的每一个新关系,url的长度都将是巨大的:)我猜这也意味着所有的业务逻辑都将在整个应用程序的用户服务中,对吗?这对我来说有点奇怪。。。但也许我错了。
我不确定这是否是一个问题,但我只是试图将api设计得尽可能优雅。
再次感谢!

qvk1mo1f

qvk1mo1f1#

检查用户是否对每个请求都有项目权限是正确的解决方案。考虑许多其他应用程序/用户正在调用API时的情况。您希望确保您的api尽可能安全,并且不能从前端进行操作。
为了提高效率,您应该有一种方法/查询来检查数据库中的关联,就像一个返回true/false的简单查询,它应该比检索所有数据和在java代码中进行比较更快。
在可能的情况下,将多个数据库查询合并为一个查询,如您的一个示例所示: GET /api/v1/projects/{projectId} 在这种情况下,不要运行查询来获取用户信息和请求项目的查询。相反,您可以使用用户表和项目表之间的联接执行单个查询,该联接只应在用户与项目关联时返回项目。最好的方法实际上取决于数据库的结构。
将用户id添加到api url中只是冗余信息。令牌中的用户id与url中的用户id匹配并不意味着用户对任何项目都有任何类型的权限。
另一个需要避免的解决方案是将用户的项目ID包含在jwt令牌中,然后无需进行数据库请求即可进行比较。这是不好的,原因有几个:
令牌应该只有用户访问api所需的信息,而不应该有业务逻辑
根据您在令牌中存储了多少业务逻辑,令牌的大小可能会变大。有关大小限制的讨论,请参阅本文:jwt令牌的最大大小是多少?
如果用户以外的其他人(如管理员)可以通过某种方式添加/删除用户与项目的关联,则在刷新令牌的数据之前,该更改不会反映在令牌中
编辑:
在Spring方面,我使用了 @PreAuthorize 注解来处理这些类型的方法检查。下面以伪代码为例。

@RestController
public class MyController {

    @PostMapping
    @PreAuthorize("@mySecurityService.isAllowed(principal, #in)")
    public SomeResponseType api1(SomeRequestType requestData) {
        /* this is not reached unless mySecurityService.isAllowed
        returns true, instead a user gets a 401/403 HTTP response 
        code (i don't remember the exact one) */
    }
}

@Service
public class MySecurityService {

    /*
    Object principal - this is spring's UserDetails object that is
    returned from the AuthenticationProvider. So basically a Java
    representation of the  JWT token which should have the 
    user's username.

    SomeRequestType requestData - this is the request data that was 
    sent to the API. I'm sure there is a way to get the project ID 
    from the URL here as well.
    */
    public boolean isAllowed(Object principal, SomeRequestType requestData) {
        /*
        take the user's username from the principal, take the 
        project ID from the request data and query the database 
        to check authorization, return true if authorized

        make this check efficient
        */

        return false;
    }
}

然后可以将注解和安全服务应用于多个方法。根据您要检查的内容,您可以有许多不同的安全服务。
还有其他可用的方法https://www.baeldung.com/spring-security-method-security 这必须在spring的配置中启用(也在链接中解释)。

3bygqnnd

3bygqnnd2#

您好,如果我理解正确,您希望自动将使用“post/api/v1/projects/{projectd}/tasks”创建的任务分配给当前登录的用户。
您可以尝试向rest控制器添加参数“principal”。主体是发送请求的用户。
获得prinicipal后,可以编写一个简单的convert方法(例如:convertprincipaltouser(principal principal))返回用户。最后,您可以将用户添加到相应的任务中)
以下是关于它的更多信息:https://www.baeldung.com/get-user-in-spring-security

相关问题