oauth2

package
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 8, 2020 License: Apache-2.0 Imports: 9 Imported by: 0

README

微信网页授权

package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"

	"github.com/chanxuehong/rand"
	"github.com/chanxuehong/session"
	"github.com/chanxuehong/sid"
	mpoauth2 "github.com/chanxuehong/wechat/mp/oauth2"
	"github.com/chanxuehong/wechat/oauth2"
)

const (
	wxAppId           = "APPID"                           // 填上自己的参数
	wxAppSecret       = "APPSECRET"                       // 填上自己的参数
	oauth2RedirectURI = "http://192.168.1.129:8080/page2" // 填上自己的参数
	oauth2Scope       = "snsapi_userinfo"                 // 填上自己的参数
)

var (
	sessionStorage                 = session.New(20*60, 60*60)
	oauth2Endpoint oauth2.Endpoint = mpoauth2.NewEndpoint(wxAppId, wxAppSecret)
)

func init() {
	http.HandleFunc("/page1", Page1Handler)
	http.HandleFunc("/page2", Page2Handler)
}

// 建立必要的 session, 然后跳转到授权页面
func Page1Handler(w http.ResponseWriter, r *http.Request) {
	sid := sid.New()
	state := string(rand.NewHex())

	if err := sessionStorage.Add(sid, state); err != nil {
		io.WriteString(w, err.Error())
		log.Println(err)
		return
	}

	cookie := http.Cookie{
		Name:     "sid",
		Value:    sid,
		HttpOnly: true,
	}
	http.SetCookie(w, &cookie)

	AuthCodeURL := mpoauth2.AuthCodeURL(wxAppId, oauth2RedirectURI, oauth2Scope, state)
	log.Println("AuthCodeURL:", AuthCodeURL)

	http.Redirect(w, r, AuthCodeURL, http.StatusFound)
}

// 授权后回调页面
func Page2Handler(w http.ResponseWriter, r *http.Request) {
	log.Println(r.RequestURI)

	cookie, err := r.Cookie("sid")
	if err != nil {
		io.WriteString(w, err.Error())
		log.Println(err)
		return
	}

	session, err := sessionStorage.Get(cookie.Value)
	if err != nil {
		io.WriteString(w, err.Error())
		log.Println(err)
		return
	}

	savedState := session.(string) // 一般是要序列化的, 这里保存在内存所以可以这么做

	queryValues, err := url.ParseQuery(r.URL.RawQuery)
	if err != nil {
		io.WriteString(w, err.Error())
		log.Println(err)
		return
	}

	code := queryValues.Get("code")
	if code == "" {
		log.Println("用户禁止授权")
		return
	}

	queryState := queryValues.Get("state")
	if queryState == "" {
		log.Println("state 参数为空")
		return
	}
	if savedState != queryState {
		str := fmt.Sprintf("state 不匹配, session 中的为 %q, url 传递过来的是 %q", savedState, queryState)
		io.WriteString(w, str)
		log.Println(str)
		return
	}

	oauth2Client := oauth2.Client{
		Endpoint: oauth2Endpoint,
	}
	token, err := oauth2Client.ExchangeToken(code)
	if err != nil {
		io.WriteString(w, err.Error())
		log.Println(err)
		return
	}
	log.Printf("token: %+v\r\n", token)

	userinfo, err := mpoauth2.GetUserInfo(token.AccessToken, token.OpenId, "", nil)
	if err != nil {
		io.WriteString(w, err.Error())
		log.Println(err)
		return
	}

	json.NewEncoder(w).Encode(userinfo)
	log.Printf("userinfo: %+v\r\n", userinfo)
	return
}

