关于 Spring Data JPA Rest (JpaRepository) 的安全问题
Security questions on Spring Data JPA Rest (JpaRepository)
我(尝试 :) 在我的 spring 启动应用程序中使用 spring-boot-starter-data-rest 来快速服务通过 true、fullblown、restFULL api 建模。效果很好。
问题 1(安全):
SpringJpaRepository 的优点是我不需要编写基本功能(save、findAll 等)的代码。是否有可能在不覆盖所有这些方法的情况下保护这些自动实现的方法(浪费 Spring 为我提供的东西)?即:
public interface BookRepository extends JpaRepository<Book, Long> {
@PreAuthorize("hasRole('ROLE_ADMIN')")
<S extends Book> Book save(Book book);
}
.
问题 2(安全):
如何保护 JpaRepository 以防止更新登录用户不是所有者的项目?
即:用户只能修改 his/her 自己的属性。
即 2:用户只能 modify/delete 创建的帖子 he/she。
此处非常欢迎示例代码。
.
问题3(DTO):
前段时间我和一位开发者朋友发生争执:他坚持认为必须有从 Spring MVC 控制器返回的 DTO。即使 DTO 是模型对象的 1-1 副本。然后我研究了,问了其他人并确认:divide/segregate 应用程序层需要 DTO。
这与 JpaRepositories 有什么关系?如何将 DTO 与 Spring auto serverd rest repos 一起使用?我应该 DTO 吗?
提前感谢您的hints/answers!
问题 1:安全性
一些old docs提及:
[...] you expose a pre-defined set of operations to clients that are not under you control, it’s pretty much all or nothing until now. There’s seemingly no way to only expose read operations while hiding state changing operations entirely.
这意味着所有方法都是自动继承的(同样,根据标准 java
继承行为)。
根据 @PreAuhtorize 文档,您也可以将注释放在 class / 接口声明上。
所以你可以只扩展一个基本接口 JpaRepository
@NoRepositoryBean // tell Spring not create instances of this one
@PreAuthorize("hasRole('ROLE_ADMIN')") // all methods will inherit this behavior
interface BaseRepository<T, ID extends Serializable> extends Repository<T, ID> {}
然后让你所有的 Repository
扩展 BaseRepository
。
问题 2:安全性
我将在这方面更笼统一些。
为了在您的应用程序中正确控制对实体的访问并定义可以看到什么,您应该始终将您的项目分成不同的层。
一个好的起点是:
layer-web
(或表示层):访问layer-business
,无法访问db-layer
。可以看到 DTO
模型但不能看到 DB models
layer-business
(或业务层):访问db-layer
但无法访问DAO
layer-db
(或数据层):转换DTO -> DB model
。持久化对象并提供查询结果
对于您的情况,我认为正确的做法是在请求到达 Repository
class 之前检查 layer-business
中的角色。
@Service
public interface BookService {
@PreAuthorize("hasRole('ROLE_ADMIN')")
ActionResult saveToDatabase(final BookDTO book);
}
或者,如前所述
@Service
@PreAuthorize("hasRole('ROLE_ADMIN')")
public interface BookService {
ActionResult saveToDatabase(final BookDTO book);
}
此外,可以通过多种方式确保用户只能修改自己的对象。
正如 this answer 指出的那样,Spring
提供了所有必要的资源。
或者,如果您熟悉 AOP
,您可以实现自己的逻辑。
例如 (dummyCode):
@Service
public interface BookService {
// custom annotation here
@RequireUserOwnership(allowAdmin = false)
ActionResult saveToDatabase(final BookDTO book);
}
支票:
public class EnsureUserOwnershipInterceptor implements MethodInterceptor {
@Autowired
private AuthenticationService authenticationService;
@Override
public Object invoke(Invocation invocation) throws Throwable {
// 1. get the BookDTO argument from the invocation
// 2. get the current user from the auth service
// 3. ensure the owner ID and the current user ID match
// ...
}
}
可以找到关于 AOP
的有用资源 here and here。
问题 3:DTO
和 DB models
Should I DTOs at all?
是的,是的,你应该。即使您的项目只有几个模型并且您只是为了好玩而编程(仅在本地主机上部署,学习,...)。
越早养成分离模型的习惯越好。
此外,从概念上讲,一个是来自未知来源的对象,另一个代表您数据库中的 table。
How this relates to JpaRepositories?
How to use DTOs with Spring auto serverd rest repos?
这就是重点!您不能将DTO
放入@Repository
。您被迫将一个转换为另一个。同时你也被强制验证转换是否有效。
您基本上确保 DTO
s(脏数据)不会以任何方式接触数据库,并且您在数据库和应用程序的其余部分之间设置了由逻辑约束构成的墙。
我还知道 Spring
与 model-conversion frameworks 集成得很好。
那么,multi-layer
/ modular
网络应用程序的优点是什么?
应用程序可以增长得非常快。特别是当您有许多开发人员在处理它时。一些开发人员倾向于寻找最快的解决方案并实施肮脏的技巧或更改访问修饰符以尽快完成工作。您应该强制人们只能通过一些明确定义的渠道访问某些资源。
您从一开始就设置的规则越多,遵循正确的编程模式的时间就越长。我已经看到银行申请在不到一年后变得一团糟。当需要 hotfix
时,更改一些代码会产生两三个其他 错误 。
您可能会遇到应用程序消耗过多 OS 资源的情况。比方说,如果您的应用程序有一个包含后台作业的模块 module-batch
,那么提取它并将其实施到另一个应用程序中会容易得多。如果您的模块包含查询数据库、访问任何类型的数据、为前端提供 API 的逻辑,ecc...您基本上将被迫将所有代码导出到新应用程序中。到那时,重构将是一件令人头疼的事情。
假设您想聘请一些数据库专家来分析您的应用程序执行的查询。通过明确定义和分离的逻辑,您可以让他们只访问必要的 modules
而不是整个应用程序。这同样适用于前端自由职业者ecc ...我也遇到过这种情况。该公司希望数据库专家修复应用程序执行的查询,但不希望他们能够访问整个代码。最后,他们放弃了数据库优化,因为这会暴露太多的外部敏感信息。
DTO
/DB model
分离有什么好处?
DTO
不会接触数据库。这使您更安全地抵御来自外部的攻击
- 你可以决定另一边的内容。您的
DTO
不需要将所有字段都实现为数据库模型。实际上,您甚至可以将 DAO
映射到许多 DTO
,反之亦然。有很多信息不应该到达前端,使用 DTO
可以轻松做到这一点。
DTO
一般都是升比@Entity
的机型。尽管实体被映射(例如 @OneToMany
)到其他实体,但 DTO
可能只包含映射对象的 id 字段。
- 您不希望数据库对象闲置太久;也不会被您的应用程序的方法传递。许多框架在每个方法结束时提交数据库事务,这意味着对数据库实体所做的任何非自愿更改都可能提交到数据库中。
就我个人而言,我认为任何受人尊敬的网络应用程序都应该严格分离层,每个层都有自己的职责并且对其他层的可见性有限。
区分数据库模型和数据传输对象也是一个很好的模式。
最后这只是我的意见;许多人认为 DTO
模式已经过时并导致不必要的代码重复 许多人认为过多的分离会导致难以维护代码。因此,您应该始终咨询不同的来源,然后应用最适合您的方法。
也很有趣:
- SE: What is the point of using DTO (Data Transfer Objects)?
- Lessons Learned: Don't Expose EF Entities to the Client Directly
- Guice Tutorial – method interception(老而金)
- SO: Large Enterprise Java Application - Modularization
- Microsoft Docs: Layered Application Guidelines
- The 5-layer architecture
我(尝试 :) 在我的 spring 启动应用程序中使用 spring-boot-starter-data-rest 来快速服务通过 true、fullblown、restFULL api 建模。效果很好。
问题 1(安全):
SpringJpaRepository 的优点是我不需要编写基本功能(save、findAll 等)的代码。是否有可能在不覆盖所有这些方法的情况下保护这些自动实现的方法(浪费 Spring 为我提供的东西)?即:
public interface BookRepository extends JpaRepository<Book, Long> {
@PreAuthorize("hasRole('ROLE_ADMIN')")
<S extends Book> Book save(Book book);
}
.
问题 2(安全):
如何保护 JpaRepository 以防止更新登录用户不是所有者的项目? 即:用户只能修改 his/her 自己的属性。 即 2:用户只能 modify/delete 创建的帖子 he/she。 此处非常欢迎示例代码。
.
问题3(DTO):
前段时间我和一位开发者朋友发生争执:他坚持认为必须有从 Spring MVC 控制器返回的 DTO。即使 DTO 是模型对象的 1-1 副本。然后我研究了,问了其他人并确认:divide/segregate 应用程序层需要 DTO。
这与 JpaRepositories 有什么关系?如何将 DTO 与 Spring auto serverd rest repos 一起使用?我应该 DTO 吗?
提前感谢您的hints/answers!
问题 1:安全性
一些old docs提及:
[...] you expose a pre-defined set of operations to clients that are not under you control, it’s pretty much all or nothing until now. There’s seemingly no way to only expose read operations while hiding state changing operations entirely.
这意味着所有方法都是自动继承的(同样,根据标准 java
继承行为)。
根据 @PreAuhtorize 文档,您也可以将注释放在 class / 接口声明上。
所以你可以只扩展一个基本接口 JpaRepository
@NoRepositoryBean // tell Spring not create instances of this one
@PreAuthorize("hasRole('ROLE_ADMIN')") // all methods will inherit this behavior
interface BaseRepository<T, ID extends Serializable> extends Repository<T, ID> {}
然后让你所有的 Repository
扩展 BaseRepository
。
问题 2:安全性
我将在这方面更笼统一些。
为了在您的应用程序中正确控制对实体的访问并定义可以看到什么,您应该始终将您的项目分成不同的层。
一个好的起点是:
layer-web
(或表示层):访问layer-business
,无法访问db-layer
。可以看到DTO
模型但不能看到DB models
layer-business
(或业务层):访问db-layer
但无法访问DAO
layer-db
(或数据层):转换DTO -> DB model
。持久化对象并提供查询结果
对于您的情况,我认为正确的做法是在请求到达 Repository
class 之前检查 layer-business
中的角色。
@Service
public interface BookService {
@PreAuthorize("hasRole('ROLE_ADMIN')")
ActionResult saveToDatabase(final BookDTO book);
}
或者,如前所述
@Service
@PreAuthorize("hasRole('ROLE_ADMIN')")
public interface BookService {
ActionResult saveToDatabase(final BookDTO book);
}
此外,可以通过多种方式确保用户只能修改自己的对象。
正如 this answer 指出的那样,Spring
提供了所有必要的资源。
或者,如果您熟悉 AOP
,您可以实现自己的逻辑。
例如 (dummyCode):
@Service
public interface BookService {
// custom annotation here
@RequireUserOwnership(allowAdmin = false)
ActionResult saveToDatabase(final BookDTO book);
}
支票:
public class EnsureUserOwnershipInterceptor implements MethodInterceptor {
@Autowired
private AuthenticationService authenticationService;
@Override
public Object invoke(Invocation invocation) throws Throwable {
// 1. get the BookDTO argument from the invocation
// 2. get the current user from the auth service
// 3. ensure the owner ID and the current user ID match
// ...
}
}
可以找到关于 AOP
的有用资源 here and here。
问题 3:DTO
和 DB models
Should I DTOs at all?
是的,是的,你应该。即使您的项目只有几个模型并且您只是为了好玩而编程(仅在本地主机上部署,学习,...)。
越早养成分离模型的习惯越好。
此外,从概念上讲,一个是来自未知来源的对象,另一个代表您数据库中的 table。
How this relates to JpaRepositories? How to use DTOs with Spring auto serverd rest repos?
这就是重点!您不能将DTO
放入@Repository
。您被迫将一个转换为另一个。同时你也被强制验证转换是否有效。
您基本上确保 DTO
s(脏数据)不会以任何方式接触数据库,并且您在数据库和应用程序的其余部分之间设置了由逻辑约束构成的墙。
我还知道 Spring
与 model-conversion frameworks 集成得很好。
那么,multi-layer
/ modular
网络应用程序的优点是什么?
应用程序可以增长得非常快。特别是当您有许多开发人员在处理它时。一些开发人员倾向于寻找最快的解决方案并实施肮脏的技巧或更改访问修饰符以尽快完成工作。您应该强制人们只能通过一些明确定义的渠道访问某些资源。 您从一开始就设置的规则越多,遵循正确的编程模式的时间就越长。我已经看到银行申请在不到一年后变得一团糟。当需要
hotfix
时,更改一些代码会产生两三个其他 错误 。您可能会遇到应用程序消耗过多 OS 资源的情况。比方说,如果您的应用程序有一个包含后台作业的模块
module-batch
,那么提取它并将其实施到另一个应用程序中会容易得多。如果您的模块包含查询数据库、访问任何类型的数据、为前端提供 API 的逻辑,ecc...您基本上将被迫将所有代码导出到新应用程序中。到那时,重构将是一件令人头疼的事情。假设您想聘请一些数据库专家来分析您的应用程序执行的查询。通过明确定义和分离的逻辑,您可以让他们只访问必要的
modules
而不是整个应用程序。这同样适用于前端自由职业者ecc ...我也遇到过这种情况。该公司希望数据库专家修复应用程序执行的查询,但不希望他们能够访问整个代码。最后,他们放弃了数据库优化,因为这会暴露太多的外部敏感信息。
DTO
/DB model
分离有什么好处?
DTO
不会接触数据库。这使您更安全地抵御来自外部的攻击- 你可以决定另一边的内容。您的
DTO
不需要将所有字段都实现为数据库模型。实际上,您甚至可以将DAO
映射到许多DTO
,反之亦然。有很多信息不应该到达前端,使用DTO
可以轻松做到这一点。 DTO
一般都是升比@Entity
的机型。尽管实体被映射(例如@OneToMany
)到其他实体,但DTO
可能只包含映射对象的 id 字段。- 您不希望数据库对象闲置太久;也不会被您的应用程序的方法传递。许多框架在每个方法结束时提交数据库事务,这意味着对数据库实体所做的任何非自愿更改都可能提交到数据库中。
就我个人而言,我认为任何受人尊敬的网络应用程序都应该严格分离层,每个层都有自己的职责并且对其他层的可见性有限。
区分数据库模型和数据传输对象也是一个很好的模式。
最后这只是我的意见;许多人认为 DTO
模式已经过时并导致不必要的代码重复 许多人认为过多的分离会导致难以维护代码。因此,您应该始终咨询不同的来源,然后应用最适合您的方法。
也很有趣:
- SE: What is the point of using DTO (Data Transfer Objects)?
- Lessons Learned: Don't Expose EF Entities to the Client Directly
- Guice Tutorial – method interception(老而金)
- SO: Large Enterprise Java Application - Modularization
- Microsoft Docs: Layered Application Guidelines
- The 5-layer architecture