简体   繁体   English

在Spring中保护REST资源的更好方法?

[英]Better way to secure REST resources in Spring?

I have a RESTful service that exposes resources like /user/{id} 我有一个RESTful服务,可提供/user/{id}

Now, the user can provide the credentials, get the token and access the resource. 现在,用户可以提供凭据,获取令牌并访问资源。 However, once authenticated, the user can access the resources for any id . 但是,通过身份验证后,用户可以访问任何id的资源。

Meaning, user1 can access the URIs like /user/1 as well as user/2 and so on. 这意味着,user1可以访问URI,例如/user/1以及user/2 ,等等。 I ended up using a Principal in the controller methods and started checking the id of the Principal with the id the user is trying to access. 最后我用一个Principal控制器的方法和开始检查id的的Principalid用户试图访问。

Further, the user has multiple resources associated with it. 此外,用户具有与其关联的多个资源。 Say, user1 owns res1 and res2, user2 owns res3 and res4. 假设用户1拥有r​​es1和res2,用户2拥有res3和res4。 These can be accessed via /user/1/res/2 . 这些可以通过/user/1/res/2 I need a way where I can prevent /user/1/res/3 as res3 is owned by user1 and not user2. 我需要一种可以阻止/user/1/res/3因为res3属于user1而不是user2。

But I believe that this problem is very common and I am not really convinced with my solution. 但是我相信这个问题非常普遍,我对解决方案并不十分信服。

Is there a better way to deal with this problem? 有没有更好的方法来解决此问题?

Thanks 谢谢

You should not be exposing resourse /user/{id} at all if you all user can do is access only their own ID. 如果您所有用户只能访问自己的ID,则根本不应该公开资源/user/{id} If I understand correctly, just exposing /user is enough, find ID of user from Principal or session etc and return result. 如果我理解正确,仅暴露/user就足够了,从Principal或session等中找到用户的ID并返回结果。

If you really want to do it, you can have custom implementation of @PreAuthorize. 如果您确实想这样做,可以使用@PreAuthorize的自定义实现。 Got this code from a blog. 从博客获得了此代码。

@PreAuthorize("isUsersRes(#id)")
@RequestMapping(method = RequestMethod.GET, value = "/users/{id}")
public UsersfindById(@PathVariable long id) {
return Users.findOne(id);
}

public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations { 公共类CustomMethodSecurityExpressionRoot扩展了SecurityExpressionRoot,实现了MethodSecurityExpressionOperations {

public CustomMethodSecurityExpressionRoot(Authentication authentication) {
    super(authentication);
}

And implemenation of isUsersRes 和isUsersRes的实现

public class CustomMethodSecurityExpressionRoot 
extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
public boolean isMember(Long OrganizationId) {
 //user logic
}

Check complete blog here 在此处查看完整的博客

This is a common problem with varied solutions. 这是各种解决方案的普遍问题。 Also its not a problem related to REST alone. 同样,这不是与REST相关的问题。 We have had this ever since apps exist. 自从存在应用程序以来,我们就拥有了这一点。 Employee can see his salary slip, leave records, etc but not another employee's. 员工可以看到他的薪水单,请假记录等,但不能看到另一个员工的。 One solution I like the most is 'security in depth'. 我最喜欢的一种解决方案是“深度安全”。 This idea comes from how I have seen this work in banking systems for decades. 这个想法来自我几十年来在银行系统中的工作方式。 This needs to get supported in the DB layer first. 这首先需要在数据库层得到支持。 You would need a table design like this example (or whatever your app's entity hierarchical structure is): 您将需要像此示例一样的表设计(或任何应用程序的实体层次结构):

Organisation
-Dept
--user

And all non-master tables need to have a relation to one of these entities. 并且所有非主表都需要与这些实体之一相关联。 Example: 例:

Payslip -> user
Leave record -> user
Manager -> dept
HR Manager -> org

etc... 等等...

You would need another table to map out the basic access levels (This can get complex if we need to implement different sub access levels) 您将需要另一个表来映射基本访问级别(如果我们需要实现不同的子访问级别,这可能会变得很复杂)

user1:dept2:org1
user2:dept2:org1

(I have seen some implementations that send this table's info as part of an encrypted access token that is used on every access request if the access has to be sessionless.) (我已经看到一些实现,该实现将表的信息作为加密访问令牌的一部分发送,如果访问必须是无会话的,则该加密访问令牌将在每个访问请求上使用。)

You have not mentioned a framework/language but most languages have a database layer. 您没有提到框架/语言,但是大多数语言都有数据库层。 For example if the DB layer is hibernate-java. 例如,如果数据库层是hibernate-java。 There are interceptors ( https://docs.jboss.org/hibernate/core/3.6/javadocs/org/hibernate/Interceptor.html#onPrepareStatement(java.lang.String) ) that can be used to modify the query thats being executed. 有一些拦截器( https://docs.jboss.org/hibernate/core/3.6/javadocs/org/hibernate/Interceptor.html#onPrepareStatement(java.lang.String) )可用于修改正在执行的查询。 Every query to DB will go with additional where-clause for these relationship keys. 对数据库的每个查询都将为这些关系键附加附加的where-子句。 We can get clever with Spring AOP, REST interceptors and a lot of other techniques on top of this basic architecture to enforce this security. 在此基本体系结构之上,我们可以使用Spring AOP,REST拦截器和许多其他技术来提高安全性,从而变得更聪明。 Idea will be that DB layer does not return data thats not accessible to the logged in user principal irrespective of what queries higher layer code uses. 想法是,无论高层代码使用什么查询,DB层都不会返回登录用户主体不可访问的数据。 if this is in place, a REST GET call for 如果到位,则进行REST GET调用

/payslip/user1/Jan-2017 /工资单/ USER1 /月,2017年

will end up with a 404 and not a 403. Expecting this to be solved by a framework or a superficial set of interceptors is both risky and not future proof. 将以404而不是403结尾。期望通过框架或表面拦截器集来解决此问题既有风险,也不是未来的证明。 We end up continuously tweaking the interceptors as the url patterns evolve. 随着URL模式的演变,我们最终不断调整拦截器。

Addition to show table examples: 另外显示表示例:

ACL table
user, uid, dept, org
--------------------
jhon, 1  , 1   , 1
mary, 2  , 2   , 1
will, 3  , 2   , 1

Payslip table
--------------
month, net, deductions,..., uid
-------------------------------------
Jan  , 200, 15.5      ,..., 3  
Feb  , 200, 15.5      ,..., 3

Project table
-------------
pname, pstart, pbudget, dept
------------------------------------
mark1, 12/21 , 20000  , 2
markx, 12/31 , 40000  , 2

What you want is user roles and permissions + cross user control. 您需要的是用户角色和权限+跨用户控制。 To find out user roles and permissions refer this 要了解用户角色和权限,请参阅

Also additionally you may want to cross check their user ID to the resource ID. 另外,您可能还想将其用户ID交叉检查到资源ID。 Since you cannot let user1's resource ID 1 to view by user2, you will need to add userID as part of the resource id ex:- /user/user_id_1. 由于您不能让user2查看用户1的资源ID 1,因此您需要将用户ID添加为资源ID的一部分,例如:-/ user / user_id_1。 Otherwise we don't have a logical way to separate which resources are applicable to which users. 否则,我们没有逻辑方法来区分哪些资源适用于哪些用户。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM