minio文件上传自动创建存储桶,自动设置外链访问策略

亡羊补牛
亡羊补牛 2019-05-18 09:30
阅读需:0




minio可以很方便的搭建自有存储服务,docker运行也比较省心,接触minio是因为冷总的PIGX,以下部分代码片段来源于pigx,这是商业软件,请大家尊重版权,本代码只起到抛砖引玉的作用,接入minio可以查看官方文档,如果需要完整minio 的Spring Starter代码,请去围观冷总的pigx,开源版地址:

https://gitee.com/log4j/pig


minio官方文档java client api 地址:

https://docs.min.io/docs/java-client-api-reference.html


介于SNS富文本code在移动端显示严重变形,所以我就全部截图了,


改造的目的:

1、因为pigx原版的minio前端获取的是blob文件流,部署的时候会直接从主服务器获取,添加了外链策略之后,就能直接从minio服务器获取文件,减少主服务器压力


2、既然多租户,每个租户的文件不分隔会很难管理,租户数据迁移麻烦,所以添加租户自动创建存储桶


3、minio默认创建的存储桶策略是无下载和上传权限的,所以无法外链访问,这也是为什么pigx默认文件流返回给前端,所以创建存储同之后我们要设置 download 权限


那下面就开始:


1、如果有pigx源码,那就直接在MInioTemplate 添加几个方法,这个只是个封装,直接接入minio就可以直接 MinioClient 调用了



2、因为策略设置 setBuckePolicy  的 Policy 参数是一个json字符串,可以使用 getBuckePolicy 传入BucketName 获取到,如果默认创建存储桶,没有设置策略,会返回空值,设置了读策略也就是download,返回的Json格式如下:


其中Action 就是我们要设置的策略,这个因为java client 和 minio的 go源码并不统一,因为没有深入研究,所以简单说明下,上图是读权限的 策略返回的json,下图是minio client 官方客户端设置策略返回的策略列表,可以看到设置download,返回的是 readonly,所以不纠结内部实现,只完成我们自己的需求:


3、每次传参都构造个json太费事了,所以我们写一个方法,直接传入bucketName 和要设置测存储桶策略类型,自动构造个json policy,先定义一个枚举,


构造json policy

这样我们需要的json格式的policy就创建好了,根据传入的策略类型,自动生成json,

4、基础服务好了之后就是前端文件上传的方法了 ,关键代码就几行:



这样就可以根据租户编号自动创建存储桶,并设置只读访问权限,前端就可以通过获取到的filePath 访问minio中的资源了。


别问bucket 的策略我怎么找到的,我是一次一次log.info分析的,不懂go源码,也就没去找,纯人肉的。


感谢小莫兄弟发现的匿名用户可以读取bucket列表问题,修复代码如下:


因为权限策略当时测试download,按照返回的json生成的,所以忽略了,添加了 

s3:ListBucket

导致可以读取列表,修复下


public static String getPolicyType(String bucketName, PolicyType policyType) {
   StringBuilder builder = new StringBuilder();
   builder.append("{\n");
   builder.append("    \"Statement\": [\n");
   builder.append("        {\n");
   builder.append("            \"Action\": [\n");

   switch (policyType) {
      case WRITE:
         builder.append("                \"s3:GetBucketLocation\",\n");
         builder.append("                \"s3:ListBucketMultipartUploads\"\n");
         break;
      case READ_WRITE:
         builder.append("                \"s3:GetBucketLocation\",\n");
         builder.append("                \"s3:ListBucket\",\n");
         builder.append("                \"s3:ListBucketMultipartUploads\"\n");
         break;
      default:
         builder.append("                \"s3:GetBucketLocation\"\n");
         break;
   }

   builder.append("            ],\n");
   builder.append("            \"Effect\": \"Allow\",\n");
   builder.append("            \"Principal\": \"*\",\n");
   builder.append("            \"Resource\": \"arn:aws:s3:::");
   builder.append(bucketName);
   builder.append("\"\n");
   builder.append("        },\n");
   if (PolicyType.READ.equals(policyType)) {
      builder.append("        {\n");
      builder.append("            \"Action\": [\n");
      builder.append("                \"s3:ListBucket\"\n");
      builder.append("            ],\n");
      builder.append("            \"Effect\": \"Deny\",\n");
      builder.append("            \"Principal\": \"*\",\n");
      builder.append("            \"Resource\": \"arn:aws:s3:::");
      builder.append(bucketName);
      builder.append("\"\n");
      builder.append("        },\n");

   }
   builder.append("        {\n");
   builder.append("            \"Action\": ");

   switch (policyType) {
      case WRITE:
         builder.append("[\n");
         builder.append("                \"s3:AbortMultipartUpload\",\n");
         builder.append("                \"s3:DeleteObject\",\n");
         builder.append("                \"s3:ListMultipartUploadParts\",\n");
         builder.append("                \"s3:PutObject\"\n");
         builder.append("            ],\n");
         break;
      case READ_WRITE:
         builder.append("[\n");
         builder.append("                \"s3:AbortMultipartUpload\",\n");
         builder.append("                \"s3:DeleteObject\",\n");
         builder.append("                \"s3:GetObject\",\n");
         builder.append("                \"s3:ListMultipartUploadParts\",\n");
         builder.append("                \"s3:PutObject\"\n");
         builder.append("            ],\n");
         break;
      default:
         builder.append("\"s3:GetObject\",\n");
         break;
   }

   builder.append("            \"Effect\": \"Allow\",\n");
   builder.append("            \"Principal\": \"*\",\n");
   builder.append("            \"Resource\": \"arn:aws:s3:::");
   builder.append(bucketName);
   builder.append("/*\"\n");
   builder.append("        }\n");
   builder.append("    ],\n");
   builder.append("    \"Version\": \"2012-10-17\"\n");
   builder.append("}\n");
   return builder.toString();
}


主要就是修改了生成policy 的json串的代码,经测试直接访问bucket会提示未授权,进入登陆界面,访问资源可以正常读取

再次感谢小莫兄弟!


评论
  • ldw4033
    2019-11-02 22:07
    小老弟,你的这种方法是简单,但是会暴露当前租户(也就是你设置的bucket)的文件夹下的所有的文件,隐私就没有了. ReadOnly意味着 - 允许匿名下载访问包括能够列出所需前缀的对象 WriteOnly意味着 - 允许匿名上传包括能够列出所需前缀的不完整上传 读写 - 匿名访问以上载和下载所有对象。这也意味着完全公开访问。 无 - 是默认(无策略),这意味着所有操作都需要针对所需的存储桶和前缀进行身份验证
    6 回复1