s3通过cloudfront签名下载,偶尔报403问题
昨天在弄s3下载的时候,遇到一个问题就是通过Ajax去请求接口返回一个签名的cloudfront的下载url.会随机的报403,
就是说这个文件没有权限,导致下载不了。因为前端下载用的是iframe,起初怀疑是iframe的加载问题,我印象中iframe这货凡正是经常冒出好多问题,
然后让前端同事改成使用A标签后,问题依旧。但是有点奇怪的是,这个url你直接在浏览器里面是能访问和下载的。这也一直就是老板怀疑我代码有问题的原因。
为什么浏览器可以?点按钮请求这个url不行。说实在的,我最开始也一直纠结在这个点上,要说我返回的url有问题,为什么直接输入可以呢?
前端同事后面又试了下,如果url一直不变。那么是能成功下载的。所以,他也确认他的代码没问题,问题陷入僵局了。哎,虽说这样,问题还是要解决呀,只能一层层分析了。
既然动态请求接口的url有问题,那我用程序访问接口拿到url后,再访问这个url看看状态码,于是用go实现了下。
package main
import (
"net/http"
"bytes"
"io/ioutil"
"fmt"
"encoding/json"
"github.com/astaxie/beego/logs"
)
const (
API_URL = "http://ap"
)
type ApiResponse struct {
Code int32 `json:"code"`
Msg string `json:"msg"`
//Data map[string]interface{} `json data`
Data ResultList `json:"data"`
}
type ResultList struct {
Result UrlList `json:"result"`
}
type UrlList struct {
Url string `json:"url"`
}
func getDownUrl() (string,error){
//resp,error := http.Post(API_URL + "fi/dff",)
postJson := `{"req_body":{"fd_lt":[{"fd":"12lvrmtp62q","fn":"contraeeeect.txt"}]}}`
req, error := http.NewRequest("POST",API_URL + "/fi/dff",bytes.NewBuffer([]byte(postJson)))
if error != nil {
return "",error
}
req.Header.Set("Content-Type","application/json;charset=UTF-8")
client := http.Client{}
resp, error :=client.Do(req)
if error != nil {
panic(error)
}
defer resp.Body.Close()
body,_ := ioutil.ReadAll(resp.Body);
//fmt.Println(string(body))
response := &ApiResponse{}
json.Unmarshal(body, response)
fmt.Println(response.Data.Result.Url)
//fmt.Println(response.Data.Result.Url)
return response.Data.Result.Url,nil
}
func getUrl(url string) {
resp, _ := http.Get(url)
defer resp.Body.Close()
if resp.StatusCode != 200{
fmt.Println("error get url",resp.StatusCode)
}
//fmt.Println("status:" + strconv.Itoa(resp.StatusCode))
}
func main(){
urlist := make(map[int]string)
/*for i:=0 ; i< 3 ;i++ {
r, _ := http.Get("https://s3-us-west-1.amazonaws.com/us-west-1.hub.page/1/1a/l1do9rlv/11al1do9rlv")
defer r.Body.Close()
fmt.Println(r.StatusCode)
}
return*/
for i:=0 ; i< 100 ;i++ {
//logs.Info(fmt.Sprintf("第%d次请求",i))
url, _ := getDownUrl()
if url != "" {
urlist[i] = url
getUrl(url)
}
}
logs.Debug("继续访问")
for i,url := range urlist {
//logs.Info(fmt.Sprintf("第%d次请求",i))
if url != "" {
urlist[i] = url
getUrl(url)
}
}
}
以上代码,我是先请求接口返回url,再发get请求,获取状态码。再把url保存起来。
通过上面的输出,发现第一次请求会出现403的情况,第二次请求都是200,这就说明问题出在服务端接口上了。
于是查看代码后,发现在签名的时候有一个时间参数,DateGreaterThan这个去掉就好了。
查了下这个参数表示对象不能在这个时间之前访问。
//从全局变量查找过期时间
$cacheInstance = CacheManage::getInstance();
$expireTime = $cacheInstance->HGet(CacheConst::$cacheList['globalsetting']['globalsetting_s3_download_expired_time'],'value');
if (empty($expireTime)) {
$expireTime = 120; //单位:分钟
}
$expires = time() + $expireTime * 60;
//自定义策略
$policys = json_encode([
'Statement' => [
[
'Resource' => $bucketInfo['url'].'/*',
'Condition' => [
// 'DateGreaterThan' => ['AWS:EpochTime' => time() ],
'DateLessThan' => ['AWS:EpochTime' => $expires],
// 'IpAddress' => ['AWS:SourceIp' => \app\util\Tools::getIp()],
],
],
],
],JSON_UNESCAPED_SLASHES);

浙公网安备 33010602011771号