Golang connects to ali Cloud private Bucket to upload pictures and authorize access to pictures

1. Why private buckets

  • Public read/write: Any user on the Internet can access the files in the Bucket and write data to the Bucket. This may cause your data leakage and cost surge, if maliciously written illegal information may infringe on your legitimate rights and interests
  • Private: Only the owner of the storage space can read and write files in the storage space. Others cannot access files in the storage space

In view of the above, the company requires that buckets be made private and accessible only to authorized users

2, preparation,

2.1 Creating a RAM Account

After opening OSS account, create RAM account, use STS temporary access certificate to access OSS, refer to Ali Cloud documentation

Tip: In Step 4, grant AliyunOSSFullAccess to the RAM roleI’ve been looking for problems all day, everyoneKeep in mind thatWriting this blog is also to record my way to fill the hole

2.2 set the Bucket

  • In object storage, the Bucket list creates private buckets

  • Then in this Bucket authorization just create OSS account ID, authorization operation is full control

3, serving

3.1 Constants and their structures

  • According to their own Ali cloud information to configure
const (
	AccessKeyId     = "* *"AK / / oss account
	AccessKeySecret = "* *"ST / / oss account
	stsEndpoint     = "* *"// STS. Ali Cloud object storage address
	RoleArn         = "* *"// To create a role
	RoleSessionName = "* *"// To create a role
	BucketName      = "* *" // Enter a Bucket name, such as exampleBucket
	Endpoint        = "* *"// Address of aliCloud object storage
	UploadOssUrl    = "* *" // Return to the front-end OSS upload address
)
type StsTokenInfo struct {
	StatusCode      int    `json:"StatusCode"`
	AccessKeyId     string `json:"AccessKeyId"`
	AccessKeySecret string `json:"AccessKeySecret"`
	SecurityToken   string `json:"SecurityToken"`
	Expiration      string `json:"Expiration"`
}

type StsErrorInfo struct {
	StatusCode   int    `json:"StatusCode"`
	ErrorCode    string `json:"ErrorCode"`
	ErrorMessage string `json:"ErrorMessage"`
}
Copy the code

3.2 Obtaining STS Temporary User Information

package aliyun

import (
	"fmt"
	openapi "github.com/alibabacloud-go/darabonba-openapi/client"
	sts "github.com/alibabacloud-go/sts-20150401/client"
	"github.com/alibabacloud-go/tea/tea"
	log "github.com/sirupsen/logrus"
)
//
// GetAliyunStsInfo
// @description: Get the STS temporary user information
// @param isReturnAll
// @return *sts.AssumeRoleResponseBody
//
func GetAliyunStsInfo(a) *sts.AssumeRoleResponseBody {
	return generateStsInfo()
}

/** * Generate STS temporary user information */
func generateStsInfo(a) *sts.AssumeRoleResponseBody {
	client, _err := createClient(tea.String(AccessKeyId), tea.String(AccessKeySecret))
	if_err ! =nil {
		fmt.Print(_err.Error())
	}
	assumeRoleRequest := &sts.AssumeRoleRequest{
		RoleArn:         tea.String(RoleArn),
		RoleSessionName: tea.String(RoleSessionName),
	}
	resp, err := client.AssumeRole(assumeRoleRequest)
	iferr ! =nil {
		fmt.Print(err.Error())
	}
	fmt.Printf("Get STS temporary user information :%v", resp)
	log.Info("To obtain STS temporary user information :", resp)
	return (*resp).Body
}

/** * Initialize the account using AK&SK Client * @param accessKeyId * @param accessKeySecret * @return Client * @throws Exception */
func createClient(accessKeyId *string, accessKeySecret *string) (_result *sts.Client, _err error) {
	config := &openapi.Config{
		AccessKeyId:     accessKeyId,
		AccessKeySecret: accessKeySecret,
	}
	// The domain name to visit
	config.Endpoint = tea.String(stsEndpoint)
	_result = &sts.Client{}
	_result, _err = sts.NewClient(config)
	return _result, _err
}
Copy the code
  • I am using a new version of the SDK Ali Cloud demo, modified, specific reference documents
  • The method of obtaining STS temporary user information needs to be used in many places, so I extract it for other interfaces to call (for example, granting access requires STS temporary user information to generate signUrl).

3.3 Interface Invocation

3.3.1 STS temporary user information that does not contain the signature

  • I use gin framework here. This interface does not contain signatures (policy and Signature) required by the front end, which requires the front end to use oss plug-in (all information returned is written in another interface).
/** interface: to get the STS user front end, load the OSS plug-in. If not, call AppAliyunPolicy */ in the policy file
func AppAliyunSts(c *gin.Context) {
	response := GetAliyunStsInfo()
	c.JSON(http.StatusOK, gin.H{
		"code": 1."data": response,
	})
	return
}
Copy the code
  • API call, return information

3.3.2 STS temporary user information with signature

  • The advantage of this interface is that no extra packages need to be loaded at the front end
package aliyun

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	log "github.com/sirupsen/logrus"
	"hash"
	"io"
	"net/http"
	"time"
)
/** Signature direct upload service for small programs to upload pictures without loading library */
// The prefix specified when the user uploads the file.
//var upload_dir string = "user-dir/"

// Expiration time is 3000 seconds
var expire_time int64 = 3000

type ConfigStruct struct {
	Expiration string     `json:"expiration"`
	Conditions [][]string `json:"conditions"`
}
type PolicyToken struct {
	StsTokenInfo
	Expire       int64  `json:"expire"`
	Signature    string `json:"signature"`
	Policy       string `json:"policy"`
	Directory    string `json:"dir"`
	UploadOssUrl string `json:"uploadOssUrl"`
}

