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

比较简单,我把docker-compose.yaml配置文件贴出来
- jeecg-boot-minio:
- image: minio/minio:latest
- networks:
- - jeecglocalhost
- container_name: jeecg-boot-minio
- ports:
- - "9001:9001"
- - "9000:9000"
- restart: always
- command: server /data --console-address ":9001"
- environment:
- MINIO_ROOT_USER: lizzminio
- MINIO_ROOT_PASSWORD: lizzminio #大于等于8位
- TZ: Asia/Shanghai
- logging:
- options:
- max-size: "50M" # 最大文件上传限制
- max-file: "10"
- driver: json-file
- volumes:
- #宿主机是window:D:/dockerFile/dockerdata/minio/data
- #宿主机是Linux:/dockerFile/dockerdata/minio/data
- - /dockerFile/dockerdata/minio/data:/data # 映射文件路径
中文操作文档:Java Client API参考文档 - MinIO 帮助文档 - 开发文档 - 文江博客
官网操作文档:MinIO | The MinIO Quickstart Guide
通过搜索资料和官方文档,大概确定了两种配置实现方式。(1)mc客户端+aws配置工具,实际上命令行的方式(2)minio控制台面板配置,实际上通过界面的形式。 网络上大量充斥第一种方式,对于初学者,会觉得麻烦,无法直观理解。我把自己通过第二种方式效果罗列出来,供大家参考。
这种网上资料比较多,大概就是先安装mc客户端(mc啥意思,可以去官网看看),aws就是用来获取minio sts配置结果的工具。
- (1)安装aws
-
- //下载安装包
-
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64-2.0.30.zip" -o "awscliv2.zip"
-
- //解压文件
-
- unzip awscliv2.zip
-
- //安装,root用户sudo可以去掉
-
- sudo ./aws/install
-
- (2)安装mc客户端
-
- //在当前目录下载mc
-
- wget https://dl.min.io/client/mc/release/linux-amd64/mc
-
- //设置为可执行
-
- chmod +x mc
-
- //在本目录执行mc命令,查看mc提供的操作命令
-
- ./mc --help
- minio的ip和端口号,minio的用户名和密码,local表示配置本地服务
-
-
- ./mc config host add local http://127.0.0.1:9000 username password
配置完成之后可以在~/.mc/config.json中查看,local配置好了本地的minio服务
cat ~/.mc/config.json

为local的minio服务创建一个用户:zhaohao,密码:zhaohao123
./mc admin user add local zhaohao zhaohao123
首先创建一个json文件
- {
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Allow",
- "Action": [
- "s3:DeleteObject",
- "s3:GetObject",
- "s3:PutObject"
- ],
- "Resource": [
- "arn:aws:s3:::*"
- ]
- }
- ]
- }
Version是版本号,Action里是允许使用s3接口的删除、获取上传对象功能
Resource是访问控制,*表示可以访问minio服务的所有bucket,若想限制访问
名为test的bucket可替换成"arn:aws:s3:::test/*" ,表示只可访问名为test的bucket
Action中s3接口数组权限策略。
其次,将json文件绑定到minio服务上
- //表示为local的minio服务绑定一个叫testpolicy的访问策略,后面是策略文件的地址
-
- ./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所在目录

- //将前面的testpolicy策略绑定给zhaohao 用户
-
- ./mc admin policy set local testpolicy user=zhaohao
为minio服务绑定完策略后,需要使用aws配置该策略
aws configure --profile testpolicy
执行完上面命令后需要进行配置:
- minio用户的信息
- AWS Access Key ID [None]: minio
- AWS Secret Access Key [None]: miniopwd
- aws服务的站点
- Default region name [None]: cn-north-1
- 第四行是文件类型
- Default output format [None]: json
配置完的信息可在~/.aws目录下进行查看和修改

