89 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			89 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			Go
		
	
	
package oss
 | 
						||
 | 
						||
import (
 | 
						||
	"crypto"
 | 
						||
	"crypto/md5"
 | 
						||
	"crypto/rsa"
 | 
						||
	"crypto/x509"
 | 
						||
	"encoding/base64"
 | 
						||
	"encoding/pem"
 | 
						||
	"errors"
 | 
						||
	"io/ioutil"
 | 
						||
	"net/http"
 | 
						||
	"regexp"
 | 
						||
	"strings"
 | 
						||
	"sync"
 | 
						||
)
 | 
						||
 | 
						||
type authenticationType struct {
 | 
						||
	lock        *sync.RWMutex
 | 
						||
	certificate map[string]*rsa.PublicKey
 | 
						||
}
 | 
						||
 | 
						||
var (
 | 
						||
	authentication = authenticationType{lock: &sync.RWMutex{}, certificate: map[string]*rsa.PublicKey{}}
 | 
						||
	urlReg         = regexp.MustCompile(`^http(|s)://gosspublic.alicdn.com/[0-9a-zA-Z]`)
 | 
						||
)
 | 
						||
 | 
						||
//验证OSS向业务服务器发来的回调函数。
 | 
						||
//该方法是并发安全的
 | 
						||
//pubKeyUrl 回调请求头中[x-oss-pub-key-url]一项,以Base64编码
 | 
						||
//reqUrl oss所发来请求的url,由path+query组成
 | 
						||
//reqBody oss所发来请求的body
 | 
						||
//authorization authorization为回调头中的签名
 | 
						||
func AuthenticateCallBack(pubKeyUrl, reqUrl, reqBody, authorization string) error {
 | 
						||
	//获取证书url
 | 
						||
	keyURL, err := base64.URLEncoding.DecodeString(pubKeyUrl)
 | 
						||
	if err != nil {
 | 
						||
		return err
 | 
						||
	}
 | 
						||
	url := string(keyURL)
 | 
						||
	//判断证书是否来自于阿里云
 | 
						||
	if !urlReg.Match(keyURL) {
 | 
						||
		return errors.New("certificate address error")
 | 
						||
	}
 | 
						||
	//获取文件名
 | 
						||
	rs := []rune(url)
 | 
						||
	filename := string(rs[strings.LastIndex(url, "/") : len(rs)-1])
 | 
						||
	authentication.lock.RLock()
 | 
						||
	certificate := authentication.certificate[filename]
 | 
						||
	authentication.lock.RUnlock()
 | 
						||
	//内存中没有证书,下载
 | 
						||
	if certificate == nil {
 | 
						||
		authentication.lock.Lock()
 | 
						||
		res, err := http.Get(url)
 | 
						||
		if err != nil {
 | 
						||
			return err
 | 
						||
		}
 | 
						||
		defer res.Body.Close()
 | 
						||
		body, err := ioutil.ReadAll(res.Body)
 | 
						||
		if err != nil {
 | 
						||
			return err
 | 
						||
		}
 | 
						||
		block, _ := pem.Decode(body)
 | 
						||
		if block == nil {
 | 
						||
			return errors.New("certificate error")
 | 
						||
		}
 | 
						||
		pubKey, err := x509.ParsePKIXPublicKey(block.Bytes)
 | 
						||
		if err != nil {
 | 
						||
			return err
 | 
						||
		}
 | 
						||
		certificate = pubKey.(*rsa.PublicKey)
 | 
						||
		authentication.certificate[filename] = certificate
 | 
						||
		authentication.lock.Unlock()
 | 
						||
	}
 | 
						||
	//证书准备完毕,开始验证
 | 
						||
	//解析签名
 | 
						||
	signature, err := base64.StdEncoding.DecodeString(authorization)
 | 
						||
	if err != nil {
 | 
						||
		return err
 | 
						||
	}
 | 
						||
	hashed := md5.New()
 | 
						||
	hashed.Write([]byte(reqUrl + "\n" + reqBody))
 | 
						||
	if err := rsa.VerifyPKCS1v15(certificate, crypto.MD5, hashed.Sum(nil), signature); err != nil {
 | 
						||
		return err
 | 
						||
	}
 | 
						||
	//验证通过
 | 
						||
	return nil
 | 
						||
}
 |