关于阿里云OSS资源STS访问控制

发布于:2022-12-24 ⋅ 阅读:(10347) ⋅ 点赞:(10)

目前还在看
什么是STS
AssumeRole
RAM角色和STS Token常见问题
RAM Policy常见示例
使用TST临时访问凭证访问OSS
大佬的博文1
大佬的博文2
做一下记录,稍后来补.。。。。。
最终解决,生成token这里
最终解决,读取这里
明天再来补自己的笔记;

先说说这个东西用在OSS解决了什么:

1:前端或者其他用户需要临时操作oss
2:私有资源指向在指定时间内允许访问


准备:

1,一个设置为私有的oss-bucket

在这里插入图片描述


2.一个专门用于STSToken相关操作的RAM账号

在这里插入图片描述


3.获取这个用户的AKID和AS

在这里插入图片描述
AS只在第一次打开的时候会显示,记得保存


4.创建权限策略

在这里插入图片描述

{
  "Version": "1",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "oss:GetObject",
        "oss:PutObject",
        "oss:DeleteObject",
        "oss:ListParts",
        "oss:AbortMultipartUpload",
        "oss:ListObjects"
      ],
      "Resource": [
        "acs:oss:*:*:xxxx/xxx"
      ],
      "Condition": {}
    }
  ]
}

