你的浏览器不支持canvas

做你害怕做的事情,然后你会发现,不过如此。

阿里云oss上传报错Invalid according to Policy Policy expired.

时间: 作者: 黄运鑫

本文章属原创文章,未经作者许可,禁止转载,复制,下载,以及用作商业用途。原作者保留所有解释权。


  • 阿里云oss上传使用的是服务端签名后直传;通过服务端生成上传签名,前端使用服务端生成的签名上传文件。

  • 在能正常使用的情况下突然报错:

<Error>
    <Code>
        AccessDenied
    </Code>
    <Message>
        Invalid according to Policy: Policy expired.
    </Message>
    <RequestId>
        5DB65B8F5C74183230A8E048
    </RequestId>
    <HostId>
        transnal-test.oss-cn-beijing.aliyuncs.com
    </HostId>
</Error>
  • 经过排查原因是服务器时间和前端时间不一致,前端时间比服务端快几分钟,所以服务端生成签名给到前端时,签名已过期。

  • 后台生成签名的java代码如下:

/**
 * Get请求
 */
@GetMapping
protected void doGet(HttpServletRequest request, HttpServletResponse response, String prefix)
        throws ServletException, IOException {

    String endpoint = this.endpoint.replace("http://", "").replace("https://", ""); // 请填写您的 endpoint。
    String host = "http://" + this.bucketName + "." + endpoint; // host的格式为 bucketname.endpoint
    // callbackUrl为 上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
    // String callbackUrl = "http://88.88.88.88.:8888";
    String dir = Constants.FILE_PREFIX; // 用户上传文件时指定的前缀。
    if (StringUtils.isNotBlank(prefix)) {
        dir += prefix + "_"; // 用户上传文件时指定的前缀。
    }

    OSS client = aliOssService.getOssClient();
    try {
        long expireTime = 30;
        long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
        Date expiration = new Date(expireEndTime);
        PolicyConditions policyConds = new PolicyConditions();
        policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
        // policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);

        String postPolicy = client.generatePostPolicy(expiration, policyConds);
        byte[] binaryData = postPolicy.getBytes("utf-8");
        String encodedPolicy = BinaryUtil.toBase64String(binaryData);
        String postSignature = client.calculatePostSignature(postPolicy);

        JSONObject ja1 = new JSONObject();
        ja1.put("accessid", this.accessKeyId);
        ja1.put("policy", encodedPolicy);
        ja1.put("signature", postSignature);
        ja1.put("dir", dir);
        ja1.put("host", host);
        ja1.put("expire", String.valueOf(expireEndTime / 1000));
        // respMap.put("expire", formatISO8601Date(expiration));

        JSONObject jasonCallback = new JSONObject();
        // jasonCallback.put("callbackUrl", callbackUrl);
        jasonCallback.put("callbackBody",
                "filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}");
        jasonCallback.put("callbackBodyType", "application/x-www-form-urlencoded");
        String base64CallbackBody = BinaryUtil.toBase64String(jasonCallback.toString().getBytes());
        ja1.put("callback", base64CallbackBody);


        // System.out.println(ja1.toString());
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "GET, POST");
        response(request, response, ja1.toString());
    } catch (Exception e) {
        e.printStackTrace();
    }
}
  • 解决方法有两种:

    1.后台生成签名时,使用前端传递过来的当前时间。
    2.生成签名时,官方代码中的参数expireTime默认是30秒,改为更大的时间。


对于本文内容有问题或建议的小伙伴,欢迎在文章底部留言交流讨论。