oauth.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. package oauth
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/http"
  6. "net/url"
  7. "github.com/silenceper/wechat/context"
  8. "github.com/silenceper/wechat/util"
  9. )
  10. const (
  11. redirectOauthURL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect"
  12. webAppRedirectOauthURL = "https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect"
  13. accessTokenURL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code"
  14. refreshAccessTokenURL = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=%s&grant_type=refresh_token&refresh_token=%s"
  15. userInfoURL = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN"
  16. checkAccessTokenURL = "https://api.weixin.qq.com/sns/auth?access_token=%s&openid=%s"
  17. )
  18. //Oauth 保存用户授权信息
  19. type Oauth struct {
  20. *context.Context
  21. }
  22. //NewOauth 实例化授权信息
  23. func NewOauth(context *context.Context) *Oauth {
  24. auth := new(Oauth)
  25. auth.Context = context
  26. return auth
  27. }
  28. //GetRedirectURL 获取跳转的url地址
  29. func (oauth *Oauth) GetRedirectURL(redirectURI, scope, state string) (string, error) {
  30. //url encode
  31. urlStr := url.QueryEscape(redirectURI)
  32. return fmt.Sprintf(redirectOauthURL, oauth.AppID, urlStr, scope, state), nil
  33. }
  34. //GetWebAppRedirectURL 获取网页应用跳转的url地址
  35. func (oauth *Oauth) GetWebAppRedirectURL(redirectURI, scope, state string) (string, error) {
  36. urlStr := url.QueryEscape(redirectURI)
  37. return fmt.Sprintf(webAppRedirectOauthURL, oauth.AppID, urlStr, scope, state), nil
  38. }
  39. //Redirect 跳转到网页授权
  40. func (oauth *Oauth) Redirect(writer http.ResponseWriter, req *http.Request, redirectURI, scope, state string) error {
  41. location, err := oauth.GetRedirectURL(redirectURI, scope, state)
  42. if err != nil {
  43. return err
  44. }
  45. http.Redirect(writer, req, location, 302)
  46. return nil
  47. }
  48. // ResAccessToken 获取用户授权access_token的返回结果
  49. type ResAccessToken struct {
  50. util.CommonError
  51. AccessToken string `json:"access_token"`
  52. ExpiresIn int64 `json:"expires_in"`
  53. RefreshToken string `json:"refresh_token"`
  54. OpenID string `json:"openid"`
  55. Scope string `json:"scope"`
  56. // UnionID 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。
  57. // 公众号文档 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
  58. UnionID string `json:"unionid"`
  59. }
  60. // GetUserAccessToken 通过网页授权的code 换取access_token(区别于context中的access_token)
  61. func (oauth *Oauth) GetUserAccessToken(code string) (result ResAccessToken, err error) {
  62. urlStr := fmt.Sprintf(accessTokenURL, oauth.AppID, oauth.AppSecret, code)
  63. var response []byte
  64. response, err = util.HTTPGet(urlStr)
  65. if err != nil {
  66. return
  67. }
  68. err = json.Unmarshal(response, &result)
  69. if err != nil {
  70. return
  71. }
  72. if result.ErrCode != 0 {
  73. err = fmt.Errorf("GetUserAccessToken error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg)
  74. return
  75. }
  76. return
  77. }
  78. //RefreshAccessToken 刷新access_token
  79. func (oauth *Oauth) RefreshAccessToken(refreshToken string) (result ResAccessToken, err error) {
  80. urlStr := fmt.Sprintf(refreshAccessTokenURL, oauth.AppID, refreshToken)
  81. var response []byte
  82. response, err = util.HTTPGet(urlStr)
  83. if err != nil {
  84. return
  85. }
  86. err = json.Unmarshal(response, &result)
  87. if err != nil {
  88. return
  89. }
  90. if result.ErrCode != 0 {
  91. err = fmt.Errorf("GetUserAccessToken error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg)
  92. return
  93. }
  94. return
  95. }
  96. //CheckAccessToken 检验access_token是否有效
  97. func (oauth *Oauth) CheckAccessToken(accessToken, openID string) (b bool, err error) {
  98. urlStr := fmt.Sprintf(checkAccessTokenURL, accessToken, openID)
  99. var response []byte
  100. response, err = util.HTTPGet(urlStr)
  101. if err != nil {
  102. return
  103. }
  104. var result util.CommonError
  105. err = json.Unmarshal(response, &result)
  106. if err != nil {
  107. return
  108. }
  109. if result.ErrCode != 0 {
  110. b = false
  111. return
  112. }
  113. b = true
  114. return
  115. }
  116. //UserInfo 用户授权获取到用户信息
  117. type UserInfo struct {
  118. util.CommonError
  119. OpenID string `json:"openid"`
  120. Nickname string `json:"nickname"`
  121. Sex int32 `json:"sex"`
  122. Province string `json:"province"`
  123. City string `json:"city"`
  124. Country string `json:"country"`
  125. HeadImgURL string `json:"headimgurl"`
  126. Privilege []string `json:"privilege"`
  127. Unionid string `json:"unionid"`
  128. }
  129. //GetUserInfo 如果scope为 snsapi_userinfo 则可以通过此方法获取到用户基本信息
  130. func (oauth *Oauth) GetUserInfo(accessToken, openID string) (result UserInfo, err error) {
  131. urlStr := fmt.Sprintf(userInfoURL, accessToken, openID)
  132. var response []byte
  133. response, err = util.HTTPGet(urlStr)
  134. if err != nil {
  135. return
  136. }
  137. err = json.Unmarshal(response, &result)
  138. if err != nil {
  139. return
  140. }
  141. if result.ErrCode != 0 {
  142. err = fmt.Errorf("GetUserInfo error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg)
  143. return
  144. }
  145. return
  146. }