授权角色拥有oss获取权限,允许操作的资源是one-goods的bucket,下面/goodsimg目录下的文件
buket名称/目录/*,代表可以操作目录之下所有文件,单独使用目录是无效的

{
  "Version": "1",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "oss:GetObject",
      "Resource": "acs:oss:*:*:one-goods/goodsimg/*"
    }
  ]
}

5.创建/编辑角色

创建:
创建角色-》信任实体:阿里云账号-》下一步-》确定
编辑:
在这里插入图片描述
角色是为了一会STS创建token 的时候指定角色,方便给token指定授权,而不是给当前创建的RAM用户使用,记得复制这里的RAN稍后靠这个指定角色
所以我们还需要给当前的RAM提供STS权限


6.为用户添加权限,这里主要是为了给用户添加STS权限,稍后当前ram才能签发token

在这里插入图片描述


至此,准备工作就完成了;

我们来代码部分;

这里我只示范资源访问,资源请求可以看我上面提供的大佬的博文。他们做了文件上传;
首先需要依赖:

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-core</artifactId>
    <version>3.2.2</version>
</dependency>
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-sts</artifactId>
    <version>3.0.0</version>
</dependency>
<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
	<version>3.11.3</version>
</dependency>

有奇怪的现象是依赖有时候在dependencyManageMent里不下载,拖到普通的dependencys里下了再拉回管理来吧

直接上代码,一行一注释,大家慢慢看

package com.doria.pzh.core;

import cn.hutool.core.bean.BeanUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.http.ProtocolType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;

import java.net.URL;
import java.util.Date;
import java.util.Random;

@SpringBootTest
@RunWith(SpringRunner.class)
public class TestSrv {
    @Autowired
    RedisTemplate<String, String> redisTemplate;

    private final String stsAk="LTAaxxxxxxxxsrkDYw5";
    private final String stsAs="QFeeQCI1xxxxxxxxxxxxxHxG";

    @Test
    public void test(){
        STSResponse sts = sts();
        OssDownVO ossDownVO = BeanUtil.copyProperties(sts, OssDownVO.class);
        ossDownVO.setBucketName("oxxxx"); // ossbucket名称
        ossDownVO.setOssEndpoint("oss-cn-beijing.aliyuncs.com");
        // 注意根目录不要带[/],根目录下的就直接写文件名否则报错The specified key does not exist
        ossDownVO.setResourceOssPath("新建文本文档.txt"); // 资源路径
        URL down = down(ossDownVO);
        // 打印获取到的url
        System.out.println(down);
    }

    /**
     * 获取STS凭证。
     * @return
     */
    public STSResponse sts() {
        AssumeRoleResponse.Credentials credentials = null;
        String endpoint = "sts.cn-beijing.aliyuncs.com"; // STS服务的节点
        String accessKeyId = stsAk;  // 阿里的AK(不是那个ak)
        String accessKeySecret = stsAs; // AS
        // 角色名称,点开角色能看到,这里决定了我们授权的临时token具有什么角色,而角色又有具体的权限
        String roleArn = "acs:ram::1133899369190070:role/oxls-crud";
        String roleSessionName = "AliyunDMSRol--ePolicy"+new Random().nextInt(10000); // 看样子是会话名称的意思
        // 这段可以根据情况自己决定设不设置,这个的作用是在设置的时候是否在原来角色的基础之上额外添加权限策略
        /*String policy = "{\n" +
                "    \"Statement\": [\n" +
                "        {\n" +
                "            \"Action\": [\n" +
                "                \"oss:GetObject\",\n" +
                "                \"oss:PutObject\",\n" +
                "                \"oss:DeleteObject\",\n" +
                "                \"oss:ListParts\",\n" +
                "                \"oss:AbortMultipartUpload\",\n" +
                "                \"oss:ListObjects\"\n" +
                "            ],\n" +
                "            \"Effect\": \"Allow\",\n" +
                "            \"Resource\": [\n" +
                "               \"acs:oss:*:*:oxls/course/*\",\n" +
                "               \"acs:oss:*:*:oxls/course\",\n" +
                "               \"acs:oss:*:*:oxls/*\",\n" +
                "               \"acs:oss:*:*:oxls/\"\n" +
                "            ]\n" +
                "        }\n" +
                "    ],\n" +
                "    \"Version\": \"1\"\n" +
                "}";*/
        try {
            // 创建一个 Aliyun Acs Client, 用于发起 OpenAPI 请求
            // 添加endpoint(直接使用STS endpoint,前两个参数留空,无需添加region ID)
            DefaultProfile.addEndpoint("", "", "Sts", endpoint);
            // 构造default profile(参数留空,无需添加region ID),客户端描述文件
            IClientProfile profile = DefaultProfile.getProfile("", accessKeyId, accessKeySecret);
            // 用profile构造client,至此就拥有了客户端
            DefaultAcsClient client = new DefaultAcsClient(profile);
            // 创建一个 AssumeRoleRequest 并设置请求参数,一个扮演某个角色的请求
            final AssumeRoleRequest request = new AssumeRoleRequest();
            request.setMethod(MethodType.POST); // 请求类型
            request.setRoleArn(roleArn); // 设置角色ARN
            request.setDurationSeconds(900L);// 设置过期时间,最小值15min也就是900秒,不设置默认3600
            request.setRoleSessionName(roleSessionName); //设置会话名称,这里可以自定义,仅仅为了区别token分发给了谁
            //request.setPolicy(policy); // 可选,如果设置了就需要添加
            request.setProtocol(ProtocolType.HTTPS); // 必须使用HTTPS协议访问STS服务);
            final AssumeRoleResponse response = client.getAcsResponse(request); // 使用客户端执行请求
            credentials = response.getCredentials(); // 获取凭证
            // 这里可以再封装返回,或者自行用这些参数请求服务获取正确的url以后返回,也可以获取url后直接重定向,总之就看你自己拉
            System.out.println("Expiration: " + credentials.getExpiration()); // 获取过期时间
            System.out.println("Access Key Id: " + credentials.getAccessKeyId());// 获取AK
            System.out.println("Access Key Secret: " + credentials.getAccessKeySecret());// 获取AS
            System.out.println("Security Token: " + credentials.getSecurityToken());// 获取安全token
            System.out.println("RequestId: " + response.getRequestId());// 获取请求id
        } catch (ClientException e) {
            System.out.println("Failed:");
            System.out.println("Error code: " + e.getErrCode());
            System.out.println("Error message: " + e.getErrMsg());
            System.out.println("RequestId: " + e.getRequestId());
        }
        if (ObjectUtils.isNotEmpty(credentials))
            return STSResponse.builder().accessKeyId(credentials.getAccessKeyId())
                    .accessKeySecret(credentials.getAccessKeySecret())
                    .endpoint(endpoint)
                    .expiration(credentials.getExpiration())
                    .securityToken(credentials.getSecurityToken())
                    .build();
        return new STSResponse();
    }

    /*
     * 下载文件
     *
     * @param ossDownVO 下载文件实体
     * @return 文件的oss地址,不含http/https
     */
    public static URL down(OssDownVO ossDownVO) {
        // String endpoint = stsResponse.getEndpoint(); // 指定节点名称
        String accessKeyId = ossDownVO.getAccessKeyId(); // 指定AK(STS获取到的AK)
        String accessKeySecret = ossDownVO.getAccessKeySecret();// 指定AS(STS获取到的AS)
        String securityToken = ossDownVO.getSecurityToken();// 指定安全Token(STS获取到的安全token)
        String bucketName = ossDownVO.getBucketName(); // bucket名称
        String ossEndpoint = ossDownVO.getOssEndpoint();// OSS节点名称
        String ossPath = ossDownVO.getResourceOssPath();// 资源路径省略节点和bucket部分

        // 指定oss节点名称,AK,AS,ST
        OSS ossClient = new OSSClientBuilder().build(ossEndpoint, accessKeyId, accessKeySecret, securityToken);
        // 设置过期时间,上面设置的是我们获取到的token过期时间,这里设置的是资源过期时间
        Date expiration = new Date(System.currentTimeMillis() + 3600 * 1000);
        // 计算获取资源路径
        URL url = ossClient.generatePresignedUrl(bucketName, ossPath, expiration);
        // 关流
        ossClient.shutdown();
        return url;
    }

}