第七步:测试
输入以下命令
--profile:指定策略
--prolicy:进行二次策略限制,格式与之前的策略相同
- aws --profile minio \
- --endpoint-url 'http://localhost:9000' \
- sts assume-role \
- --policy '{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Action": ["s3:GetObject"],"Resource": ["arn:aws:s3:::*"]}]}' --role-arn 'arn:aws:s3:::*' \
- --role-session-name anything
结果:这些就是我们需要的签名信息了
- {
- "Credentials": {
- "AccessKeyId": "JJOUBSPDSK20Y2500847",
- "SecretAccessKey": "QpXMeXQHE+qYGddh5On+DlWYXpenK+j5yOHBn4Hm",
- "SessionToken": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiJKSk9VQlNQRFNLMjBZMjUwMDg0NyIsImV4cCI6MTY0MTg5NTQxNiwicGFyZW50IjoibWluaW8iLCJzZXNzaW9uUG9saWN5IjoiZXlKV1pYSnphVzl1SWpvZ0lqSXdNVEl0TVRBdE1UY2lMQ0pUZEdGMFpXMWxiblFpT2lCYmV5SkZabVpsWTNRaU9pQWlRV3hzYjNjaUxDSkJZM1JwYjI0aU9pQmJJbk16T2tkbGRFOWlhbVZqZENKZExDSlNaWE52ZFhKalpTSTZJRnNpWVhKdU9tRjNjenB6TXpvNk9pb2lYWDFkZlE9PSJ9.Gt2I3TG0jpvApAQrrS1QEZV0N3cIbtXnbNQ07-qe2cLb-uty-IaiVYhCyUHG80vwqJCxjnHiqYitziMWZna6sg",
- "Expiration": "2022-01-11T10:03:36+00:00"
- },
- "AssumedRoleUser": {
- "Arn": ""
- }
- }


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



