oauth2

package
v0.0.0-...-5652a87 Latest Latest
Warning

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

Go to latest
Published: Nov 18, 2017 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"
	"github.com/chanxuehong/wechat/mp/user/oauth2"
)

var (
	sessionStorage = session.New(20*60, 60*60)

	oauth2Config = oauth2.NewOAuth2Config(
		"appid",                     // 填上自己的参数
		"appsecret",                 // 填上自己的参数
		"http://192.168.1.80/page2", // 授权后跳转地址
		"snsapi_userinfo",           // 需要用户授权, snsapi_base 不需要
	)
)

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

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

	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 := oauth2Config.AuthCodeURL(state, nil)
	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)

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

	code := urlValues.Get("code")
	urlState := urlValues.Get("state")

	if savedState != urlState {
		io.WriteString(w, fmt.Sprintf("state 不匹配, session 中的为 %q, url 传递过来的是 %q", savedState, urlState))
		return
	}

	if code == "" {
		log.Println("客户禁止授权")
		return
	}

	oauth2Client := oauth2.Client{
		Config: oauth2Config,
	}

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

	userinfo, err := oauth2Client.UserInfo(oauth2.Language_zh_CN)
	if err != nil {
		io.WriteString(w, err.Error())
		log.Println(err)
		return
	}

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

func main() {
	http.ListenAndServe(":80", nil)
}

Documentation

Overview

网页授权获取用户基本信息

Index

Constants

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

Variables

View Source
var ErrNoHeadImage = errors.New("没有头像")

Functions

func AuthCodeURL

func AuthCodeURL(appId, redirectURI, scope, state string, redirectURIExt url.Values) string

Types

type Client

type Client struct {
	Config Config

	// TokenStorage, Token 两个字段正常情况下只用指定一个, 如果两个同时被指定了, 优先使用 TokenStorage;
	// Client 会自动将最新的 Token 更新到 Client.Token 字段, 不管 Token 字段一开始是否被指定!!!
	TokenStorage TokenStorage
	Token        *Token

	HttpClient *http.Client // 如果 HttpClient == nil 则默认用 http.DefaultClient
}

func (*Client) CheckAccessTokenValid

func (clt *Client) CheckAccessTokenValid() (valid bool, err error)

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

func (*Client) Exchange

func (clt *Client) Exchange(code string) (token *Token, err error)

通过code换取网页授权access_token.

返回的 token == clt.Token

func (*Client) TokenRefresh

func (clt *Client) TokenRefresh() (token *Token, err error)

刷新access_token(如果需要).

返回的 token == clt.Token

func (*Client) UserInfo

func (clt *Client) UserInfo(lang string) (info *UserInfo, err error)

获取用户信息(需scope为 snsapi_userinfo).

lang 可能的取值是 zh_CN, zh_TW, en, 如果留空 "" 则默认为 zh_CN.

type Config

type Config interface {
	AuthCodeURL(state string, redirectURIExt url.Values) string // 请求用户授权的地址, 获取code; redirectURIExt 用于扩展回调地址的参数
	ExchangeTokenURL(code string) string                        // 通过code换取access_token的地址
	RefreshTokenURL(refreshToken string) string                 // 刷新access_token的地址
	UserInfoURL(accessToken, openId, lang string) string        // 获取用户信息的地址
}

type OAuth2Config

type OAuth2Config struct {
	AppId     string
	AppSecret string

	// 用户授权后跳转的目的地址
	// 用户授权后跳转到 RedirectURI?code=CODE&state=STATE
	// 用户禁止授权跳转到 RedirectURI?state=STATE
	RedirectURI string

	// 应用授权作用域, snsapi_base, snsapi_userinfo
	Scopes []string
}

func NewOAuth2Config

func NewOAuth2Config(AppId, AppSecret, RedirectURI string, Scope ...string) *OAuth2Config

func (*OAuth2Config) AuthCodeURL

func (cfg *OAuth2Config) AuthCodeURL(state string, redirectURIExt url.Values) string

func (*OAuth2Config) ExchangeTokenURL

func (cfg *OAuth2Config) ExchangeTokenURL(code string) string

func (*OAuth2Config) RefreshTokenURL

func (cfg *OAuth2Config) RefreshTokenURL(refreshToken string) string

func (*OAuth2Config) UserInfoURL

func (cfg *OAuth2Config) UserInfoURL(accessToken, openId, lang string) string

type Token

type Token struct {
	AccessToken  string // 网页授权接口调用凭证, 注意: 此access_token与基础支持的access_token不同
	ExpiresAt    int64  // 过期时间, unixtime, 分布式系统要求时间同步, 建议使用 NTP
	RefreshToken string // 用户刷新access_token

	OpenId  string   // 用户唯一标识, 请注意, 在未关注公众号时, 用户访问公众号的网页, 也会产生一个用户和公众号唯一的OpenID
	UnionId string   // UnionID机制
	Scopes  []string // 用户授权的作用域
}

func (*Token) AccessTokenExpired

func (token *Token) AccessTokenExpired() bool

判断 Token.AccessToken 是否过期, 过期返回 true, 否则返回 false

type TokenStorage

type TokenStorage interface {
	Get() (*Token, error)
	Put(*Token) error
}

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正方形头像),
	// 用户没有头像时该项为空
	HeadImageURL string `json:"headimgurl"`

	// 用户特权信息, json 数组, 如微信沃卡用户为(chinaunicom)
	Privilege []string `json:"privilege"`

	// 用户统一标识. 针对一个微信开放平台帐号下的应用, 同一用户的unionid是唯一的.
	UnionId string `json:"unionid"`
}

func (*UserInfo) HeadImageSize

func (info *UserInfo) HeadImageSize() (size int, err error)

获取用户图像的大小, 如果用户没有图像则返回 ErrNoHeadImage 错误.

Jump to

Keyboard shortcuts

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