func AppAliyunPolicy(c *gin.Context) {
	uploadDir := c.DefaultQuery("dir"."user-dir/")
	/ / get the accessKeyId token2 accessKeySecret
	resp := GetAliyunStsInfo()
	log.Info(resp)

	now := time.Now().Unix()
	expire_end := now + expire_time
	var tokenExpire = getGmtIso8601(expire_end)

	//create post policy json
	var config ConfigStruct
	config.Expiration = tokenExpire
	var condition []string
	condition = append(condition, "starts-with")
	condition = append(condition, "$key")
	condition = append(condition, uploadDir)
	config.Conditions = append(config.Conditions, condition)

	//calucate signature
	result, err := json.Marshal(config)
	debyte := base64.StdEncoding.EncodeToString(result)
	h := hmac.New(func(a) hash.Hash { return sha1.New() }, []byte(*resp.Credentials.AccessKeySecret))
	io.WriteString(h, debyte)
	signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil))

	policyToken := &PolicyToken{}
	policyToken.AccessKeyId = *resp.Credentials.AccessKeyId
	policyToken.AccessKeySecret = *resp.Credentials.AccessKeySecret
	policyToken.SecurityToken = *resp.Credentials.SecurityToken
	policyToken.Expiration = *resp.Credentials.Expiration
	policyToken.Expire = expire_end
	policyToken.Signature = string(signedStr)
	policyToken.Directory = uploadDir
	policyToken.Policy = string(debyte)
	policyToken.UploadOssUrl = UploadOssUrl
	iferr ! =nil {
		fmt.Println("json err:", err)
	}
	c.JSON(http.StatusOK, gin.H{
		"code": 1."data": policyToken,
	})
	return
}

func getGmtIso8601(expireEnd int64) string {
	var tokenExpire = time.Unix(expireEnd, 0).UTC().Format("2006-01-02T15:04:05Z")
	return tokenExpire
}
Copy the code
  • The Get interface carries the dir parameter to specify which folder to put in
  • Interface call, returns information

3.4 Authorized Access

3.4.1 Interface Invocation

  • Ali Cloud provides two authorization methods, URL authorization and header authorization. I choose URL for image authorization. Images need to be authorized to carry the signature parameters can request to the specific reference document [] ([picture archived failure outside the chain, the source station might be hotlinking prevention mechanism, proposed to directly upload picture preserved (img – W41fNy75-1648959247715) (media / 16489520500281/1648956337 6848.jpg)]

)

package aliyun

import (
	"fmt"
	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	"github.com/gin-gonic/gin"
	"net/http"
)

/** Image authorization returns the signed URL */
func GetSignURL(c *gin.Context) {
	accessKeyId := c.DefaultQuery("accessKeyId"."")
	accessKeySecret := c.DefaultQuery("accessKeySecret"."")
	securityToken := c.DefaultQuery("securityToken"."")
	fullImgPath := c.DefaultQuery("fullImgPath"."") // Image full path
	// If empty, request STS temporary user information
	if accessKeyId == "" || accessKeySecret == "" || securityToken == "" {
		stsTokenInfo := GetAliyunStsInfo()
		accessKeyId = *stsTokenInfo.Credentials.AccessKeyId
		accessKeySecret = *stsTokenInfo.Credentials.AccessKeySecret
		securityToken = *stsTokenInfo.Credentials.SecurityToken
	}

	// After obtaining the STS temporary credential, you can generate OSSClient from the SecurityToken and temporary access key (AccessKeyId and AccessKeySecret) contained in it.
	client, err := oss.New(Endpoint, accessKeyId, accessKeySecret, oss.SecurityToken(securityToken))
	iferr ! =nil {
		fmt.Print(err.Error())
	}
	/ / fill in the full path to the file, such as exampledir/exampleobject. TXT. The full file path cannot contain a Bucket name.
	objectName := fullImgPath
	// Get storage space.
	bucket, err := client.Bucket(BucketName)
	iferr ! =nil {
		fmt.Print(err.Error())
	}

	// Signature direct.
	signedURL, err := bucket.SignURL(objectName, oss.HTTPGet, 6000)
	iferr ! =nil {
		fmt.Print(err.Error())
	}
	c.JSON(http.StatusOK, gin.H{
		"code": 1."data": signedURL,
	})
}

Copy the code
  • There are four parameters here. The previous parameter was obtained by the STS temporary user and passed from the front end (this scenario applies to the image echo after uploading the image, so the image URL with signature is required). If the front end does not transmit these three parameters, we need to call the previous GetAliyunStsInfo() to generate them.
  • FullImgPath parameter is the photo of the full path and fill in the full path to the file, such as exampledir/exampleobject. TXT. The full file path cannot contain a Bucket name.

3.4.2 API Call information is returned

  • If there is no authorization, it will prompt

I ran into this problem because I did not set OSS maximum permissions for RAM roles, keep in mind

  • If the expiration date has passed, it will prompt
<Error>
<Code>AccessDenied</Code>
<Message>Request has expired.</Message>
<RequestId>* * *</RequestId>
<HostId>* * * *</HostId>
<Expires>The 2022-04-02 T08:42:47. 000 z</Expires>
<ServerTime>The 2022-04-03 T03:56:52. 000 z</ServerTime>
</Error>
Copy the code

Ok, the above interface has been tested and adjusted by myself, which takes a day and a half. There is nothing wrong with the code. The problem lies in the configuration permissions