소스 검색

前端调用看看

刘云鑫 1 개월 전
부모
커밋
bc953c9407
2개의 변경된 파일87개의 추가작업 그리고 25개의 파일을 삭제
  1. 7 0
      alien-store/pom.xml
  2. 80 25
      alien-store/src/main/java/shop/alien/store/controller/OSSDirectUploadNewController.java

+ 7 - 0
alien-store/pom.xml

@@ -340,6 +340,13 @@
             <groupId>com.alibaba</groupId>
             <artifactId>fastjson</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>sts20150401</artifactId>
+            <version>1.1.6</version>
+        </dependency>
+
     </dependencies>
 
     <build>

+ 80 - 25
alien-store/src/main/java/shop/alien/store/controller/OSSDirectUploadNewController.java

@@ -2,6 +2,8 @@ package shop.alien.store.controller;
 
 import com.alibaba.fastjson.JSONObject;
 import com.aliyun.oss.common.utils.BinaryUtil;
+import com.aliyun.sts20150401.models.AssumeRoleResponse;
+import com.aliyun.sts20150401.models.AssumeRoleResponseBody;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -14,10 +16,14 @@ import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import javax.annotation.PostConstruct;
 import javax.crypto.Mac;
 import javax.crypto.spec.SecretKeySpec;
 import java.nio.charset.StandardCharsets;
-import java.time.*;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
 
