• 【避坑】minio临时凭证STS实现上传,下载


    一、背景

    minio作为图床,采用用户到minio服务器的点对点使用方式,将上传用户的长期凭证放在客户端(app或者web端)带来极大的风险。通常通过临时凭证STS签名的方式实现。简单的逻辑关系图如下:

    二、minio服务docker安装

    比较简单,我把docker-compose.yaml配置文件贴出来

    1. jeecg-boot-minio:
    2. image: minio/minio:latest
    3. networks:
    4. - jeecglocalhost
    5. container_name: jeecg-boot-minio
    6. ports:
    7. - "9001:9001"
    8. - "9000:9000"
    9. restart: always
    10. command: server /data --console-address ":9001"
    11. environment:
    12. MINIO_ROOT_USER: lizzminio
    13. MINIO_ROOT_PASSWORD: lizzminio #大于等于8位
    14. TZ: Asia/Shanghai
    15. logging:
    16. options:
    17. max-size: "50M" # 最大文件上传限制
    18. max-file: "10"
    19. driver: json-file
    20. volumes:
    21. #宿主机是window:D:/dockerFile/dockerdata/minio/data
    22. #宿主机是Linux:/dockerFile/dockerdata/minio/data
    23. - /dockerFile/dockerdata/minio/data:/data # 映射文件路径

    配置中一定要加上时区设置,保证宿主机和docker时区一致。

    中文操作文档:Java Client API参考文档 - MinIO 帮助文档 - 开发文档 - 文江博客

    官网操作文档:MinIO | The MinIO Quickstart Guide

    三、实践操作

    通过搜索资料和官方文档,大概确定了两种配置实现方式。(1)mc客户端+aws配置工具,实际上命令行的方式(2)minio控制台面板配置,实际上通过界面的形式。 网络上大量充斥第一种方式,对于初学者,会觉得麻烦,无法直观理解。我把自己通过第二种方式效果罗列出来,供大家参考。

    (1)mc客户端+aws配置工具

    这种网上资料比较多,大概就是先安装mc客户端(mc啥意思,可以去官网看看),aws就是用来获取minio sts配置结果的工具。

    第一步:在运维机器或者minio的宿主机器上安装MC客户端和AWS应用

    1. (1)安装aws
    2. //下载安装包
    3. curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64-2.0.30.zip" -o "awscliv2.zip"
    4. //解压文件
    5. unzip awscliv2.zip
    6. //安装,root用户sudo可以去掉
    7. sudo ./aws/install
    8. (2)安装mc客户端
    9. //在当前目录下载mc
    10. wget https://dl.min.io/client/mc/release/linux-amd64/mc
    11. //设置为可执行
    12. chmod +x mc
    13. //在本目录执行mc命令,查看mc提供的操作命令
    14. ./mc --help

    第二步:mc连接或者说绑定minio服务

    1. minio的ip和端口号,minio的用户名和密码,local表示配置本地服务
    2. ./mc config host add local http://127.0.0.1:9000 username password

     配置完成之后可以在~/.mc/config.json中查看,local配置好了本地的minio服务

    cat  ~/.mc/config.json

    第三步:利用mc创建用户

    为local的minio服务创建一个用户:zhaohao,密码:zhaohao123

    ./mc admin user add local zhaohao   zhaohao123

    第四步:利用mc创建策略

    首先创建一个json文件

    1. {
    2. "Version": "2012-10-17",
    3. "Statement": [
    4. {
    5. "Effect": "Allow",
    6. "Action": [
    7. "s3:DeleteObject",
    8. "s3:GetObject",
    9. "s3:PutObject"
    10. ],
    11. "Resource": [
    12. "arn:aws:s3:::*"
    13. ]
    14. }
    15. ]
    16. }

    Version是版本号,Action里是允许使用s3接口的删除、获取上传对象功能

    Resource是访问控制,*表示可以访问minio服务的所有bucket,若想限制访问

    名为test的bucket可替换成"arn:aws:s3:::test/*" ,表示只可访问名为test的bucket

    Action中s3接口数组权限策略。

    其次,将json文件绑定到minio服务上

    1. //表示为local的minio服务绑定一个叫testpolicy的访问策略,后面是策略文件的地址
    2. ./mc admin policy add local testpolicy /home/mxShop/minio/config/company.json

    查看绑定的策略:./mc admin policy info local minio 

    若出现:-bash: ./mc: No such file or directory,需要切换到mc所在目录

    第五步:利用mc绑定用户和策略

    1. //将前面的testpolicy策略绑定给zhaohao   用户
    2. ./mc admin policy set local testpolicy user=zhaohao  

    第六步:利用aws进行配置验证

    为minio服务绑定完策略后,需要使用aws配置该策略

    aws configure --profile testpolicy

    执行完上面命令后需要进行配置:

    1. minio用户的信息
    2. AWS Access Key ID [None]: minio
    3. AWS Secret Access Key [None]: miniopwd
    4. aws服务的站点
    5. Default region name [None]: cn-north-1
    6. 第四行是文件类型
    7. Default output format [None]: json

     配置完的信息可在~/.aws目录下进行查看和修改

     第七步:测试

    输入以下命令

    --profile:指定策略

    --prolicy:进行二次策略限制,格式与之前的策略相同

    1. aws --profile minio \
    2. --endpoint-url 'http://localhost:9000' \
    3. sts assume-role \
    4. --policy '{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Action": ["s3:GetObject"],"Resource": ["arn:aws:s3:::*"]}]}' --role-arn 'arn:aws:s3:::*' \
    5. --role-session-name anything

     结果:这些就是我们需要的签名信息了

    1. {
    2. "Credentials": {
    3. "AccessKeyId": "JJOUBSPDSK20Y2500847",
    4. "SecretAccessKey": "QpXMeXQHE+qYGddh5On+DlWYXpenK+j5yOHBn4Hm",
    5. "SessionToken": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiJKSk9VQlNQRFNLMjBZMjUwMDg0NyIsImV4cCI6MTY0MTg5NTQxNiwicGFyZW50IjoibWluaW8iLCJzZXNzaW9uUG9saWN5IjoiZXlKV1pYSnphVzl1SWpvZ0lqSXdNVEl0TVRBdE1UY2lMQ0pUZEdGMFpXMWxiblFpT2lCYmV5SkZabVpsWTNRaU9pQWlRV3hzYjNjaUxDSkJZM1JwYjI0aU9pQmJJbk16T2tkbGRFOWlhbVZqZENKZExDSlNaWE52ZFhKalpTSTZJRnNpWVhKdU9tRjNjenB6TXpvNk9pb2lYWDFkZlE9PSJ9.Gt2I3TG0jpvApAQrrS1QEZV0N3cIbtXnbNQ07-qe2cLb-uty-IaiVYhCyUHG80vwqJCxjnHiqYitziMWZna6sg",
    6. "Expiration": "2022-01-11T10:03:36+00:00"
    7. },
    8. "AssumedRoleUser": {
    9. "Arn": ""
    10. }
    11. }

    (2)minio控制台面板配置

    第一步:登录控制面板,创建策略

     

    第二步:创建用户,绑定策略

    记录自己创建的用户Access Key  Secret Key

     

    以上两步就OK了,简单快速

    (3)java客户端测试

    引入minio sdk

    1. <dependency>
    2. <groupId>io.miniogroupId>
    3. <artifactId>minioartifactId>
    4. <version>8.3.3version>
    5. dependency>
    1. package com.rcgx.util.minio;
    2. import com.rcgx.pay.common.util.DateUtil;
    3. import io.minio.MinioClient;
    4. import io.minio.PutObjectArgs;
    5. import io.minio.credentials.AssumeRoleProvider;
    6. import io.minio.credentials.Credentials;
    7. import io.minio.credentials.StaticProvider;
    8. import io.minio.errors.*;
    9. import java.io.BufferedInputStream;
    10. import java.io.File;
    11. import java.io.FileInputStream;
    12. import java.io.IOException;
    13. import java.security.InvalidKeyException;
    14. import java.security.NoSuchAlgorithmException;
    15. import java.util.Date;
    16. public class Test03 {
    17. //服务所在ip地址和端口
    18. public static final String ENDPOINT = "http://192.168.3.38:9000/";
    19. //mc的用户名
    20. public static final String ACCESS_KEY_COMPANY = "zhaohao";
    21. //mc的密码
    22. public static final String SECRET_KEY_COMPANY = "zhaohao123";
    23. //aws服务端点
    24. public static final String REGION = "cn-north-1";
    25. //上传的bucket名
    26. public static final String BUCKET = "test";
    27. //授权策略,允许访问名为bucket的桶的目录
    28. public static final String ROLE_ARN = "arn:aws:s3:::test/*";
    29. public static final String ROLE_SESSION_NAME = "anysession";
    30. //定义策略,可进行二次限定
    31. public static final String POLICY_GET_AND_PUT = "{\n" +
    32. " \"Version\": \"2012-10-17\",\n" +
    33. " \"Statement\": [\n" +
    34. " {\n" +
    35. " \"Effect\": \"Allow\",\n" +
    36. " \"Action\": [\n" +
    37. " \"s3:GetObject\",\n" +
    38. " \"s3:GetBucketLocation\",\n" +
    39. " \"s3:PutObject\"\n" +
    40. " ],\n" +
    41. " \"Resource\": [\n" +
    42. " \"arn:aws:s3:::test/*\"\n" +
    43. " ]\n" +
    44. " }\n" +
    45. " ]\n" +
    46. "}";
    47. public static void main(String[] args) {
    48. try {
    49. int durationSeconds = 360000;//秒
    50. //创建签名对象
    51. AssumeRoleProvider provider = new AssumeRoleProvider(
    52. ENDPOINT,
    53. ACCESS_KEY_COMPANY,
    54. SECRET_KEY_COMPANY,
    55. durationSeconds,//默认3600秒失效,设置小于这个就是3600,大于3600就实际值
    56. POLICY_GET_AND_PUT,
    57. REGION,
    58. ROLE_ARN,
    59. ROLE_SESSION_NAME,
    60. null,
    61. null);
    62. Date expirationDate = DateUtil.getDateByAddMinute(new Date(),durationSeconds/60);
    63. /**
    64. * 打印provider签名属性
    65. */
    66. System.out.println("sessionToken="+provider.fetch().sessionToken());
    67. System.out.println("accessKey="+provider.fetch().accessKey());
    68. System.out.println("secretKey="+provider.fetch().secretKey());
    69. System.out.println("isExpired="+provider.fetch().isExpired());
    70. System.out.println("expirationDate="+com.rcgx.util.DateUtil.getDateTimeFormat(expirationDate));
    71. Credentials credentials = provider.fetch();
    72. String sessionToken=provider.fetch().sessionToken();
    73. String accessKey=provider.fetch().accessKey();
    74. String secretKey=provider.fetch().secretKey();
    75. StaticProvider staticProvider = new StaticProvider(credentials.accessKey(), credentials.secretKey(), credentials.sessionToken());
    76. MinioClient minioClient = MinioClient.builder().endpoint(ENDPOINT).credentialsProvider(staticProvider).build();
    77. File file = new File("C:\\Users\\zhaohao\\Pictures\\Camera Roll\\人民政府市长办公会议纪要【2018】126号-六、公交场站免于处罚-2018年第4次轨道交通建设50专题例会会议纪要(1)_1645434959808.pdf.png");
    78. //这个objectName的值是经过上面的policy授权的
    79. String objectName = "mx/pic1.jpg";
    80. try {
    81. FileInputStream fileInputStream = new FileInputStream(file);
    82. minioClient.putObject(PutObjectArgs.builder().bucket(BUCKET)
    83. .object(objectName)
    84. .contentType("image/jpg")
    85. .stream(fileInputStream, fileInputStream.available(), -1).build());
    86. File file2 = new File("C:\\Users\\zhaohao\\Pictures\\Camera Roll\\微信截图_20210308150013 - 副本.png");
    87. //这个objectName的值是经过上面的policy授权的
    88. String objectName2 = "mx/pic2.jpg";
    89. FileInputStream fileInputStream2 = new FileInputStream(file2);
    90. minioClient.putObject(PutObjectArgs.builder().bucket(BUCKET)
    91. .object(objectName2)
    92. .contentType("image/jpg")
    93. .stream(fileInputStream2, fileInputStream2.available(), -1).build());
    94. } catch (Exception e) {
    95. e.printStackTrace();
    96. }
    97. /*//使用签名获取mc对象
    98. MinioClient minioClient = MinioClient.builder()
    99. .endpoint(ENDPOINT)
    100. .credentialsProvider(provider)
    101. .build();
    102. String filename = "github-docker-compose.yml";
    103. try {
    104. //对象流,获取文件
    105. BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\dockerFile\\" + filename));
    106. //使用mc对象上传文件
    107. minioClient.putObject(PutObjectArgs.builder()
    108. //桶名和aws服务端点
    109. .bucket(BUCKET).region(REGION)
    110. //前缀和对象名
    111. .object("mx/" + filename)
    112. .stream(bis, bis.available(), -1)
    113. .build());
    114. System.out.println("文件上传成功!!");
    115. } catch (ErrorResponseException e) {
    116. e.printStackTrace();
    117. } catch (InsufficientDataException e) {
    118. e.printStackTrace();
    119. } catch (InternalException e) {
    120. e.printStackTrace();
    121. } catch (InvalidKeyException e) {
    122. e.printStackTrace();
    123. } catch (InvalidResponseException e) {
    124. e.printStackTrace();
    125. } catch (IOException e) {
    126. e.printStackTrace();
    127. } catch (ServerException e) {
    128. e.printStackTrace();
    129. } catch (XmlParserException e) {
    130. e.printStackTrace();
    131. }*/
    132. } catch (NoSuchAlgorithmException e) {
    133. e.printStackTrace();
    134. }
    135. }
    136. }

    运行结果

    1. sessionToken=eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiIyRktYWVYxSExKWTJBNldWMjlUMyIsImV4cCI6MTY1OTUzMzgxNywicGFyZW50Ijoiemhhb2hhbyIsInNlc3Npb25Qb2xpY3kiOiJld29nSWxabGNuTnBiMjRpT2lBaU1qQXhNaTB4TUMweE55SXNDaUFpVTNSaGRHVnRaVzUwSWpvZ1d3b2dJSHNLSUNBZ0lrVm1abVZqZENJNklDSkJiR3h2ZHlJc0NpQWdJQ0pCWTNScGIyNGlPaUJiQ2lBZ0lDQWljek02UjJWMFQySnFaV04wSWl3S0lDQWdJQ0p6TXpwSFpYUkNkV05yWlhSTWIyTmhkR2x2YmlJc0NpQWdJQ0FpY3pNNlVIVjBUMkpxWldOMElnb2dJQ0JkTEFvZ0lDQWlVbVZ6YjNWeVkyVWlPaUJiQ2lBZ0lDQWlZWEp1T21GM2N6cHpNem82T25SbGMzUXZLaUlLSUNBZ1hRb2dJSDBLSUYwS2ZRPT0ifQ.BbwnrFLmUiYdxJN4SpXqtMYQgiKioPx_IT08AUSOAma5CrK5pfqS6WZWOo5LXMbx1nmmv0oz_BegDuuc2PsWoQ
    2. accessKey=2FKXYV1HLJY2A6WV29T3
    3. secretKey=XdXIrKOkA6EBdj9I4SRfe1BNiqPHrhC75xgiPwod
    4. isExpired=false
    5. expirationDate=2022-08-03 21:36:57
    6. Process finished with exit code 0

    实际运用可以将accessKey ,secretKey,expirationDate以及其他minio配置通过接口发送给前端 即可实现

    四、异常报错解决避坑

    (1)如果出现以下错误,通常是策略配上的action中,缺少权限。

    (2)但是我看网上很多讲docker容器时区和宿主机时区不一样导致的错误,但是当我把时区调整后,或者直接手动修改docker内时间,保持时间一直后,仍然报这个异常。这个就很容易让人误解,网上的资料不能尽信啊。

    比如: minio AccessDenied_隐0士的博客-CSDN博客_accessdenied minio

    ErrorResponse(code = AccessDenied, message = Access denied, bucketName = bucket, objectName = null_爱驹一族的博客-CSDN博客

  • 相关阅读:
    ES索引Json格式字段设计
    ORACLE Primavera P6/Unifier 中秋快乐
    二进制数的运算原理与门电路实现
    ssm+微信小程序基于微信小程序的社区老人健康管理服务系统的设计与实现毕业设计源码011513
    C# 连接SQL Sever 数据库与数据查询实例 数据仓库
    PHP低代码开发引擎—流程引擎
    IDEA DeBug 调试工具详解
    Yarn的优势及使用
    java8概要
    RK3568平台开发系列讲解(AI篇)车辆检测&车道线识别&可行驶区域分割 模型对比检测结果
  • 原文地址:https://blog.csdn.net/yilvqingtai/article/details/126059858