credential.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. package tos
  2. import (
  3. "sync/atomic"
  4. "time"
  5. "unsafe"
  6. "golang.org/x/sync/singleflight"
  7. )
  8. type Credential struct {
  9. AccessKeyID string
  10. AccessKeySecret string
  11. SecurityToken string
  12. }
  13. // Credentials provides Credential
  14. type Credentials interface {
  15. Credential() Credential
  16. }
  17. // StaticCredentials Credentials with static access-key and secret-key
  18. type StaticCredentials struct {
  19. accessKey string
  20. secretKey string
  21. securityToken string
  22. }
  23. // NewStaticCredentials Credentials with static access-key and secret-key
  24. // use StaticCredentials.WithSecurityToken to set security-token
  25. //
  26. // you can use it as:
  27. // client, err := tos.NewClient(endpoint, tos.WithCredentials(tos.NewStaticCredentials(accessKey, secretKey)))
  28. // // do something more
  29. //
  30. // And you can use tos.WithPerRequestSigner set the 'Signer' for each request.
  31. //
  32. func NewStaticCredentials(accessKeyID, accessKeySecret string) *StaticCredentials {
  33. return &StaticCredentials{
  34. accessKey: accessKeyID,
  35. secretKey: accessKeySecret,
  36. }
  37. }
  38. // WithSecurityToken set security-token
  39. func (sc *StaticCredentials) WithSecurityToken(securityToken string) {
  40. sc.securityToken = securityToken
  41. }
  42. func (sc *StaticCredentials) Credential() Credential {
  43. return Credential{
  44. AccessKeyID: sc.accessKey,
  45. AccessKeySecret: sc.secretKey,
  46. SecurityToken: sc.securityToken,
  47. }
  48. }
  49. // WithoutSecretKeyCredentials Credentials with static access-key and no secret-key
  50. //
  51. // If you don't want to use secret-key directly, but use signed-key, you can use it as:
  52. // signer := tos.NewSignV4(tos.NewWithoutSecretKeyCredentials(accessKey), region)
  53. // signer.WithSigningKey(func(*SigningKeyInfo) []byte { return signingKey})
  54. // client, err := tos.NewClient(endpoint, tos.WithSigner(signer))
  55. // // do something more
  56. //
  57. // And you can use tos.WithPerRequestSigner set the 'Signer' for each request.
  58. //
  59. type WithoutSecretKeyCredentials struct {
  60. accessKey string
  61. securityToken string
  62. }
  63. func NewWithoutSecretKeyCredentials(accessKeyID string) *WithoutSecretKeyCredentials {
  64. return &WithoutSecretKeyCredentials{
  65. accessKey: accessKeyID,
  66. securityToken: "",
  67. }
  68. }
  69. // WithSecurityToken set security-token
  70. func (sc *WithoutSecretKeyCredentials) WithSecurityToken(securityToken string) {
  71. sc.securityToken = securityToken
  72. }
  73. func (sc *WithoutSecretKeyCredentials) Credential() Credential {
  74. return Credential{
  75. AccessKeyID: sc.accessKey,
  76. SecurityToken: sc.securityToken,
  77. }
  78. }
  79. // FederationToken contains Credential and Credential's expiration time
  80. type FederationToken struct {
  81. Credential Credential
  82. Expiration time.Time
  83. }
  84. // FederationTokenProvider provides FederationToken
  85. type FederationTokenProvider interface {
  86. FederationToken() (*FederationToken, error)
  87. }
  88. // FederationCredentials implements Credentials interfaces with flushing Credential periodically
  89. type FederationCredentials struct {
  90. cachedToken *FederationToken
  91. refreshing uint32
  92. preFetch time.Duration
  93. tokenProvider FederationTokenProvider
  94. flight singleflight.Group
  95. }
  96. // NewFederationCredentials FederationCredentials implements Credentials interfaces with flushing Credential periodically
  97. //
  98. // use WithPreFetch set prefetch time
  99. func NewFederationCredentials(tokenProvider FederationTokenProvider) (*FederationCredentials, error) {
  100. cred, err := tokenProvider.FederationToken()
  101. if err != nil {
  102. return nil, err
  103. }
  104. return &FederationCredentials{
  105. cachedToken: cred,
  106. preFetch: 5 * time.Minute,
  107. tokenProvider: tokenProvider,
  108. }, nil
  109. }
  110. // WithPreFetch set prefetch time
  111. func (fc *FederationCredentials) WithPreFetch(preFetch time.Duration) {
  112. fc.preFetch = preFetch
  113. }
  114. func (fc *FederationCredentials) token() *FederationToken {
  115. return (*FederationToken)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&fc.cachedToken))))
  116. }
  117. // Credential for Credentials interface
  118. func (fc *FederationCredentials) Credential() Credential {
  119. now := time.Now()
  120. if token := fc.token(); now.After(token.Expiration) { // 已经过期
  121. _, _, _ = fc.flight.Do("flushing", func() (interface{}, error) {
  122. flushed, err := fc.tokenProvider.FederationToken()
  123. if err != nil {
  124. return nil, err
  125. }
  126. atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&fc.cachedToken)), unsafe.Pointer(flushed))
  127. return flushed, nil
  128. })
  129. } else if now.Add(fc.preFetch).After(token.Expiration) &&
  130. atomic.LoadUint32(&fc.refreshing) == 0 {
  131. // 将要过期, prefetch token
  132. if atomic.CompareAndSwapUint32(&fc.refreshing, 0, 1) {
  133. defer atomic.StoreUint32(&fc.refreshing, 0)
  134. if newToken, err := fc.tokenProvider.FederationToken(); err == nil {
  135. atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&fc.cachedToken)), unsafe.Pointer(newToken))
  136. }
  137. }
  138. }
  139. return fc.token().Credential
  140. }