实体

// 下载文件请求VO
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OssDownVO {
    private String expiration;
    private String accessKeyId;
    private String accessKeySecret;
    private String securityToken;
    private String requestId;
    private String bucketName;
    private String ossEndpoint;
    private String resourceOssPath;
}
// STS 获取到的响应结果
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class STSResponse {
    private String endpoint;
    private String expiration;
    private String accessKeyId;
    private String accessKeySecret;
    private String securityToken;
    private String requestId;
}

总结一波

用户

用于细化管理每个子账号的权限,可以设置只能使用api或者控制台登录
在这里插入图片描述
在这里插入图片描述
里面可以使用系统设置好的权限,也可以自定义权限策略


策略

在这里插入图片描述
用于设置一个权限或者权限组和,这个权限可以给其他用户直接使用,也可以把权限给某个角色

角色

在这里插入图片描述
角色为一个权限的集合,可以给角色设置各种权限,也可以使用自定义的权限策略,通过角色的RAN就可以动态的生成临时访问Token,使用赋予角色的权限

用户组

在这里插入图片描述
用户组其实就是在批量赋予权限,具体权限的编辑和角色与用户一样

实际使用

/**
 * 阿里云STS访问控制工具
 */
@Service
public class AliYunSTSUtils {

    private static STSCertificate stsCertificate;
    @Value("${stsEndpoint}")
    private String stsEndpoint;
    @Value("${stsRoleArn}")
    private String stsRoleArn;
    @Value("${downloadUrlExp}")
    private Long downloadUrlExp;
    @Autowired
    private AliyunOSSConfig aliyunConfig; //已经配置好的oss配置类bean,内含节点信息和桶名称