以上两步就OK了,简单快速
引入minio sdk
- <dependency>
- <groupId>io.miniogroupId>
- <artifactId>minioartifactId>
- <version>8.3.3version>
- dependency>
- package com.rcgx.util.minio;
-
-
- import com.rcgx.pay.common.util.DateUtil;
- import io.minio.MinioClient;
- import io.minio.PutObjectArgs;
- import io.minio.credentials.AssumeRoleProvider;
- import io.minio.credentials.Credentials;
- import io.minio.credentials.StaticProvider;
- import io.minio.errors.*;
-
- import java.io.BufferedInputStream;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.security.InvalidKeyException;
- import java.security.NoSuchAlgorithmException;
- import java.util.Date;
-
- public class Test03 {
- //服务所在ip地址和端口
- public static final String ENDPOINT = "http://192.168.3.38:9000/";
-
- //mc的用户名
- public static final String ACCESS_KEY_COMPANY = "zhaohao";
- //mc的密码
- public static final String SECRET_KEY_COMPANY = "zhaohao123";
- //aws服务端点
- public static final String REGION = "cn-north-1";
- //上传的bucket名
- public static final String BUCKET = "test";
- //授权策略,允许访问名为bucket的桶的目录
- public static final String ROLE_ARN = "arn:aws:s3:::test/*";
- public static final String ROLE_SESSION_NAME = "anysession";
- //定义策略,可进行二次限定
- public static final String POLICY_GET_AND_PUT = "{\n" +
- " \"Version\": \"2012-10-17\",\n" +
- " \"Statement\": [\n" +
- " {\n" +
- " \"Effect\": \"Allow\",\n" +
- " \"Action\": [\n" +
- " \"s3:GetObject\",\n" +
- " \"s3:GetBucketLocation\",\n" +
- " \"s3:PutObject\"\n" +
- " ],\n" +
- " \"Resource\": [\n" +
- " \"arn:aws:s3:::test/*\"\n" +
- " ]\n" +
- " }\n" +
- " ]\n" +
- "}";
- public static void main(String[] args) {
- try {
- int durationSeconds = 360000;//秒
- //创建签名对象
- AssumeRoleProvider provider = new AssumeRoleProvider(
- ENDPOINT,
- ACCESS_KEY_COMPANY,
- SECRET_KEY_COMPANY,
- durationSeconds,//默认3600秒失效,设置小于这个就是3600,大于3600就实际值
- POLICY_GET_AND_PUT,
- REGION,
- ROLE_ARN,
- ROLE_SESSION_NAME,
- null,
- null);
- Date expirationDate = DateUtil.getDateByAddMinute(new Date(),durationSeconds/60);
- /**
- * 打印provider签名属性
- */
- System.out.println("sessionToken="+provider.fetch().sessionToken());
- System.out.println("accessKey="+provider.fetch().accessKey());
- System.out.println("secretKey="+provider.fetch().secretKey());
- System.out.println("isExpired="+provider.fetch().isExpired());
- System.out.println("expirationDate="+com.rcgx.util.DateUtil.getDateTimeFormat(expirationDate));
-
- Credentials credentials = provider.fetch();
- String sessionToken=provider.fetch().sessionToken();
- String accessKey=provider.fetch().accessKey();
- String secretKey=provider.fetch().secretKey();
-
-
-
-
- StaticProvider staticProvider = new StaticProvider(credentials.accessKey(), credentials.secretKey(), credentials.sessionToken());
- MinioClient minioClient = MinioClient.builder().endpoint(ENDPOINT).credentialsProvider(staticProvider).build();
- File file = new File("C:\\Users\\zhaohao\\Pictures\\Camera Roll\\人民政府市长办公会议纪要【2018】126号-六、公交场站免于处罚-2018年第4次轨道交通建设50专题例会会议纪要(1)_1645434959808.pdf.png");
- //这个objectName的值是经过上面的policy授权的
- String objectName = "mx/pic1.jpg";
- try {
- FileInputStream fileInputStream = new FileInputStream(file);
- minioClient.putObject(PutObjectArgs.builder().bucket(BUCKET)
- .object(objectName)
- .contentType("image/jpg")
- .stream(fileInputStream, fileInputStream.available(), -1).build());
-
- File file2 = new File("C:\\Users\\zhaohao\\Pictures\\Camera Roll\\微信截图_20210308150013 - 副本.png");
- //这个objectName的值是经过上面的policy授权的
- String objectName2 = "mx/pic2.jpg";
- FileInputStream fileInputStream2 = new FileInputStream(file2);
- minioClient.putObject(PutObjectArgs.builder().bucket(BUCKET)
- .object(objectName2)
- .contentType("image/jpg")
- .stream(fileInputStream2, fileInputStream2.available(), -1).build());
- } catch (Exception e) {
- e.printStackTrace();
- }
-
-
- /*//使用签名获取mc对象
- MinioClient minioClient = MinioClient.builder()
- .endpoint(ENDPOINT)
- .credentialsProvider(provider)
- .build();
- String filename = "github-docker-compose.yml";
- try {
- //对象流,获取文件
- BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\dockerFile\\" + filename));
- //使用mc对象上传文件
- minioClient.putObject(PutObjectArgs.builder()
- //桶名和aws服务端点
- .bucket(BUCKET).region(REGION)
- //前缀和对象名
- .object("mx/" + filename)
- .stream(bis, bis.available(), -1)
- .build());
- System.out.println("文件上传成功!!");
- } catch (ErrorResponseException e) {
- e.printStackTrace();
- } catch (InsufficientDataException e) {
- e.printStackTrace();
- } catch (InternalException e) {
- e.printStackTrace();
- } catch (InvalidKeyException e) {
- e.printStackTrace();
- } catch (InvalidResponseException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (ServerException e) {
- e.printStackTrace();
- } catch (XmlParserException e) {
- e.printStackTrace();
- }*/
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- }
-
- }
- }
运行结果
-
- sessionToken=eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiIyRktYWVYxSExKWTJBNldWMjlUMyIsImV4cCI6MTY1OTUzMzgxNywicGFyZW50Ijoiemhhb2hhbyIsInNlc3Npb25Qb2xpY3kiOiJld29nSWxabGNuTnBiMjRpT2lBaU1qQXhNaTB4TUMweE55SXNDaUFpVTNSaGRHVnRaVzUwSWpvZ1d3b2dJSHNLSUNBZ0lrVm1abVZqZENJNklDSkJiR3h2ZHlJc0NpQWdJQ0pCWTNScGIyNGlPaUJiQ2lBZ0lDQWljek02UjJWMFQySnFaV04wSWl3S0lDQWdJQ0p6TXpwSFpYUkNkV05yWlhSTWIyTmhkR2x2YmlJc0NpQWdJQ0FpY3pNNlVIVjBUMkpxWldOMElnb2dJQ0JkTEFvZ0lDQWlVbVZ6YjNWeVkyVWlPaUJiQ2lBZ0lDQWlZWEp1T21GM2N6cHpNem82T25SbGMzUXZLaUlLSUNBZ1hRb2dJSDBLSUYwS2ZRPT0ifQ.BbwnrFLmUiYdxJN4SpXqtMYQgiKioPx_IT08AUSOAma5CrK5pfqS6WZWOo5LXMbx1nmmv0oz_BegDuuc2PsWoQ
- accessKey=2FKXYV1HLJY2A6WV29T3
- secretKey=XdXIrKOkA6EBdj9I4SRfe1BNiqPHrhC75xgiPwod
- isExpired=false
- expirationDate=2022-08-03 21:36:57
-
- Process finished with exit code 0
实际运用可以将accessKey ,secretKey,expirationDate以及其他minio配置通过接口发送给前端 即可实现
(1)如果出现以下错误,通常是策略配上的action中,缺少权限。


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