|
| 1 | +// Copyright 2020 FastWeGo |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +// you may not use this file except in compliance with the License. |
| 5 | +// You may obtain a copy of the License at |
| 6 | +// |
| 7 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +// |
| 9 | +// Unless required by applicable law or agreed to in writing, software |
| 10 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +// See the License for the specific language governing permissions and |
| 13 | +// limitations under the License. |
| 14 | + |
| 15 | +// Package oauth 代公众号发起网页授权 |
| 16 | +package oauth |
| 17 | + |
| 18 | +import ( |
| 19 | + "encoding/json" |
| 20 | + "fmt" |
| 21 | + "net/url" |
| 22 | + |
| 23 | + "github.com/fastwego/wxopen" |
| 24 | +) |
| 25 | + |
| 26 | +const ( |
| 27 | + apiGetAccessToken = "/sns/oauth2/component/access_token" |
| 28 | + apiRefreshAccessToken = "/sns/oauth2/component/refresh_token" |
| 29 | + apiGetUserInfo = "/sns/userinfo" |
| 30 | +) |
| 31 | + |
| 32 | +/* |
| 33 | +获取用户授权跳转链接 |
| 34 | +
|
| 35 | +在确保微信公众账号拥有授权作用域(scope 参数)的权限的前提下(一般而言,已微信认证的服务号拥有 snsapi_base 和 snsapi_userinfo),使用微信客户端打开以下链接(严格按照以下格式,包括顺序和大小写,并请将参数替换为实际内容) |
| 36 | +
|
| 37 | +See: https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Official_Accounts/official_account_website_authorization.html |
| 38 | +
|
| 39 | +GET https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE&component_appid=component_appid#wechat_redirect |
| 40 | +
|
| 41 | +*/ |
| 42 | +func GetAuthorizeUrl(ctx *wxopen.Platform, appid string, redirect_uri string, scope string, state string) (redirectUri string, err error) { |
| 43 | + uriTpl := "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s&component_appid=%s#wechat_redirect" |
| 44 | + redirectUri = fmt.Sprintf(uriTpl, appid, url.QueryEscape(redirect_uri), scope, state, ctx.Config.AppId) |
| 45 | + return |
| 46 | +} |
| 47 | + |
| 48 | +type AccessToken struct { |
| 49 | + AccessToken string `json:"access_token"` |
| 50 | + ExpiresIn int `json:"expires_in"` |
| 51 | + RefreshToken string `json:"refresh_token"` |
| 52 | + Openid string `json:"openid"` |
| 53 | + Scope string `json:"scope"` |
| 54 | +} |
| 55 | + |
| 56 | +/* |
| 57 | +通过code换取网页授权access_token |
| 58 | +
|
| 59 | +获取第一步的 code 后,请求以下链接获取 access_token 需要注意的是,由于安全方面的考虑,对访问该链接的客户端有 IP 白名单的要求 |
| 60 | +
|
| 61 | +See: https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Official_Accounts/official_account_website_authorization.html |
| 62 | +
|
| 63 | +GET https://api.weixin.qq.com/sns/oauth2/component/access_token?appid=APPID&code=CODE&grant_type=authorization_code&component_appid=COMPONENT_APPID&component_access_token=COMPONENT_ACCESS_TOKEN |
| 64 | +*/ |
| 65 | +func GetAccessToken(ctx *wxopen.Platform, appid string, code string) (accessToken AccessToken, err error) { |
| 66 | + |
| 67 | + params := url.Values{} |
| 68 | + params.Add("appid", appid) |
| 69 | + params.Add("code", code) |
| 70 | + params.Add("grant_type", "authorization_code") |
| 71 | + params.Add("component_appid", ctx.Config.AppId) |
| 72 | + |
| 73 | + resp, err := ctx.Client.HTTPGet(apiGetAccessToken + "?" + params.Encode()) |
| 74 | + if err != nil { |
| 75 | + return |
| 76 | + } |
| 77 | + |
| 78 | + err = json.Unmarshal(resp, &accessToken) |
| 79 | + if err != nil { |
| 80 | + return |
| 81 | + } |
| 82 | + return |
| 83 | +} |
| 84 | + |
| 85 | +/* |
| 86 | +刷新access_token |
| 87 | +
|
| 88 | +由于 access_token 拥有较短的有效期,当 access_token 超时后,可以使用 refresh_token 进行刷新,refresh_token 拥有较长的有效期(30 天),当 refresh_token 失效的后,需要用户重新授权 |
| 89 | +
|
| 90 | +See: https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Official_Accounts/official_account_website_authorization.html |
| 91 | +
|
| 92 | +GET https://api.weixin.qq.com/sns/oauth2/component/refresh_token?appid=APPID&grant_type=refresh_token&component_appid=COMPONENT_APPID&component_access_token=COMPONENT_ACCESS_TOKEN&refresh_token=REFRESH_TOKEN |
| 93 | +*/ |
| 94 | +func RefreshAccessToken(ctx *wxopen.Platform, appid string, refresh_token string) (accessToken AccessToken, err error) { |
| 95 | + params := url.Values{} |
| 96 | + params.Add("appid", appid) |
| 97 | + params.Add("refresh_token", refresh_token) |
| 98 | + params.Add("grant_type", "refresh_token") |
| 99 | + params.Add("component_appid", ctx.Config.AppId) |
| 100 | + |
| 101 | + resp, err := ctx.Client.HTTPGet(apiRefreshAccessToken + "?" + params.Encode()) |
| 102 | + if err != nil { |
| 103 | + return |
| 104 | + } |
| 105 | + |
| 106 | + err = json.Unmarshal(resp, &accessToken) |
| 107 | + if err != nil { |
| 108 | + return |
| 109 | + } |
| 110 | + return |
| 111 | + |
| 112 | +} |
| 113 | + |
| 114 | +type UserInfo struct { |
| 115 | + Openid string `json:"openid"` |
| 116 | + Nickname string `json:"nickname"` |
| 117 | + Sex int64 `json:"sex"` |
| 118 | + Province string `json:"province"` |
| 119 | + City string `json:"city"` |
| 120 | + Country string `json:"country"` |
| 121 | + Headimgurl string `json:"headimgurl"` |
| 122 | + Privilege []string `json:"privilege"` |
| 123 | + Unionid string `json:"unionid"` |
| 124 | +} |
| 125 | + |
| 126 | +/* |
| 127 | +拉取用户信息 |
| 128 | +
|
| 129 | +如果网页授权作用域为snsapi_userinfo,则此时开发者可以通过access_token和openid拉取用户信息了 |
| 130 | +
|
| 131 | +See: https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Official_Accounts/official_account_website_authorization.html |
| 132 | +
|
| 133 | +GET https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN |
| 134 | +*/ |
| 135 | +func GetUserInfo(ctx *wxopen.Platform, access_token string, openid string) (userInfo UserInfo, err error) { |
| 136 | + |
| 137 | + params := url.Values{} |
| 138 | + params.Add("access_token", access_token) |
| 139 | + params.Add("openid", openid) |
| 140 | + params.Add("lang", "zh_CN") |
| 141 | + |
| 142 | + resp, err := ctx.Client.HTTPGet(apiGetUserInfo + "?" + params.Encode()) |
| 143 | + if err != nil { |
| 144 | + return |
| 145 | + } |
| 146 | + |
| 147 | + err = json.Unmarshal(resp, &userInfo) |
| 148 | + if err != nil { |
| 149 | + return |
| 150 | + } |
| 151 | + return |
| 152 | +} |
0 commit comments