作者 tangxvhui

新增

1 package aliyun 1 package aliyun
2 2
3 -import (  
4 - "github.com/astaxie/beego"  
5 -)  
6 -  
7 /* 3 /*
8 AK AccessKey 完全访问权限 4 AK AccessKey 完全访问权限
9 STS (Security Token Service) 临时访问控制 5 STS (Security Token Service) 临时访问控制
10 */ 6 */
11 -  
12 -var (  
13 - Endpoint string = beego.AppConfig.String("end_point")  
14 - BucketName string = beego.AppConfig.String("bucket_name")  
15 - //BucketNameCdn string = beego.AppConfig.String("bucket_name_cdn")  
16 - //EndpointCdn string = beego.AppConfig.String("end_point_cdn")  
17 -)  
18 -  
19 -//type OssSts struct{  
20 -//  
21 -//}  
22 -  
23 -//func NewSTS(){  
24 -// client,_ :=oos.NewClientWithAccessKey(RegionID,AccessKeyID,AccessKeySecret)  
25 -// client.DoAction()  
26 -//} 7 +type ossConfig struct {
  8 + AccessID string
  9 + AccessKey string
  10 + RoleAcs string
  11 + EndPoint string
  12 + BuckName string
  13 + CallbackUrl string
  14 +}
  1 +package oss
  2 +
  3 +import (
  4 + "crypto/hmac"
  5 + "crypto/sha1"
  6 + "crypto/tls"
  7 + "encoding/base64"
  8 + "io/ioutil"
  9 + "net/http"
  10 + "net/url"
  11 + "time"
  12 +)
  13 +
  14 +type AliyunStsClient struct {
  15 + ChildAccountKeyId string
  16 + ChildAccountSecret string
  17 + RoleAcs string
  18 +}
  19 +
  20 +func NewStsClient(key, secret, roleAcs string) *AliyunStsClient {
  21 + return &AliyunStsClient{
  22 + ChildAccountKeyId: key,
  23 + ChildAccountSecret: secret,
  24 + RoleAcs: roleAcs,
  25 + }
  26 +}
  27 +
  28 +func (cli *AliyunStsClient) GenerateSignatureUrl(sessionName, durationSeconds string) (string, error) {
  29 + assumeUrl := "SignatureVersion=1.0"
  30 + assumeUrl += "&Format=JSON"
  31 + assumeUrl += "&Timestamp=" + url.QueryEscape(time.Now().UTC().Format("2006-01-02T15:04:05Z"))
  32 + assumeUrl += "&RoleArn=" + url.QueryEscape(cli.RoleAcs)
  33 + assumeUrl += "&RoleSessionName=" + sessionName
  34 + assumeUrl += "&AccessKeyId=" + cli.ChildAccountKeyId
  35 + assumeUrl += "&SignatureMethod=HMAC-SHA1"
  36 + assumeUrl += "&Version=2015-04-01"
  37 + assumeUrl += "&Action=AssumeRole"
  38 + assumeUrl += "&SignatureNonce=" + "TODO"
  39 + assumeUrl += "&DurationSeconds=" + durationSeconds
  40 +
  41 + // 解析成V type
  42 + signToString, err := url.ParseQuery(assumeUrl)
  43 + if err != nil {
  44 + return "", err
  45 + }
  46 +
  47 + // URL顺序化
  48 + result := signToString.Encode()
  49 +
  50 + // 拼接
  51 + StringToSign := "GET" + "&" + "%2F" + "&" + url.QueryEscape(result)
  52 +
  53 + // HMAC
  54 + hashSign := hmac.New(sha1.New, []byte(cli.ChildAccountSecret+"&"))
  55 + hashSign.Write([]byte(StringToSign))
  56 +
  57 + // 生成signature
  58 + signature := base64.StdEncoding.EncodeToString(hashSign.Sum(nil))
  59 +
  60 + // Url 添加signature
  61 + assumeUrl = "https://sts.aliyuncs.com/?" + assumeUrl + "&Signature=" + url.QueryEscape(signature)
  62 +
  63 + return assumeUrl, nil
  64 +}
  65 +
  66 +// 请求构造好的URL,获得授权信息
  67 +// 安全认证 HTTPS
  68 +func (cli *AliyunStsClient) GetStsResponse(url string) ([]byte, error) {
  69 + tr := &http.Transport{
  70 + TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
  71 + }
  72 + client := &http.Client{Transport: tr}
  73 +
  74 + resp, err := client.Get(url)
  75 + if err != nil {
  76 + return nil, err
  77 + }
  78 + defer resp.Body.Close()
  79 +
  80 + body, err := ioutil.ReadAll(resp.Body)
  81 +
  82 + return body, err
  83 +}
  1 +package oss
  2 +
  3 +import (
  4 + "crypto"
  5 + "crypto/md5"
  6 + "crypto/rsa"
  7 + "crypto/x509"
  8 + "encoding/base64"
  9 + "encoding/pem"
  10 + "errors"
  11 + "fmt"
  12 + "io/ioutil"
  13 + "net/http"
  14 + "strconv"
  15 +
  16 + "github.com/astaxie/beego"
  17 +)
  18 +
  19 +func ValidSignure(r *http.Request, requestBody []byte) bool {
  20 + if r.Method != "POST" {
  21 + beego.Error("request.Method!=post")
  22 + return false
  23 + }
  24 + // Get PublicKey bytes
  25 + bytePublicKey, err := getPublicKey(r)
  26 + if err != nil {
  27 + beego.Error(err)
  28 + return false
  29 + }
  30 + // Get Authorization bytes : decode from Base64String
  31 + byteAuthorization, err := getAuthorization(r)
  32 + if err != nil {
  33 + beego.Error(err)
  34 + return false
  35 + }
  36 + // Get MD5 bytes from Newly Constructed Authrization String.
  37 + byteMD5, err := getMD5FromNewAuthString(r, requestBody)
  38 + if err != nil {
  39 + beego.Error(err)
  40 + return false
  41 + }
  42 +
  43 + // VerifySignature and response to client
  44 + if verifySignature(bytePublicKey, byteMD5, byteAuthorization) {
  45 + return true
  46 + } else {
  47 + beego.Error("verifySignature return false")
  48 + return false
  49 + }
  50 +
  51 + return false
  52 +}
  53 +
  54 +func handlerRequest(w http.ResponseWriter, r *http.Request) {
  55 + if r.Method == "POST" {
  56 + fmt.Println("\nHandle Post Request...")
  57 +
  58 + // Get PublicKey bytes
  59 + bytePublicKey, err := getPublicKey(r)
  60 + if err != nil {
  61 + responseFailed(w)
  62 + return
  63 + }
  64 + beego.Debug("bytePublicKey:", string(bytePublicKey))
  65 + // Get Authorization bytes : decode from Base64String
  66 + byteAuthorization, err := getAuthorization(r)
  67 + if err != nil {
  68 + responseFailed(w)
  69 + return
  70 + }
  71 + beego.Debug("byteAuthorization:", string(byteAuthorization))
  72 + // Get MD5 bytes from Newly Constructed Authrization String.
  73 + byteMD5, err := getMD5FromNewAuthString(r, []byte{})
  74 + if err != nil {
  75 + responseFailed(w)
  76 + return
  77 + }
  78 +
  79 + // VerifySignature and response to client
  80 + if verifySignature(bytePublicKey, byteMD5, byteAuthorization) {
  81 +
  82 + // Do something you want accoding to callback_body ...
  83 +
  84 + // response OK : 200
  85 + responseSuccess(w)
  86 + } else {
  87 + // response FAILED : 400
  88 + responseFailed(w)
  89 + }
  90 + }
  91 +}
  92 +
  93 +// getPublicKey : Get PublicKey bytes from Request.URL
  94 +func getPublicKey(r *http.Request) ([]byte, error) {
  95 + var bytePublicKey []byte
  96 +
  97 + // get PublicKey URL
  98 + publicKeyURLBase64 := r.Header.Get("x-oss-pub-key-url")
  99 + if publicKeyURLBase64 == "" {
  100 + fmt.Println("GetPublicKey from Request header failed : No x-oss-pub-key-url field. ")
  101 + return bytePublicKey, errors.New("no x-oss-pub-key-url field in Request header ")
  102 + }
  103 + publicKeyURL, _ := base64.StdEncoding.DecodeString(publicKeyURLBase64)
  104 + // get PublicKey Content from URL
  105 + responsePublicKeyURL, err := http.Get(string(publicKeyURL))
  106 + if err != nil {
  107 + fmt.Printf("Get PublicKey Content from URL failed : %s \n", err.Error())
  108 + return bytePublicKey, err
  109 + }
  110 + defer responsePublicKeyURL.Body.Close()
  111 + bytePublicKey, err = ioutil.ReadAll(responsePublicKeyURL.Body)
  112 + if err != nil {
  113 + fmt.Printf("Read PublicKey Content from URL failed : %s \n", err.Error())
  114 + return bytePublicKey, err
  115 + }
  116 +
  117 + return bytePublicKey, nil
  118 +}
  119 +
  120 +// getAuthorization : decode from Base64String
  121 +func getAuthorization(r *http.Request) ([]byte, error) {
  122 + var byteAuthorization []byte
  123 +
  124 + strAuthorizationBase64 := r.Header.Get("authorization")
  125 + if strAuthorizationBase64 == "" {
  126 + fmt.Println("Failed to get authorization field from request header. ")
  127 + return byteAuthorization, errors.New("no authorization field in Request header")
  128 + }
  129 + byteAuthorization, _ = base64.StdEncoding.DecodeString(strAuthorizationBase64)
  130 +
  131 + return byteAuthorization, nil
  132 +}
  133 +
  134 +// getMD5FromNewAuthString : Get MD5 bytes from Newly Constructed Authrization String.
  135 +func getMD5FromNewAuthString(r *http.Request, requestBody []byte) ([]byte, error) {
  136 + var byteMD5 []byte
  137 + // Construct the New Auth String from URI+Query+Body
  138 + _, err := ioutil.ReadAll(r.Body)
  139 + r.Body.Close()
  140 + if err != nil {
  141 + fmt.Printf("Read Request Body failed : %s \n", err.Error())
  142 + return byteMD5, err
  143 + }
  144 + //strCallbackBody := string(bodyContent)
  145 + strCallbackBody := string(requestBody)
  146 + strURLPathDecode, errUnescape := unescapePath(r.URL.Path, encodePathSegment)
  147 + if errUnescape != nil {
  148 + fmt.Printf("url.PathUnescape failed : URL.Path=%s, error=%s \n", r.URL.Path, err.Error())
  149 + return byteMD5, errUnescape
  150 + }
  151 +
  152 + // Generate New Auth String prepare for MD5
  153 + strAuth := ""
  154 + if r.URL.RawQuery == "" {
  155 + strAuth = fmt.Sprintf("%s\n%s", strURLPathDecode, strCallbackBody)
  156 + } else {
  157 + strAuth = fmt.Sprintf("%s?%s\n%s", strURLPathDecode, r.URL.RawQuery, strCallbackBody)
  158 + }
  159 + // Generate MD5 from the New Auth String
  160 + md5Ctx := md5.New()
  161 + md5Ctx.Write([]byte(strAuth))
  162 + byteMD5 = md5Ctx.Sum(nil)
  163 +
  164 + return byteMD5, nil
  165 +}
  166 +
  167 +// verifySignature
  168 +func verifySignature(bytePublicKey []byte, byteMd5 []byte, authorization []byte) bool {
  169 + pubBlock, _ := pem.Decode(bytePublicKey)
  170 + if pubBlock == nil {
  171 + beego.Error("Failed to parse PEM block containing the public key")
  172 + return false
  173 + }
  174 + pubInterface, err := x509.ParsePKIXPublicKey(pubBlock.Bytes)
  175 + if (pubInterface == nil) || (err != nil) {
  176 + beego.Error(fmt.Sprintf("x509.ParsePKIXPublicKey(publicKey) failed : %s \n", err.Error()))
  177 + return false
  178 + }
  179 + pub := pubInterface.(*rsa.PublicKey)
  180 +
  181 + errorVerifyPKCS1v15 := rsa.VerifyPKCS1v15(pub, crypto.MD5, byteMd5, authorization)
  182 + if errorVerifyPKCS1v15 != nil {
  183 + beego.Error(fmt.Sprintf("Signature Verification is Failed : %s \n", errorVerifyPKCS1v15.Error()))
  184 + return false
  185 + }
  186 +
  187 + fmt.Printf("Signature Verification is Successful. \n")
  188 + return true
  189 +}
  190 +
  191 +// responseSuccess : Response 200 to client
  192 +func responseSuccess(w http.ResponseWriter) {
  193 + strResponseBody := "{\"Status\":\"OK\"}"
  194 + w.Header().Set("Content-Type", "application/json")
  195 + w.Header().Set("Content-Length", strconv.Itoa(len(strResponseBody)))
  196 + w.WriteHeader(http.StatusOK)
  197 + w.Write([]byte(strResponseBody))
  198 + fmt.Printf("\nPost Response : 200 OK . \n")
  199 +}
  200 +
  201 +// responseFailed : Response 400 to client
  202 +func responseFailed(w http.ResponseWriter) {
  203 + w.WriteHeader(http.StatusBadRequest)
  204 + fmt.Printf("\nPost Response : 400 BAD . \n")
  205 +}
  206 +
  207 +func printByteArray(byteArrary []byte, arrName string) {
  208 + fmt.Printf("printByteArray : ArrayName=%s, ArrayLength=%d \n", arrName, len(byteArrary))
  209 + for i := 0; i < len(byteArrary); i++ {
  210 + fmt.Printf("%02x", byteArrary[i])
  211 + }
  212 + fmt.Printf("printByteArray : End . \n")
  213 +}
  214 +
  215 +// EscapeError Escape Error
  216 +type EscapeError string
  217 +
  218 +func (e EscapeError) Error() string {
  219 + return "invalid URL escape " + strconv.Quote(string(e))
  220 +}
  221 +
  222 +// InvalidHostError Invalid Host Error
  223 +type InvalidHostError string
  224 +
  225 +func (e InvalidHostError) Error() string {
  226 + return "invalid character " + strconv.Quote(string(e)) + " in host name"
  227 +}
  228 +
  229 +type encoding int
  230 +
  231 +const (
  232 + encodePath encoding = 1 + iota
  233 + encodePathSegment
  234 + encodeHost
  235 + encodeZone
  236 + encodeUserPassword
  237 + encodeQueryComponent
  238 + encodeFragment
  239 +)
  240 +
  241 +// unescapePath : unescapes a string; the mode specifies, which section of the URL string is being unescaped.
  242 +func unescapePath(s string, mode encoding) (string, error) {
  243 +
  244 + // Count %, check that they're well-formed.
  245 + mode = encodePathSegment
  246 + n := 0
  247 + hasPlus := false
  248 + for i := 0; i < len(s); {
  249 + switch s[i] {
  250 + case '%':
  251 + n++
  252 + if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
  253 + s = s[i:]
  254 + if len(s) > 3 {
  255 + s = s[:3]
  256 + }
  257 + return "", EscapeError(s)
  258 + }
  259 + // Per https://tools.ietf.org/html/rfc3986#page-21
  260 + // in the host component %-encoding can only be used
  261 + // for non-ASCII bytes.
  262 + // But https://tools.ietf.org/html/rfc6874#section-2
  263 + // introduces %25 being allowed to escape a percent sign
  264 + // in IPv6 scoped-address literals. Yay.
  265 + if mode == encodeHost && unhex(s[i+1]) < 8 && s[i:i+3] != "%25" {
  266 + return "", EscapeError(s[i : i+3])
  267 + }
  268 + if mode == encodeZone {
  269 + // RFC 6874 says basically "anything goes" for zone identifiers
  270 + // and that even non-ASCII can be redundantly escaped,
  271 + // but it seems prudent to restrict %-escaped bytes here to those
  272 + // that are valid host name bytes in their unescaped form.
  273 + // That is, you can use escaping in the zone identifier but not
  274 + // to introduce bytes you couldn't just write directly.
  275 + // But Windows puts spaces here! Yay.
  276 + v := unhex(s[i+1])<<4 | unhex(s[i+2])
  277 + if s[i:i+3] != "%25" && v != ' ' && shouldEscape(v, encodeHost) {
  278 + return "", EscapeError(s[i : i+3])
  279 + }
  280 + }
  281 + i += 3
  282 + case '+':
  283 + hasPlus = mode == encodeQueryComponent
  284 + i++
  285 + default:
  286 + if (mode == encodeHost || mode == encodeZone) && s[i] < 0x80 && shouldEscape(s[i], mode) {
  287 + return "", InvalidHostError(s[i : i+1])
  288 + }
  289 + i++
  290 + }
  291 + }
  292 +
  293 + if n == 0 && !hasPlus {
  294 + return s, nil
  295 + }
  296 +
  297 + t := make([]byte, len(s)-2*n)
  298 + j := 0
  299 + for i := 0; i < len(s); {
  300 + switch s[i] {
  301 + case '%':
  302 + t[j] = unhex(s[i+1])<<4 | unhex(s[i+2])
  303 + j++
  304 + i += 3
  305 + case '+':
  306 + if mode == encodeQueryComponent {
  307 + t[j] = ' '
  308 + } else {
  309 + t[j] = '+'
  310 + }
  311 + j++
  312 + i++
  313 + default:
  314 + t[j] = s[i]
  315 + j++
  316 + i++
  317 + }
  318 + }
  319 + return string(t), nil
  320 +}
  321 +
  322 +// Please be informed that for now shouldEscape does not check all
  323 +// reserved characters correctly. See golang.org/issue/5684.
  324 +func shouldEscape(c byte, mode encoding) bool {
  325 + // §2.3 Unreserved characters (alphanum)
  326 + if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' {
  327 + return false
  328 + }
  329 +
  330 + if mode == encodeHost || mode == encodeZone {
  331 + // §3.2.2 Host allows
  332 + // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
  333 + // as part of reg-name.
  334 + // We add : because we include :port as part of host.
  335 + // We add [ ] because we include [ipv6]:port as part of host.
  336 + // We add < > because they're the only characters left that
  337 + // we could possibly allow, and Parse will reject them if we
  338 + // escape them (because hosts can't use %-encoding for
  339 + // ASCII bytes).
  340 + switch c {
  341 + case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', ':', '[', ']', '<', '>', '"':
  342 + return false
  343 + }
  344 + }
  345 +
  346 + switch c {
  347 + case '-', '_', '.', '~': // §2.3 Unreserved characters (mark)
  348 + return false
  349 +
  350 + case '$', '&', '+', ',', '/', ':', ';', '=', '?', '@': // §2.2 Reserved characters (reserved)
  351 + // Different sections of the URL allow a few of
  352 + // the reserved characters to appear unescaped.
  353 + switch mode {
  354 + case encodePath: // §3.3
  355 + // The RFC allows : @ & = + $ but saves / ; , for assigning
  356 + // meaning to individual path segments. This package
  357 + // only manipulates the path as a whole, so we allow those
  358 + // last three as well. That leaves only ? to escape.
  359 + return c == '?'
  360 +
  361 + case encodePathSegment: // §3.3
  362 + // The RFC allows : @ & = + $ but saves / ; , for assigning
  363 + // meaning to individual path segments.
  364 + return c == '/' || c == ';' || c == ',' || c == '?'
  365 +
  366 + case encodeUserPassword: // §3.2.1
  367 + // The RFC allows ';', ':', '&', '=', '+', '$', and ',' in
  368 + // userinfo, so we must escape only '@', '/', and '?'.
  369 + // The parsing of userinfo treats ':' as special so we must escape
  370 + // that too.
  371 + return c == '@' || c == '/' || c == '?' || c == ':'
  372 +
  373 + case encodeQueryComponent: // §3.4
  374 + // The RFC reserves (so we must escape) everything.
  375 + return true
  376 +
  377 + case encodeFragment: // §4.1
  378 + // The RFC text is silent but the grammar allows
  379 + // everything, so escape nothing.
  380 + return false
  381 + }
  382 + }
  383 +
  384 + // Everything else must be escaped.
  385 + return true
  386 +}
  387 +
  388 +func ishex(c byte) bool {
  389 + switch {
  390 + case '0' <= c && c <= '9':
  391 + return true
  392 + case 'a' <= c && c <= 'f':
  393 + return true
  394 + case 'A' <= c && c <= 'F':
  395 + return true
  396 + }
  397 + return false
  398 +}
  399 +
  400 +func unhex(c byte) byte {
  401 + switch {
  402 + case '0' <= c && c <= '9':
  403 + return c - '0'
  404 + case 'a' <= c && c <= 'f':
  405 + return c - 'a' + 10
  406 + case 'A' <= c && c <= 'F':
  407 + return c - 'A' + 10
  408 + }
  409 + return 0
  410 +}