☰
Current Page
Main Menu
Home
Home
Editing
对象存储权限设计方案
Edit
Preview
h1
h2
h3
default
Set your preferred keybinding
default
vim
emacs
markdown
Set this page's format to
AsciiDoc
Creole
Markdown
MediaWiki
Org-mode
Plain Text
RDoc
Textile
Rendering unavailable for
BibTeX
Pod
reStructuredText
Help 1
Help 1
Help 1
Help 2
Help 3
Help 4
Help 5
Help 6
Help 7
Help 8
Autosaved text is available. Click the button to restore it.
Restore Text
### 业务和公共分桶方案 #### 现状分析 关于 minio 对象存储现在是统一在 application-dev.properties 中配置: ```properties minio.endpoint=http://192.168.175.54:9000 minio.accessKey=snest minio.secretKey=12345678 minio.bucketName=apps minio.publicBucket=public-bucket # 没用 # minio.region = oss-cn-hangzhou ``` 调用 `meta_attachment` 元模型的 `upload` 方法上传文件,所有的业务都统一使用同一个桶apps. 所有的业务都共享同一个桶,导致同一个桶中存储的文件,无法区分是哪个业务上传的。 #### 需求分析 目前了解到的需求是: 为了安全考虑不让公网直接访问minio,我们让他们开放,如果他们开放了就会所有人都能直接访问到这个桶,那业务附件也被访问到了,这明显存在安全问题。 所以要把系统可以公开的公共资源放入到公共桶,可以外部访问,私有桶只能程序通过id、secret访问。 MinIO 的权限主要在 **存储桶(Bucket)级别** 和 **对象(Object)级别** 进行设置。 1. **桶策略 (Bucket Policy)**:这是最主要的方式。你可以为整个桶或桶内特定目录前缀(Prefix)设置详细的访问规则(Allow/Deny),规定谁(Principal)能对哪些资源(Resource)执行什么操作(Action,如 `s3:GetObject`)。 2. **访问控制列表 (ACL)**:一种相对传统的权限控制方式,可以为匿名用户或特定用户设置路径级权限。 3. **预签名 URL (Presigned URL)**:这是访问私有资源最安全的方式。后端应用使用自己的 Access Key 和 Secret Key 生成一个**有时效性的临时网址**,分享给前端。在有效期内,即使是没有凭证的用户也能通过此链接访问或上传特定对象,过期后自动失效。 以下是三种常见的桶权限级别对比: | 权限类型 | 描述 | 适用场景 | 风险等级 | |:---------------| :--- | :--- | :--- | | **`private`** | **完全私有**,需签名 URL | 敏感数据、业务附件 | **低** | | **`download`** | **只读公开**,允许列出和下载文件 | 公开文档、静态资源 | **中** | | **`public`** | **完全公开**,允许读写删 | 公共上传区(**谨慎使用**) | **高** | 对应的方案是:**创建一个 `public` (或 `download`) 桶存放公共资源,一个 `private` 桶存放业务附件。** ### 目前实施方案 目前实施的方案是为平台新增一个公有桶,平台业务涉及到的对象需存储到公有桶中,其他非业务依然沿用原有桶。 但是需要引擎和平台app做相应的调整。 - 1. 新增两个配置项,分别是后端配置和前端配置; ```properties # 后端配置项 minio.bucket.edition=v2 minio.iidpBucket=iidp-bucket # 前端配置项 bucketType = public/private ``` - 2. 为什么要有两个配置项 - 2.1. 后端配置功能开关,是为了兼容已有的系统,如果没有配置 `minio.bucket.edition=v2` 则所有逻辑都保持原来一致,如果有配置则按照新逻辑执行; - 2.2. 前端配置是为了给业务一个选项,因为暴露给前端的接口可能由平台调用也可能由业务调用,所以需要传递一个标识来表明是否需要存储到共有桶,默认行为与原有逻辑保持一致。 - 3. 调整 - 3.1. 引擎在 `/file/upload` 接口需要处理两个配置项,即前端和后端,只有两个配置项都满足的情况下才执行新逻辑,否则依然是原来逻辑。 - 3.2. 类似地,平台app 也需要处理两个配置项,但是如果只有一个配置项那就只需要处理一个,比如不需要前端页面来上传。 - 4. 样板代码 ```java // 获取 minioTemplate 实例 MinioTemplate minioTemplate = MinioTemplate.getInstance(); // 获取业务所需的桶,有两个选择,如果是非平台的业务则用 getBucket,如果是平台业务则调用 getIIDPBucket String minioBucketName = minioTemplate.getBucket(); String minioBucketName = minioTemplate.getIIDPBucket(); // 拿到 instance 和 bucket 去使用minio 接口 String objectName = minioTemplate.putObject(byteArrayInputStream, contentType, originalFilename, bucket); // 其他接口等 // 同时支持获取预签名的url。 presignedGetObjectUrl 签名是的Get方法的url,类似地有 presignedPostObjectUrl String url = minioTemplate.presignedGetObjectUrl("apps-bucket", "test.json"); System.out.println(url); // 也可以自行指定具体的method,比如 HttpMethod.PUT,HttpMethod.GET 等,根据业务场景来获取指定的方法权限 url = minioTemplate.presignedObjectUrl(HttpMethod.PUT, "apps-bucket", "test.json"); System.out.println(url); ``` --- ### ⚙️ 二、方案实施详细步骤 #### 步骤 1: 规划与创建存储桶 1. **创建两个桶**: * `public-bucket`: 用于存放可以公开访问的静态资源,如网站图片、CSS、JS、公共文档等。 * `private-bucket`: 用于存放所有业务附件、用户上传的私人文件等敏感数据。 2. **使用 `mc` 命令行工具创建桶**: ```bash # 设置 MinIO 服务器别名(以本地为例) mc alias set myminio http://your-minio-server:9000 your-admin-user your-admin-password # 创建两个桶 mc mb myminio/public-bucket mc mb myminio/private-bucket ``` #### 步骤 2: 配置桶权限策略 1. **将公共桶设置为 `download` (只读) 权限**: ```bash # 设置整个 public-bucket 为只读(允许匿名下载) mc anonymous set download myminio/public-bucket ``` 这样,任何人都可以通过直接的 URL(如 `http://your-minio-server:9000/public-bucket/image.jpg`)访问这个桶里的对象,但**不能上传、修改或删除**。 2. **确保私有桶权限为 `private`**: ```bash # 确保 private-bucket 是私有状态(默认通常是 private) mc anonymous set none myminio/private-bucket ``` 此举确保了**任何未经认证的请求(包括直接通过浏览器访问对象URL)都会被拒绝**,返回 `Access Denied`。 #### 步骤 3: 实现安全的私有对象访问机制 这是最关键的一步。对于私有桶中的业务附件,**绝对不能直接暴露 URL**,必须通过后端应用生成**预签名 URL**。 以下是一个 Spring Boot 集成 MinIO 并生成预签名 URL 的示例: 1. **后端服务配置与代码示例**: * **application.yml 配置**: ```yaml minio.public-bucket: public-bucket minio.private-bucket: private-bucket ``` * **Service 层:生成预签名 URL**: ```java @Service @RequiredArgsConstructor public class MinioService { private final MinioClient minioClient; private final MinioProperties properties; // 读取配置的类 /** * 为私有文件生成一个有时效性的下载链接(例如7天) * @param objectName 对象在私有桶中的名称 * @return 预签名的临时URL */ public String getPresignedDownloadUrl(String objectName) throws Exception { return minioClient.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() .method(Method.GET) // 指定GET方法用于下载 .bucket(properties.getPrivateBucket()) .object(objectName) .expiry(7, TimeUnit.DAYS) // 设置链接7天后过期 .build() ); } // ... 其他方法如文件上传等 } ``` * **Controller 层**: ```java @RestController @RequestMapping("/api/files") @RequiredArgsConstructor public class FileController { private final MinioService minioService; @GetMapping("/download-url") public ResponseEntity<String> getDownloadUrl(@RequestParam String filename) { try { // 1. 此处可添加业务逻辑:验证当前用户是否有权限下载此文件 // 2. 如果有权限,则生成预签名URL String url = minioService.getPresignedDownloadUrl(filename); return ResponseEntity.ok(url); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error generating URL"); } } } ``` 3. **前端访问流程**: 1. 用户点击“下载附件”按钮。 2. 前端调用后端 API (`/api/files/download-url?filename=xxx`)。 3. 后端校验用户身份和权限后,调用 MinIO 服务生成一个临时的预签名 URL 并返回给前端。 4. 前端接收到这个临时 URL 后,自动重定向或使用 `window.location.href` 打开它,即可下载文件。 5. **这个临时 URL 过期即失效**,即使被他人获取,风险也在可控范围内。 ### 💎 总结 通过 **“公共桶只读 + 私有桶完全隔离 + 后端预签名”** 的组合方案,可以完美解决需求中的安全问题。 * 对于**公共资源**,利用 MinIO 的 `download` 策略提供高效便捷的直接访问。 * 对于**业务附件**等敏感数据,通过 **`private` 策略**彻底关闭公网直连,所有访问必须经由**后端应用授权**后,通过**有时效的预签名 URL** 进行。 这样既满足了业务功能,又极大地提升了系统的安全性。实施时,请务必记得结合网络隔离和 HTTPS 等额外安全措施。 --- 在通过后端生成预签名 URL(Presigned URL)提供 MinIO 资源访问权限之前,**必须对请求的用户进行身份校验**。这个过程的核心是:**业务后端的用户身份体系** 与 **MinIO 的对象存储权限** 是两套独立的系统,需要通过后端应用来“桥接”和“翻译”。 下面详细分析这个身份校验的过程和权限控制的原理。 ### 🔐 一、 身份校验与权限控制流程 整个流程的核心在于,最终用户不直接拥有 MinIO 的权限,而是通过后端应用这个“可信中介”来间接获得临时、可控的资源访问权。下图描绘了预签名 URL 生成和使用的完整流程,其中后端对用户的身份校验是发起一切操作的起点: ```mermaid sequenceDiagram participant U as 最终用户 participant A as 后端应用服务器 participant M as MinIO 服务 U->>A: 1. 请求下载/上传文件(携带Cookie/JWT) Note right of A: 身份校验阶段 A->>A: 2. 验证用户身份与权限 alt 验证失败 A-->>U: 返回错误(401/403) else 验证成功 Note right of A: 预签名URL生成阶段 A->>M: 3. 使用自身凭证请求预签名URL M-->>A: 4. 返回预签名URL A-->>U: 5. 返回预签名URL Note right of U: 直接访问阶段 U->>M: 6. 使用预签名URL直接访问MinIO M->>M: 7. 验证URL签名及有效期 alt URL验证失败 M-->>U: 返回错误(403) else URL验证成功 M-->>U: 8. 返回请求的文件或允许上传 end end ``` 这个过程的关键在于: 1. **最终用户**与**后端应用**之间,通过你熟悉的业务身份认证体系(如 Cookie/Session 或 JWT)来建立信任。 2. **后端应用**与 **MinIO** 之间,通过 MinIO 的 Access Key 和 Secret Key 来建立信任。 3. 预签名 URL 则是后端应用用自己的 MinIO 凭证,为特定操作(如 GetObject)生成的一个“临时门票”。最终用户拿着这张“门票”可以直接去找 MinIO“兑换”服务,而无需再经过后端应用。 ### 📝 二、 后端身份校验的具体内容 当后端应用收到用户请求后,它会进行一系列校验,这部分属于业务逻辑,与 MinIO 无关: * **身份认证 (Authentication)**: 确认用户是谁。通常通过验证用户提交的 **Token(如 JWT)** 或 **Session Cookie** 来实现。 * **权限授权 (Authorization)**: 判断该用户是否有权执行其请求的操作。这通常需要通过查询数据库或缓存来确认用户的角色、权限等级或是否拥有该文件的访问权。 * **业务规则校验**: 根据业务需求进行更细致的控制。例如: * 该文件是否属于该用户? * 用户是否处于试用期,禁止下载原始大小图片? * 用户的下载次数是否已用完? **只有通过了所有这些校验**,后端应用才会动用其高级权限,向 MinIO 请求生成预签名 URL。 ### 🔗 三、 用户身份与 MinIO 权限的关联方式 后端应用如何将“用户”这个概念映射到 MinIO 的权限上,通常有两种模式: 1. **代理模式(最常见)**: * **设置**:后端应用使用一个高权限的 MinIO 客户端(拥有读取整个存储桶的权限)。 * **工作方式**:所有用户对文件的访问请求,都通过这个高权限客户端来生成预签名 URL。**MinIO 只知道请求来自后端应用,不知道最终用户是谁**。 * **权限控制**:权限控制完全由你的后端业务逻辑决定。MinIO 只是无条件地信任来自后端应用的请求。 * **优点**:实现简单,无需在 MinIO 中维护复杂的用户体系。 * **缺点**:权限控制粒度完全依赖于后端应用的逻辑。 2. **~~映射模式(更精细)~~**: * **设置**:为每个应用用户或在 MinIO 中创建一个对应的 IAM 用户或策略(Policy)。 * **工作方式**:后端应用在校验完用户身份后,使用**对应该用户的 MinIO 凭证**来生成预签名 URL。 * **权限控制**:权限控制由 MinIO 和业务后端共同完成。MinIO 能知道请求来自于哪个具体用户,并会根据为该用户设置的策略来限制其访问范围(例如,只能访问某个前缀下的文件)。 * **优点**:权限控制粒度更细,安全性更高,可以实现不同用户访问不同文件的需求。 * **缺点**:需要在 MinIO 中维护大量用户和策略,系统更复杂。 对于绝大多数应用,**代理模式** 已经完全足够且是推荐的做法。 ### 🛡️ 四、 安全性与最佳实践 1. **预签名 URL 的本质是“临时授权”**:它解决了让一个无权限的用户临时获得特定资源访问权的问题,但其安全性建立在后端严格的身份校验和 URL 的短暂有效期上。 2. **设置短暂的有效期**:这是最重要的安全措施。根据场景,将有效期设置为 5 到 15 分钟,即使 URL 被泄露,风险窗口也很短。 3. **最小权限原则**:用于生成签名的 MinIO 客户端凭证,其权限应被严格限制。如果后端只需要生成下载链接,那么它的权限应只有 `s3:GetObject`,绝不能赋予 `s3:PutObject` 或 `s3:DeleteObject` 等权限。 4. **监控与审计**:记录所有预签名 URL 的生成和使用日志,以便在出现安全事件时进行追踪。 ### 💎 总结 可以明确的是:**必须在生成预签名 URL 前进行身份校验**。 这个过程可以概括为: 1. 用户向后台管理员(后端应用)申请参观证(请求预签名 URL)。 2. 管理员(后端应用)首先核查你的身份和权限(业务逻辑校验)。 3. 核查通过后,管理员用自己的高级门禁卡(MinIO 凭证)为你生成一张有时效的参观证(预签名 URL)。 4. 你拿着这张参观证可以直接进入展厅(MinIO)访问特定展品(对象),门口的保安(MinIO 服务)只认参观证,不再核查你的原始身份。 以上是业界常用方案,对于iidp平台可以结合多租户权限来结合考虑。
Uploading file...
Sidebar
[[_TOC_]]
Edit message:
Cancel