mirror of
https://github.com/casdoor/casdoor.git
synced 2025-05-24 08:20:31 +08:00
feat: add WeChatIdProvider (#62)
* feat: add WeChatIdProvider Signed-off-by: Weihao <1340908470@qq.com> * refactor: refactor WeChatIdProvider to suit to IdProvider interface; translate the annotation to English Signed-off-by: Weihao <1340908470@qq.com> * refactor: remove unnecessary comment Signed-off-by: Weihao <1340908470@qq.com>
This commit is contained in:
parent
85c76e2ed1
commit
3e41ce0104
@ -39,6 +39,8 @@ func GetIdProvider(providerType string, clientId string, clientSecret string, re
|
||||
return NewGoogleIdProvider(clientId, clientSecret, redirectUrl)
|
||||
} else if providerType == "QQ" {
|
||||
return NewQqIdProvider(clientId, clientSecret, redirectUrl)
|
||||
} else if providerType == "WeChat" {
|
||||
return NewWeChatIdProvider(clientId, clientSecret, redirectUrl)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
167
idp/weChat.go
Normal file
167
idp/weChat.go
Normal file
@ -0,0 +1,167 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package idp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"golang.org/x/oauth2"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WeChatIdProvider struct {
|
||||
Client *http.Client
|
||||
Config *oauth2.Config
|
||||
}
|
||||
|
||||
type TencentAccessToken struct {
|
||||
AccessToken string `json:"access_token"` //Interface call credentials
|
||||
ExpiresIn int64 `json:"expires_in"` //access_token interface call credential timeout time, unit (seconds)
|
||||
RefreshToken string `json:"refresh_token"` //User refresh access_token
|
||||
Openid string `json:"openid"` //Unique ID of authorized user
|
||||
Scope string `json:"scope"` //The scope of user authorization, separated by commas. (,)
|
||||
Unionid string `json:"unionid"` //This field will appear if and only if the website application has been authorized by the user's UserInfo.
|
||||
}
|
||||
|
||||
type TencentUserInfo struct {
|
||||
Openid string `json:"openid"` //The ID of an ordinary user, which is unique to the current developer account
|
||||
Nickname string `json:"nickname"` //Ordinary user nickname
|
||||
Sex int `json:"sex"` //Ordinary user gender, 1 is male, 2 is female
|
||||
Province string `json:"province"` //Province filled in by ordinary user's personal information
|
||||
City string `json:"city"` //City filled in by general user's personal data
|
||||
Country string `json:"country"` //Country, such as China is CN
|
||||
Headimgurl string `json:"headimgurl"` //User avatar, the last value represents the size of the square avatar (there are optional values of 0, 46, 64, 96, 132, 0 represents a 640*640 square avatar), this item is empty when the user does not have a avatar
|
||||
Privilege []string `json:"privilege"` //User Privilege information, json array, such as Wechat Woka user (chinaunicom)
|
||||
Unionid string `json:"unionid"` //Unified user identification. For an application under a WeChat open platform account, the unionid of the same user is unique.
|
||||
}
|
||||
|
||||
func NewWeChatIdProvider(clientId string, clientSecret string, redirectUrl string) *WeChatIdProvider {
|
||||
idp := &WeChatIdProvider{}
|
||||
|
||||
config := idp.getConfig(clientId, clientSecret, redirectUrl)
|
||||
idp.Config = config
|
||||
|
||||
return idp
|
||||
}
|
||||
|
||||
func (idp *WeChatIdProvider) SetHttpClient(client *http.Client) {
|
||||
idp.Client = client
|
||||
}
|
||||
|
||||
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
|
||||
func (idp *WeChatIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
|
||||
var endpoint = oauth2.Endpoint{
|
||||
TokenURL: "https://graph.qq.com/oauth2.0/token",
|
||||
}
|
||||
|
||||
var config = &oauth2.Config{
|
||||
Scopes: []string{"snsapi_login"},
|
||||
Endpoint: endpoint,
|
||||
ClientID: clientId,
|
||||
ClientSecret: clientSecret,
|
||||
RedirectURL: redirectUrl,
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// GetToken use code get access_token (*operation of getting code ought to be done in front)
|
||||
// get more detail via: https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
|
||||
func (idp *WeChatIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
params := url.Values{}
|
||||
params.Add("grant_type", "authorization_code")
|
||||
params.Add("appid", idp.Config.ClientID)
|
||||
params.Add("secret", idp.Config.ClientSecret)
|
||||
params.Add("code", code)
|
||||
|
||||
getAccessTokenUrl := fmt.Sprintf("https://api.weixin.qq.com/sns/oauth2/access_token?%s", params.Encode())
|
||||
tokenResponse, err := idp.Client.Get(getAccessTokenUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func(Body io.ReadCloser) {
|
||||
err := Body.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}(tokenResponse.Body)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = buf.ReadFrom(tokenResponse.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var tencentAccessToken TencentAccessToken
|
||||
if err = json.Unmarshal([]byte(buf.String()), &tencentAccessToken); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
token := oauth2.Token{
|
||||
AccessToken: tencentAccessToken.AccessToken,
|
||||
TokenType: "WeChatAccessToken",
|
||||
RefreshToken: tencentAccessToken.RefreshToken,
|
||||
Expiry: time.Time{},
|
||||
}
|
||||
|
||||
raw := make(map[string]string)
|
||||
raw["Openid"] = tencentAccessToken.Openid
|
||||
token.WithExtra(raw)
|
||||
|
||||
return &token, nil
|
||||
}
|
||||
|
||||
// GetUserInfo use TencentAccessToken gotten before return TencentUserInfo
|
||||
// get more detail via: https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Authorized_Interface_Calling_UnionID.html
|
||||
func (idp *WeChatIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
var tencentUserInfo TencentUserInfo
|
||||
accessToken := token.AccessToken
|
||||
openid := token.Extra("Openid")
|
||||
|
||||
getUserInfoUrl := fmt.Sprintf("https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s", accessToken, openid)
|
||||
getUserInfoResponse, err := idp.Client.Get(getUserInfoUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func(Body io.ReadCloser) {
|
||||
err := Body.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}(getUserInfoResponse.Body)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = buf.ReadFrom(getUserInfoResponse.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = json.Unmarshal([]byte(buf.String()), &tencentUserInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userInfo := UserInfo{
|
||||
Username: tencentUserInfo.Nickname,
|
||||
Email: "",
|
||||
AvatarUrl: tencentUserInfo.Headimgurl,
|
||||
}
|
||||
|
||||
return &userInfo, nil
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user