@@ -46,6 +52,25 @@ public class OSSDirectUploadNewController {
     @Value("${ali.oss.bucketName}")
     private String bucketName;
 
+    @Value("${ali.oss.ossStsRoleArn}")
+    private String ossStsRoleArn;
+
+    public static String STATIC_ACCESS_KEY_ID;
+    public static String STATIC_ACCESS_KEY_SECRET;
+    public static String STATIC_END_POINT;
+    public static String STATIC_BUCKET_NAME;
+    public static String OSS_STS_ROLE_ARN;
+    // 3. 初始化方法:对象创建后立即执行,把非静态值赋给静态变量
+    @PostConstruct
+    public void init() {
+        STATIC_ACCESS_KEY_ID = this.accessKeyId;
+        STATIC_ACCESS_KEY_SECRET = this.accessKeySecret;
+        STATIC_END_POINT = this.endPoint;
+        STATIC_BUCKET_NAME = this.bucketName;
+        OSS_STS_ROLE_ARN = this.ossStsRoleArn;
+    }
+
+
     // 从endPoint提取region(例如:oss-cn-beijing.aliyuncs.com -> cn-beijing)
     private String getRegion() {
         if (endPoint != null && endPoint.contains("oss-")) {
@@ -58,6 +83,22 @@ public class OSSDirectUploadNewController {
         return "cn-beijing"; // 默认值
     }
 
+    /**
+     * 从 OSS Endpoint 得到 STS Endpoint。获取临时凭证必须使用 STS 端点,不能使用 OSS 端点。
+     * 例如:oss-cn-beijing.aliyuncs.com -> sts.cn-beijing.aliyuncs.com
+     */
+    private static String getStsEndpointFromOssEndpoint(String ossEndpoint) {
+        if (ossEndpoint != null && ossEndpoint.contains("oss-")) {
+            int start = ossEndpoint.indexOf("oss-") + 4;
+            int end = ossEndpoint.indexOf(".aliyuncs.com");
+            if (end > start) {
+                String region = ossEndpoint.substring(start, end);
+                return "sts." + region + ".aliyuncs.com";
+            }
+        }
+        return "sts.cn-beijing.aliyuncs.com";
+    }
+
     // 构建host地址
     private String getHost() {
         return "https://" + bucketName + "." + endPoint;
@@ -99,31 +140,28 @@ public class OSSDirectUploadNewController {
         return formattedDate;
     }
     //初始化STS Client(如果需要使用STS,取消注释并配置相关环境变量)
-    /*
     public static com.aliyun.sts20150401.Client createStsClient() throws Exception {
         // 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。
         // 建议使用更安全的 STS 方式。
         com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
                 // 必填,请确保代码运行环境设置了环境变量 OSS_ACCESS_KEY_ID。
-                .setAccessKeyId(System.getenv("OSS_ACCESS_KEY_ID"))
+                .setAccessKeyId(STATIC_ACCESS_KEY_ID)
                 // 必填,请确保代码运行环境设置了环境变量 OSS_ACCESS_KEY_SECRET。
-                .setAccessKeySecret(System.getenv("OSS_ACCESS_KEY_SECRET"));
-        // Endpoint 请参考 https://api.aliyun.com/product/Sts
-        config.endpoint = "sts.cn-hangzhou.aliyuncs.com";
+                .setAccessKeySecret(STATIC_ACCESS_KEY_SECRET);
+        // 获取临时凭证必须使用 STS 端点,不能使用 OSS 端点。参见 https://api.aliyun.com/product/Sts
+        config.endpoint = getStsEndpointFromOssEndpoint(STATIC_END_POINT);
         return new com.aliyun.sts20150401.Client(config);
     }
-    */
 
     //获取STS临时凭证
     // 注意:此方法需要STS服务支持,如果不需要STS,可以直接使用AccessKey
     // 这里暂时注释掉STS相关代码,直接使用配置的AccessKey
-    /*
     public static AssumeRoleResponseBody.AssumeRoleResponseBodyCredentials getCredential() throws Exception {
         com.aliyun.sts20150401.Client client = OSSDirectUploadNewController.createStsClient();
         com.aliyun.sts20150401.models.AssumeRoleRequest assumeRoleRequest = new com.aliyun.sts20150401.models.AssumeRoleRequest()
                 // 必填,请确保代码运行环境设置了环境变量 OSS_STS_ROLE_ARN
-                .setRoleArn(System.getenv("OSS_STS_ROLE_ARN"))
-                .setRoleSessionName("yourRoleSessionName");// 自定义会话名称
+                .setRoleArn(OSS_STS_ROLE_ARN)
+                .setRoleSessionName("ossDirectPass");// 自定义会话名称
         com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
         try {
             // 复制代码运行请自行打印 API 的返回值
@@ -131,21 +169,33 @@ public class OSSDirectUploadNewController {
             // credentials里包含了后续要用到的AccessKeyId、AccessKeySecret和SecurityToken。
             return response.body.credentials;
         } catch (Exception error) {
+            String msg = error.getMessage() != null ? error.getMessage() : "";
+            if (msg.contains("NoSuchBucket") || msg.contains("bucket does not exist")) {
+                log.error("指定的Bucket不存在,请检查配置 ali.oss.bucketName: {}", STATIC_BUCKET_NAME, error);
+                throw new RuntimeException("指定的Bucket不存在,请检查配置 ali.oss.bucketName: " + STATIC_BUCKET_NAME, error);
+            }
+            if (msg.contains("403") || msg.contains("not authorized") || msg.contains("authorized by RAM")) {
+                log.error("STS AssumeRole 无权限(403):当前 RAM 用户/角色未授权扮演角色,请到阿里云 RAM 控制台检查:1) 当前 AccessKey 对应用户需有 sts:AssumeRole 权限;2) 角色 {} 的信任策略需允许该用户扮演。", OSS_STS_ROLE_ARN, error);
+                throw new RuntimeException("STS 临时凭证无权限(403):请在 RAM 控制台为当前 AccessKey 对应用户授权 sts:AssumeRole,并确保角色 " + OSS_STS_ROLE_ARN + " 的信任策略允许该用户扮演。", error);
+            }
             log.error("获取STS凭证失败: {}", error.getMessage(), error);
             throw new RuntimeException("获取STS凭证失败", error);
         }
-        return null;
     }
-    */
 
     @ApiOperation("生成OSS直传签名(OSS4-HMAC-SHA256方式)")
     @GetMapping("/signature")
     public ResponseEntity<Map<String, String>> getPostSignatureForOssUpload() {
         try {
-            // 直接使用配置的AccessKey(如果使用STS,需要调用getCredential()方法)
-            String accesskeyid = accessKeyId;
-            String accesskeysecret = accessKeySecret;
-            String securitytoken = ""; // 如果使用STS,这里应该是securityToken
+//            // 直接使用配置的AccessKey(如果使用STS,需要调用getCredential()方法)
+//            String accesskeyid = accessKeyId;
+//            String accesskeysecret = accessKeySecret;
+//            String securitytoken = ""; // 如果使用STS,这里应该是securityToken
+            AssumeRoleResponseBody.AssumeRoleResponseBodyCredentials sts_data = getCredential();
+
+            String accesskeyid =  sts_data.accessKeyId;
+            String accesskeysecret =  sts_data.accessKeySecret;
+            String securitytoken =  sts_data.securityToken;
             
             String region = getRegion();
             String host = getHost();
@@ -174,6 +224,13 @@ public class OSSDirectUploadNewController {
         bucketCondition.put("bucket", bucketName);
         conditions.add(bucketCondition);
 
+            // 如果使用STS,需要添加security-token条件
+            if (securitytoken != null && !securitytoken.isEmpty()) {
+                Map<String, String> securityTokenCondition = new HashMap<>();
+                securityTokenCondition.put("x-oss-security-token", securitytoken);
+                conditions.add(securityTokenCondition);
+            }
+
         Map<String, String> signatureVersionCondition = new HashMap<>();
         signatureVersionCondition.put("x-oss-signature-version", "OSS4-HMAC-SHA256");
         conditions.add(signatureVersionCondition);
@@ -186,12 +243,6 @@ public class OSSDirectUploadNewController {
         dateCondition.put("x-oss-date", x_oss_date);
         conditions.add(dateCondition);
 
-        // 如果使用STS,需要添加security-token条件
-        if (securitytoken != null && !securitytoken.isEmpty()) {
-            Map<String, String> securityTokenCondition = new HashMap<>();
-            securityTokenCondition.put("x-oss-security-token", securitytoken);
-            conditions.add(securityTokenCondition);
-        }
 
         conditions.add(Arrays.asList("content-length-range", 1, 10240000));
         conditions.add(Arrays.asList("eq", "$success_action_status", "200"));
@@ -201,8 +252,8 @@ public class OSSDirectUploadNewController {
 
         String jsonPolicy = mapper.writeValueAsString(policy);
 
-        // 步骤2:构造待签名字符串(StringToSign)。
-        String stringToSign = new String(Base64.encodeBase64(jsonPolicy.getBytes()));
+        // 步骤2:构造待签名字符串(StringToSign)。使用 UTF-8 与文档/服务端一致,避免签名差异。
+        String stringToSign = new String(Base64.encodeBase64(jsonPolicy.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
 
         // 步骤3:计算SigningKey。
         byte[] dateKey = hmacsha256(("aliyun_v4" + accesskeysecret).getBytes(), date);
@@ -225,8 +276,9 @@ public class OSSDirectUploadNewController {
         }
 
         Map<String, String> response = new HashMap<>();
-        // 将数据添加到 map 中
+        // 与文档一致:version / x_oss_signature_version 均返回,便于前端按文档示例取值
         response.put("version", "OSS4-HMAC-SHA256");
+        response.put("x_oss_signature_version", "OSS4-HMAC-SHA256");
         // 这里是易错点,不能直接传policy,需要做一下Base64编码
         response.put("policy", stringToSign);
         response.put("x_oss_credential", x_oss_credential);
@@ -238,6 +290,9 @@ public class OSSDirectUploadNewController {
         }
         response.put("dir", uploadDir);
         response.put("host", host);
+        // 前端 PostObject 表单字段名必须与 OSS 约定一致,否则会返回 MethodNotAllowed(405):
+        // policy, x-oss-signature(本接口返回 signature), x-oss-signature-version, x-oss-credential, x-oss-date,
+        // x-oss-security-token(有 STS 时), key(=dir+文件名), success_action_status, file(必须最后一项)
         if (!base64CallbackBody.isEmpty()) {
             response.put("callback", base64CallbackBody);
         }