    /**
     * 根据oss资源路径获取授权以后的临时下载链接
     *
     * @param ossPath
     * @return
     * @throws ClientException
     */
    public URL getPrivateOssResourse(String ossPath) throws ClientException, MalformedURLException {
        if (StringUtils.isBlank(ossPath))
            return new URL("");
        // 示例:https://asssua.oss-cn-shenzhen.aliyuncs.com/audioCourse/NEVERNEVER%20-%20Glorious.mp3
        // 去除前缀获得OSS资源路径
        ossPath = ossPath.replace(aliyunConfig.getPrivateUrlPrefix(), "");
        if (ObjectUtils.isEmpty(stsCertificate) ||
                PzhTimeUtils.getMills(LocalDateTime.parse(stsCertificate.getExpiration(), DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")),ZoneOffset.UTC) - System.currentTimeMillis() <= 60000
        ) {
            // 如果凭证为空或者距离过期小于1分钟,执行刷新凭证
            stsCertificate = getStsCertificate();
        }
        STSOssDownVO stsOssDownVO = BeanUtil.copyProperties(stsCertificate, STSOssDownVO.class);
        stsOssDownVO.setBucketName(aliyunConfig.getPrivateBucketName());
        stsOssDownVO.setOssEndpoint(aliyunConfig.getPrivateEndpoint());
        stsOssDownVO.setResourceOssPath(ossPath);
        return getDownLoadUrl(stsOssDownVO);
    }

    // 获取STS凭证,凭证代表了当前对象客户端通过了授权,这个授权在过期之前可以一直执行oss的相对操作
    private STSCertificate getStsCertificate() throws ClientException {
        AssumeRoleResponse.Credentials credentials;
        String endpoint = stsEndpoint; // STS服务的节点
        String accessKeyId = aliyunConfig.getPrivateAccessKeyId();  // 阿里的AK(不是那个哒哒哒冒蓝火的ak)
        String accessKeySecret = aliyunConfig.getPrivateAccessKeySecret(); // AS
        // 角色名称,点开角色能看到,这里决定了我们授权的临时token具有什么角色,而角色又有具体的权限
        String roleArn = stsRoleArn;
        String roleSessionName = "AliyunDMSRol--ePolicy"; // 看样子是会话名称的意思
        // 这段可以根据情况自己决定设不设置,这个的作用是在设置的时候是否在原来角色的基础之上额外添加权限策略
        /*String policy = "{\n" +
                "    \"Statement\": [\n" +
                "        {\n" +
                "            \"Action\": [\n" +
                "                \"oss:GetObject\",\n" +
                "                \"oss:PutObject\",\n" +
                "                \"oss:DeleteObject\",\n" +
                "                \"oss:ListParts\",\n" +
                "                \"oss:AbortMultipartUpload\",\n" +
                "                \"oss:ListObjects\"\n" +
                "            ],\n" +
                "            \"Effect\": \"Allow\",\n" +
                "            \"Resource\": [\n" +
                "               \"acs:oss:*:*:oxls/course/*\",\n" +
                "               \"acs:oss:*:*:oxls/course\",\n" +
                "               \"acs:oss:*:*:oxls/*\",\n" +
                "               \"acs:oss:*:*:oxls/\"\n" +
                "            ]\n" +
                "        }\n" +
                "    ],\n" +
                "    \"Version\": \"1\"\n" +
                "}";*/

        // 创建一个 Aliyun Acs Client, 用于发起 OpenAPI 请求
        // 添加endpoint(直接使用STS endpoint,前两个参数留空,无需添加region ID)
        DefaultProfile.addEndpoint("", "", "Sts", endpoint);
        // 构造default profile(参数留空,无需添加region ID),客户端描述文件
        IClientProfile profile = DefaultProfile.getProfile("", accessKeyId, accessKeySecret);
        // 用profile构造client,至此就拥有了客户端
        DefaultAcsClient client = new DefaultAcsClient(profile);
        // 创建一个 AssumeRoleRequest 并设置请求参数,一个扮演某个角色的请求
        final AssumeRoleRequest request = new AssumeRoleRequest();
        request.setMethod(MethodType.POST); // 请求类型
        request.setRoleArn(roleArn); // 设置角色ARLNL
        request.setDurationSeconds(3600L);// 设置过期时间,最小值15min也就是900秒,不设置默认3600
        request.setRoleSessionName(roleSessionName); //设置会话名称,这里可以自定义,仅仅为了区别token分发给了谁
        //request.setPolicy(policy); // 可选,如果设置了就需要添加
        request.setProtocol(ProtocolType.HTTPS); // 必须使用HTTPS协议访问STS服务);
        final AssumeRoleResponse response = client.getAcsResponse(request); // 使用客户端执行请求
        credentials = response.getCredentials(); // 获取凭证

        if (ObjectUtils.isNotEmpty(credentials))
            return STSCertificate.builder().accessKeyId(credentials.getAccessKeyId())
                    .accessKeySecret(credentials.getAccessKeySecret())
                    .endpoint(endpoint)
                    .expiration(credentials.getExpiration())
                    .securityToken(credentials.getSecurityToken())
                    .build();
        return new STSCertificate();
    }

    /**
     * 获取授权以后的下载链接
     *
     * @param ossDownVO 下载必备参数。包含了oss参数以及资源位置和证书
     * @return
     */
    private URL getDownLoadUrl(STSOssDownVO ossDownVO) {
        String accessKeyId = ossDownVO.getAccessKeyId(); // 指定AK(STS获取到的AK)
        String accessKeySecret = ossDownVO.getAccessKeySecret();// 指定AS(STS获取到的AS)
        String securityToken = ossDownVO.getSecurityToken();// 指定安全Token(STS获取到的安全token)
        String bucketName = ossDownVO.getBucketName(); // bucket名称
        String ossEndpoint = ossDownVO.getOssEndpoint();// OSS节点名称
        String ossPath = ossDownVO.getResourceOssPath();// 资源路径省略节点和bucket部分
        // 指定oss节点名称,AK,AS,ST
        OSS ossClient = new OSSClientBuilder().build(
                ossEndpoint,
                accessKeyId,
                accessKeySecret,
                securityToken);
        // 设置过期时间,上面设置的是我们获取到的token过期时间,这里设置的是资源过期时间
        Date expiration = new Date(System.currentTimeMillis() + downloadUrlExp * 1000);
        // 计算获取资源路径
        URL url = ossClient.generatePresignedUrl(bucketName, ossPath, expiration);
        // 关流
        ossClient.shutdown();
        return url;
    }
}

@ApiModel(value = "STS授权凭证-sts authorization certificate")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class STSCertificate {

    // sts节点
    private String endpoint;
    // 过期时间
    private String expiration;
    // 凭证ak
    private String accessKeyId;
    // 凭证as
    private String accessKeySecret;
    // 凭证token
    private String securityToken;
    // 请求id
    private String requestId;

}

报错提示:

com.aliyuncs.exceptions.ClientException: NoSuchBucket : The specified bucket does not exist.
千万记住STSEndpoint需要的是STS节点,而不是你的OSS节点
sts.cn-hangzhou.aliyuncs.com
sts.aliyuncs.com

权限不足的解决

com.aliyuncs.exceptions.ClientException: NoPermission : You are not authorized to do this action. You should be authorized by RAM.
https://blog.csdn.net/u014089832/article/details/118735933

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到