func main() {
	fmt.Println(http.ListenAndServe(":8080", nil))
}
上面的程序基本上会打印下面的内容
2016/03/11 14:44:10 AuthCodeURL: https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=http%3A%2F%2F192.168.1.129%3A8080%2Fpage2&response_type=code&scope=snsapi_userinfo&state=12fa275bac6998ba8f89a5baf13f93a0#wechat_redirect
2016/03/11 14:44:12 /page2?code=001e44d3e2972606638027b31e61a8dH&state=12fa275bac6998ba8f89a5baf13f93a0
2016/03/11 14:44:12 [WECHAT_DEBUG] [API] GET https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=APPSECRET&code=001e44d3e2972606638027b31e61a8dH&grant_type=authorization_code
2016/03/11 14:44:13 [WECHAT_DEBUG] [API] http response body:
{"access_token":"OezXcEiiBSKSxW0eoylIeBXr5MlyOHqjC6Db82eo0Txphdi2X9hT8lI2PqjpL0rCFVQjLdSs4v2GQXE8BcLRz1hCQHL6wWXl9013zYMOAvE1UGCV4q-xAIlRVuDa85Mqnqw9himuhlgFUP2Kn0qcFg","expires_in":7200,"refresh_token":"OezXcEiiBSKSxW0eoylIeBXr5MlyOHqjC6Db82eo0Txphdi2X9hT8lI2PqjpL0rCZP99UMCpbrq8v2TWR7uxK5fb0ekmVFl9L1kUOsh1mjQy6rhQG5sGqFBKrkzPr9KQbjTxrvFtscFPmCOMuKi9EQ","openid":"os-IKuHd9pJ6xsn4mS7GyL4HxqI4","scope":"snsapi_userinfo"}
2016/03/11 14:44:13 token: &{AccessToken:OezXcEiiBSKSxW0eoylIeBXr5MlyOHqjC6Db82eo0Txphdi2X9hT8lI2PqjpL0rCFVQjLdSs4v2GQXE8BcLRz1hCQHL6wWXl9013zYMOAvE1UGCV4q-xAIlRVuDa85Mqnqw9himuhlgFUP2Kn0qcFg CreatedAt:1457678653 ExpiresIn:6000 RefreshToken:OezXcEiiBSKSxW0eoylIeBXr5MlyOHqjC6Db82eo0Txphdi2X9hT8lI2PqjpL0rCZP99UMCpbrq8v2TWR7uxK5fb0ekmVFl9L1kUOsh1mjQy6rhQG5sGqFBKrkzPr9KQbjTxrvFtscFPmCOMuKi9EQ OpenId:os-IKuHd9pJ6xsn4mS7GyL4HxqI4 UnionId: Scope:snsapi_userinfo}
2016/03/11 14:44:13 [WECHAT_DEBUG] [API] GET https://api.weixin.qq.com/sns/userinfo?access_token=OezXcEiiBSKSxW0eoylIeBXr5MlyOHqjC6Db82eo0Txphdi2X9hT8lI2PqjpL0rCFVQjLdSs4v2GQXE8BcLRz1hCQHL6wWXl9013zYMOAvE1UGCV4q-xAIlRVuDa85Mqnqw9himuhlgFUP2Kn0qcFg&openid=os-IKuHd9pJ6xsn4mS7GyL4HxqI4&lang=zh_CN
2016/03/11 14:44:13 [WECHAT_DEBUG] [API] http response body:
{"openid":"os-IKuHd9pJ6xsn4mS7GyL4HxqI4","nickname":"产学红","sex":1,"language":"zh_CN","city":"安庆","province":"安徽","country":"中国","headimgurl":"http:\/\/wx.qlogo.cn\/mmopen\/O1HUibMqqHXduhNiagwbE0m4zgJU2YbFkyZPG6VoH8IP2wEdFuWcnjUtrXHNl1OmCsoffYBBnkC0cy1yfsOibcenaAn2SeRNKYw\/0","privilege":[]}
2016/03/11 14:44:13 userinfo: &{OpenId:os-IKuHd9pJ6xsn4mS7GyL4HxqI4 Nickname:产学红 Sex:1 City:安庆 Province:安徽 Country:中国 HeadImageURL:http://wx.qlogo.cn/mmopen/O1HUibMqqHXduhNiagwbE0m4zgJU2YbFkyZPG6VoH8IP2wEdFuWcnjUtrXHNl1OmCsoffYBBnkC0cy1yfsOibcenaAn2SeRNKYw/0 Privilege:[] UnionId:}

Documentation

Overview

微信网页授权.

Index

Constants

View Source
const (
	LanguageZhCN = "zh_CN" // 简体中文
	LanguageZhTW = "zh_TW" // 繁体中文
	LanguageEN   = "en"    // 英文
)
View Source
const (
	SexUnknown = 0 // 未知
	SexMale    = 1 // 男性
	SexFemale  = 2 // 女性
)

Variables

This section is empty.

Functions

func Auth

func Auth(accessToken, openId string, httpClient *http.Client) (valid bool, err error)

Auth 检验授权凭证 access_token 是否有效.

accessToken: 网页授权接口调用凭证
openId:      用户的唯一标识
httpClient:  如果不指定则默认为 util.DefaultHttpClient

func AuthCodeURL

func AuthCodeURL(appId, redirectURI, scope, state string) string

AuthCodeURL 生成网页授权地址.

appId:       公众号的唯一标识
redirectURI: 授权后重定向的回调链接地址
scope:       应用授权作用域
state:       重定向后会带上 state 参数, 开发者可以填写 a-zA-Z0-9 的参数值, 最多128字节

Types

type Endpoint

type Endpoint struct {
	AppId     string
	AppSecret string
}

Endpoint 实现了 github.com/chanxuehong/wechat/oauth2.Endpoint 接口.

func NewEndpoint

func NewEndpoint(AppId, AppSecret string) *Endpoint

func (*Endpoint) ExchangeTokenURL

func (p *Endpoint) ExchangeTokenURL(code string) string

func (*Endpoint) RefreshTokenURL

func (p *Endpoint) RefreshTokenURL(refreshToken string) string

func (*Endpoint) SessionCodeUrl

func (p *Endpoint) SessionCodeUrl(code string) string

type Session

type Session struct {
	OpenId     string `json:"openid"`            // 用户唯一标识
	UnionId    string `json:"unionid,omitempty"` // 用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回
	SessionKey string `json:"session_key"`       // 会话密钥
}

func GetSession

func GetSession(Endpoint *Endpoint, code string) (session *Session, err error)

GetSession 获取小程序会话

func GetSessionWithClient

func GetSessionWithClient(Endpoint *Endpoint, code string, httpClient *http.Client) (session *Session, err error)

GetSessionWithClient 获取小程序会话

type SessionInfo

type SessionInfo struct {
	OpenId   string `json:"openId"`   // 用户的唯一标识
	Nickname string `json:"nickName"` // 用户昵称
	Gender   int    `json:"gender"`   // 用户的性别, 值为1时是男性, 值为2时是女性, 值为0时是未知
	Language string `json:"language"` // 用户的语言
	City     string `json:"city"`     // 普通用户个人资料填写的城市
	Province string `json:"province"` // 用户个人资料填写的省份
	Country  string `json:"country"`  // 国家, 如中国为CN

	// 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),
	// 用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。
	AvatarUrl string `json:"avatarUrl"`
	UnionId   string `json:"unionId"` // 只有在将小程序绑定到微信开放平台帐号后,才会出现该字段。
}

func GetSessionInfo

func GetSessionInfo(EncryptedData, sessionKey, iv string) (info *SessionInfo, err error)

GetSessionInfo 解密小程序会话加密信息

type UserInfo

type UserInfo struct {
	OpenId   string `json:"openid"`   // 用户的唯一标识
	Nickname string `json:"nickname"` // 用户昵称
	Sex      int    `json:"sex"`      // 用户的性别, 值为1时是男性, 值为2时是女性, 值为0时是未知
	City     string `json:"city"`     // 普通用户个人资料填写的城市
	Province string `json:"province"` // 用户个人资料填写的省份
	Country  string `json:"country"`  // 国家, 如中国为CN

	// 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),
	// 用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。
	HeadImageURL string `json:"headimgurl,omitempty"`

	Privilege []string `json:"privilege,omitempty"` // 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom)
	UnionId   string   `json:"unionid,omitempty"`   // 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。
}

func GetUserInfo

func GetUserInfo(accessToken, openId, lang string, httpClient *http.Client) (info *UserInfo, err error)

GetUserInfo 获取用户信息.

accessToken: 网页授权接口调用凭证
openId:      用户的唯一标识
lang:        返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语, 如果留空 "" 则默认为 zh_CN
httpClient:  如果不指定则默认为 util.DefaultHttpClient

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL