添加项目文件。

This commit is contained in:
2023-03-13 15:00:34 +08:00
parent 42bf06ca3e
commit 1d73df3235
1205 changed files with 185078 additions and 0 deletions

View File

@@ -0,0 +1,111 @@
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Cache;
using Aop.Api;
using Aop.Api.Request;
using Aop.Api.Response;
using Newtonsoft.Json;
namespace JNPF.Extras.CollectiveOAuth.Request;
public partial class AlipayMpAuthRequest : DefaultAuthRequest
{
private IAopClient aopClient;
public AlipayMpAuthRequest(ClientConfig config) : base(config, new AlipayMPAuthSource())
{
aopClient = new DefaultAopClient(source.accessToken(), config.clientId, config.clientSecret, "json", "1.0", "RSA2", config.alipayPublicKey, "GBK", false);
}
public AlipayMpAuthRequest(ClientConfig config, IAuthStateCache authStateCache) : base(config, new AlipayMPAuthSource(), authStateCache)
{
aopClient = new DefaultAopClient(source.accessToken(), config.clientId, config.clientSecret, "json", "1.0", "RSA2", config.alipayPublicKey, "GBK", false);
}
/**
* 微信的特殊性,此时返回的信息同时包含 openid 和 access_token
*
* @param authCallback 回调返回的参数
* @return 所有信息
*/
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
request.GrantType = "authorization_code";
request.Code = authCallback.auth_code;
AlipaySystemOauthTokenResponse response = null;
try
{
response = this.aopClient.Execute(request);
}
catch (Exception e)
{
throw new Exception(e.Message);
}
if (response.IsError)
{
throw new Exception(response.SubMsg);
}
var authToken = new AuthToken();
authToken.accessToken = response.AccessToken;
authToken.uid = response.UserId;
authToken.expireIn = Convert.ToInt32(response.ExpiresIn);
authToken.refreshToken = response.RefreshToken;
authToken.userId = response.AlipayUserId;
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
string accessToken = authToken.accessToken;
AlipayUserInfoShareRequest request = new AlipayUserInfoShareRequest();
AlipayUserInfoShareResponse response = null;
try
{
response = this.aopClient.Execute(request, accessToken);
}
catch (Exception e)
{
throw new Exception(e.Message, e);
}
if (response.IsError)
{
throw new Exception(response.SubMsg);
}
string province = response.Province, city = response.City;
string location = string.Format("{0} {1}", !province.IsNullOrWhiteSpace() ? "" : province, !city.IsNullOrWhiteSpace() ? "" : city);
var authUser = new AuthUser();
authUser.username = response.UserName.IsNullOrWhiteSpace() ? response.NickName : response.UserName;
authUser.nickname = response.NickName;
authUser.avatar = response.Avatar;
authUser.location = location;
authUser.uuid = response.UserId;
authUser.gender = GlobalAuthUtil.getRealGender(response.Gender);
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = response;
authUser.originalUserStr = JsonConvert.SerializeObject(response);
return authUser;
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("app_id", config.clientId)
.queryParam("scope", config.scope.IsNullOrWhiteSpace() ? "auth_user" : config.scope)
.queryParam("redirect_uri", config.redirectUri)
.queryParam("state", getRealState(state))
.build();
}
}

View File

@@ -0,0 +1,124 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Enums;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class BaiduAuthRequest : DefaultAuthRequest
{
public BaiduAuthRequest(ClientConfig config) : base(config, new BaiduAuthSource())
{
}
public BaiduAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new BaiduAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
string response = doPostAuthorizationCode(authCallback.code);
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken();
authToken.accessToken = accessTokenObject.getString("access_token");
authToken.refreshToken = accessTokenObject.getString("refresh_token");
authToken.expireIn = accessTokenObject.getInt32("expires_in");
authToken.scope = accessTokenObject.getString("scope");
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
string response = doGetUserInfo(authToken);
var userObj = response.parseObject();
this.checkResponse(userObj);
var authUser = new AuthUser();
authUser.uuid = userObj.getString("userid");
authUser.username = userObj.getString("username");
authUser.nickname = userObj.getString("username");
string protrait = userObj.getString("portrait");
authUser.avatar = protrait.IsNullOrWhiteSpace() ? null : string.Format("http://himg.bdimg.com/sys/portrait/item/{0}.jpg", protrait);
authUser.remark = userObj.getString("userdetail");
authUser.gender = GlobalAuthUtil.getRealGender(userObj.getString("sex"));
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = userObj;
authUser.originalUserStr = response;
return authUser;
}
public override AuthResponse revoke(AuthToken authToken)
{
string response = doGetRevoke(authToken);
var revokeObj = response.parseObject();
this.checkResponse(revokeObj);
// 返回1表示取消授权成功否则失败
AuthResponseStatus status = revokeObj.getInt32("result") == 1 ? AuthResponseStatus.SUCCESS : AuthResponseStatus.FAILURE;
return new AuthResponse(status.GetCode(), status.GetDesc());
}
public override AuthResponse refresh(AuthToken authToken)
{
string refreshUrl = UrlBuilder.fromBaseUrl(this.source.refresh())
.queryParam("grant_type", "refresh_token")
.queryParam("refresh_token", authToken.refreshToken)
.queryParam("client_id", this.config.clientId)
.queryParam("client_secret", this.config.clientSecret)
.build();
string response = HttpUtils.RequestGet(refreshUrl);
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var newAuthToken = new AuthToken();
newAuthToken.accessToken = accessTokenObject.getString("access_token");
newAuthToken.refreshToken = accessTokenObject.getString("refresh_token");
newAuthToken.expireIn = accessTokenObject.getInt32("expires_in");
newAuthToken.scope = accessTokenObject.getString("scope");
return new AuthResponse(AuthResponseStatus.SUCCESS.GetCode(), AuthResponseStatus.SUCCESS.GetDesc(), newAuthToken);
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.clientId)
.queryParam("redirect_uri", config.redirectUri)
.queryParam("display", "page")
.queryParam("scope", "basic")
.queryParam("state", getRealState(state))
.build();
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常则返回JSONObject
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("error") || dic.ContainsKey("error_code"))
{
throw new Exception($@"error_code: {dic.getString("error_code")}," +
$" error_description: {dic.getString("error_description")}," +
$" error_msg: {dic.getString("error_msg")}");
}
}
}

View File

@@ -0,0 +1,102 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using System.Net;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class CodingAuthRequest : DefaultAuthRequest
{
public CodingAuthRequest(ClientConfig config) : base(config, new CodingAuthSource())
{
}
public CodingAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new CodingAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
string response = doGetAuthorizationCode(authCallback.code);
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken();
authToken.accessToken = accessTokenObject.getString("access_token");
authToken.expireIn = accessTokenObject.getInt32("expires_in");
authToken.refreshToken = accessTokenObject.getString("refresh_token");
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
string response = doGetUserInfo(authToken);
var resData = response.parseObject();
this.checkResponse(resData);
var userObj = resData.getString("data").parseObject();
var authUser = new AuthUser();
authUser.uuid = userObj.getString("id");
authUser.username = userObj.getString("name");
authUser.nickname = userObj.getString("name");
authUser.avatar = $"{"https://coding.net/"}{userObj.getString("avatar")}";
authUser.blog = $"{"https://coding.net/"}{userObj.getString("path")}";
authUser.company = userObj.getString("company");
authUser.location = userObj.getString("location");
authUser.email = userObj.getString("email");
authUser.remark = userObj.getString("slogan");
authUser.gender = GlobalAuthUtil.getRealGender(userObj.getString("sex"));
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = resData;
authUser.originalUserStr = response;
return authUser;
}
protected override string doGetUserInfo(AuthToken authToken)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
return HttpUtils.RequestJsonGet(userInfoUrl(authToken));
}
protected override string doGetAuthorizationCode(String code)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
return HttpUtils.RequestJsonGet(accessTokenUrl(code));
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.clientId)
.queryParam("redirect_uri", config.redirectUri)
.queryParam("scope", config.scope.IsNullOrWhiteSpace() ? "user" : config.scope)
.queryParam("state", getRealState(state))
.build();
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常则返回Exception
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("code") && dic.getInt32("code") != 0)
{
throw new Exception($"{dic.getString("msg")}");
}
}
}

View File

@@ -0,0 +1,102 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
using Newtonsoft.Json;
using DingTalk.Api;
using DingTalk.Api.Response;
using DingTalk.Api.Request;
using JNPF.Common.Security;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class DingTalkScanAuthRequest : DefaultAuthRequest
{
public DingTalkScanAuthRequest(ClientConfig config) : base(config, new DingTalkScanAuthSource())
{
}
public DingTalkScanAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new DingTalkScanAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
var map = new Dictionary<string, object>();
map.Add("clientId", this.config.clientId);
map.Add("clientSecret", this.config.clientSecret);
map.Add("code", authCallback.code);
map.Add("refreshToken", authCallback.code);
map.Add("grantType", "authorization_code");
var head = new Dictionary<string, object>() { { "Content-Type", "application/json" } };
var response = HttpUtils.RequestPost(source.accessToken(), map.ToJsonString(), head);
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var res = new AuthToken();
res.accessToken = accessTokenObject["accessToken"].ToString();
res.refreshToken = accessTokenObject["refreshToken"].ToString();
res.expireIn = Convert.ToInt16(accessTokenObject["expireIn"]);
res.openId = accessTokenObject.ContainsKey("openId") ? accessTokenObject["openId"].ToString() : string.Empty;
return res;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
var httpHeader = new Dictionary<string, object>();
httpHeader.Add("x-acs-dingtalk-access-token", authToken.accessToken);
httpHeader.Add("Content-Type", "application/json");
var response = HttpUtils.RequestGet(userInfoUrl(authToken), httpHeader);
var data = response.parseObject();
this.checkResponse(data);
var authUser = new AuthUser();
authUser.uuid = data.getString("unionId");
authUser.username = data.getString("nick");
authUser.nickname = data.getString("nick");
authUser.email = data.getString("email");
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUserStr = response;
return authUser;
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常则返回JSONObject
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("errcode") && dic.getInt32("errcode") != 0)
{
throw new Exception($"errcode: {dic.getString("errcode")}, errmsg: {dic.getString("errmsg")}");
}
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.clientId)
.queryParam("scope", "openid")
.queryParam("redirect_uri", config.redirectUri)
.queryParam("prompt", "consent")
.build();
}
}

View File

@@ -0,0 +1,160 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class DouyinAuthRequest : DefaultAuthRequest
{
public DouyinAuthRequest(ClientConfig config) : base(config, new DouyinAuthSource())
{
}
public DouyinAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new DouyinAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
return this.getToken(accessTokenUrl(authCallback.code));
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
string response = doGetUserInfo(authToken);
var userInfoObject = response.parseObject();
this.checkResponse(userInfoObject);
var userObj = userInfoObject.getString("data").parseObject();
var location = $"{userObj.getString("country")}-{userObj.getString("province")}-{userObj.getString("city")}";
var authUser = new AuthUser();
authUser.uuid = userObj.getString("union_id");
authUser.username = userObj.getString("nickname");
authUser.nickname = userObj.getString("nickname");
authUser.avatar = userObj.getString("avatar");
authUser.location = location;
authUser.remark = userObj.getString("description");
authUser.gender = GlobalAuthUtil.getRealGender(userObj.getString("gender"));
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = userObj;
authUser.originalUserStr = response;
return authUser;
}
public override AuthResponse refresh(AuthToken oldToken)
{
var data = getToken(refreshTokenUrl(oldToken.refreshToken));
return new AuthResponse(AuthResponseStatus.SUCCESS.GetCode(), AuthResponseStatus.SUCCESS.GetDesc(), data);
}
/**
* 获取token适用于获取access_token和刷新token
*
* @param accessTokenUrl 实际请求token的地址
* @return token对象
*/
private AuthToken getToken(string accessTokenUrl)
{
var response = HttpUtils.RequestPost(accessTokenUrl);
string accessTokenStr = response;
var tokenObj = accessTokenStr.parseObject();
this.checkResponse(tokenObj);
var accessTokenObject = tokenObj.getString("data").parseObject();
var authToken = new AuthToken
{
accessToken = accessTokenObject.getString("access_token"),
openId = accessTokenObject.getString("open_id"),
expireIn = accessTokenObject.getInt32("token_type"),
refreshToken = accessTokenObject.getString("refresh_token"),
scope = accessTokenObject.getString("scope")
};
return authToken;
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_key", config.clientId)
.queryParam("redirect_uri", config.redirectUri)
.queryParam("scope", config.scope.IsNullOrWhiteSpace() ? "user_info" : config.scope)
.queryParam("state", getRealState(state))
.build();
}
/**
* 返回获取accessToken的url
*
* @param code oauth的授权码
* @return 返回获取accessToken的url
*/
protected override string accessTokenUrl(string code)
{
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("code", code)
.queryParam("client_key", config.clientId)
.queryParam("client_secret", config.clientSecret)
.queryParam("grant_type", "authorization_code")
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken oauth返回的token
* @return 返回获取userInfo的url
*/
protected override string userInfoUrl(AuthToken authToken)
{
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.accessToken)
.queryParam("open_id", authToken.openId)
.build();
}
/**
* 返回获取accessToken的url
*
* @param refreshToken oauth返回的refreshtoken
* @return 返回获取accessToken的url
*/
protected override string refreshTokenUrl(string refreshToken)
{
return UrlBuilder.fromBaseUrl(source.refresh())
.queryParam("client_key", config.clientId)
.queryParam("refresh_token", refreshToken)
.queryParam("grant_type", "refresh_token")
.build();
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常则返回Exception
*/
private void checkResponse(Dictionary<string, object> dic)
{
string message = dic.getString("message");
var data = dic.getString("data").parseObject();
int errorCode = data.getInt32("error_code");
if ("error".Equals(message) || errorCode != 0)
{
throw new Exception(data.getString("description"));
}
}
}

View File

@@ -0,0 +1,280 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
using System.Text;
using System.Security.Cryptography;
using Newtonsoft.Json;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class ElemeAuthRequest : DefaultAuthRequest
{
public ElemeAuthRequest(ClientConfig config) : base(config, new ElemeAuthSource())
{
}
public ElemeAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new ElemeAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
var reqParams = new Dictionary<string, object>
{
{ "client_id", config.clientId },
{ "redirect_uri", config.clientSecret },
{ "code", authCallback.code },
{ "grant_type", "authorization_code" },
};
var reqHeaders = this.getSpecialHeader(this.getRequestId());
var response = HttpUtils.RequestFormPost(source.accessToken(), reqParams.spellParams(), reqHeaders);
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken
{
accessToken = accessTokenObject.getString("access_token"),
expireIn = accessTokenObject.getInt32("expires_in"),
refreshToken = accessTokenObject.getString("refresh_token"),
tokenType = accessTokenObject.getString("token_type"),
code = authCallback.code
};
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
// 获取商户账号信息的API接口名称
String action = "eleme.user.getUser";
// 时间戳单位秒。API服务端允许客户端请求最大时间误差为正负5分钟。
long timestamp = DateTime.Now.Ticks;
// 公共参数
var metasHashMap = new Dictionary<string, object>();
metasHashMap.Add("app_key", config.clientId);
metasHashMap.Add("timestamp", timestamp);
string signature = this.generateElemeSignature(timestamp, action, authToken.accessToken);
string requestId = this.getRequestId();
var paramsMap = new Dictionary<string, object>
{
{ "nop", "1.0.0" },
{ "id", requestId },
{ "action", action },
{ "token", authToken.accessToken },
{ "metas", JsonConvert.SerializeObject(metasHashMap) },
{ "params", "{}" },
{ "signature", signature }
};
var reqHeaders = new Dictionary<string, object>
{
{ "Content-Type", "application/json; charset=utf-8" },
{ "Accept", "text/xml,text/javascript,text/html" },
{ "Accept-Encoding", "gzip" },
{ "User-Agent", "eleme-openapi-java-sdk"},
{ "x-eleme-requestid", requestId},
{ "Authorization", this.spliceBasicAuthStr()}
};
var response = HttpUtils.RequestPost(source.userInfo(), JsonConvert.SerializeObject(paramsMap), reqHeaders);
var resObj = response.parseObject();
// 校验请求
if (resObj.ContainsKey("name"))
{
throw new Exception(resObj.getString("message"));
}
if (resObj.ContainsKey("error") && !resObj.getString("error").IsNullOrWhiteSpace())
{
throw new Exception(resObj.getJSONObject("error").getString("message"));
}
var userObj = resObj.getJSONObject("result");
var authUser = new AuthUser
{
uuid = userObj.getString("userId"),
username = userObj.getString("userName"),
nickname = userObj.getString("userName"),
gender = AuthUserGender.UNKNOWN,
token = authToken,
source = source.getName(),
originalUser = resObj,
originalUserStr = response
};
return authUser;
}
public override AuthResponse refresh(AuthToken oldToken)
{
var reqParams = new Dictionary<string, object>
{
{ "refresh_token", oldToken.refreshToken },
{ "grant_type", "refresh_token" },
};
var reqHeaders = this.getSpecialHeader(this.getRequestId());
var response = HttpUtils.RequestFormPost(source.accessToken(), reqParams.spellParams(), reqHeaders);
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken
{
accessToken = accessTokenObject.getString("access_token"),
refreshToken = accessTokenObject.getString("refresh_token"),
expireIn = accessTokenObject.getInt32("expires_in"),
tokenType = accessTokenObject.getString("token_type")
};
return new AuthResponse(AuthResponseStatus.SUCCESS.GetCode(), AuthResponseStatus.SUCCESS.GetDesc(), authToken);
}
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(base.authorize(state))
.queryParam("scope", config.scope.IsNullOrWhiteSpace() ? "all" : config.scope)
.build();
}
private string spliceBasicAuthStr()
{
string encodeToString = encodeBase64($"{config.clientId}:{config.clientSecret}");
return $"Basic {encodeToString}";
}
private Dictionary<string, object> getSpecialHeader(string requestId)
{
var headers = new Dictionary<string, object>
{
{ "Content-Type", "application/x-www-form-urlencoded;charset=UTF-8" },
{ "Accept", "text/xml,text/javascript,text/html" },
{ "Accept-Encoding", "gzip" },
{ "User-Agent", "eleme-openapi-java-sdk"},
{ "x-eleme-requestid", requestId},
{ "Authorization", this.spliceBasicAuthStr()}
};
return headers;
}
private string getRequestId()
{
return (Guid.NewGuid().ToString() + "|" + DateTime.Now.Ticks.ToString()).ToUpper();
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常则返回Exception
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("error"))
{
throw new Exception($"{dic.getString("error_description")}");
}
}
///编码
public string encodeBase64(string contentStr, string encodeType = "utf-8")
{
string encode = "";
byte[] bytes = Encoding.GetEncoding(encodeType).GetBytes(contentStr);
try
{
encode = Convert.ToBase64String(bytes);
}
catch
{
encode = contentStr;
}
return encode;
}
///解码
public string decodeBase64(string contentStr, string encodeType = "utf-8")
{
string decode = "";
byte[] bytes = Convert.FromBase64String(contentStr);
try
{
decode = Encoding.GetEncoding(encodeType).GetString(bytes);
}
catch
{
decode = contentStr;
}
return decode;
}
/**
* 生成饿了么请求的Signature
* <p>
* 代码copy并修改自https://coding.net/u/napos_openapi/p/eleme-openapi-java-sdk/git/blob/master/src/main/java/eleme/openapi/sdk/utils/SignatureUtil.java
*
* @param appKey 平台应用的授权key
* @param secret 平台应用的授权密钥
* @param timestamp 时间戳单位秒。API服务端允许客户端请求最大时间误差为正负5分钟。
* @param action 饿了么请求的api方法
* @param token 用户授权的token
* @param parameters 加密参数
* @return Signature
*/
public string generateElemeSignature(long timestamp, string action, string token)
{
Dictionary<string, object> dicList = new Dictionary<string, object>();
dicList.Add("app_key", config.clientId);
dicList.Add("timestamp", timestamp);
var signStr = dicList.Sort().spellParams();
string splice = $"{action}{token}{signStr}{config.clientSecret}";
string calculatedSignature = hashMd5String(splice);
return calculatedSignature;
}
/// <summary>
/// 对字符串进行Md5加密isUpper为True时返回大写反之小写
/// </summary>
/// <param name="willMd5Str"></param>
/// <param name="isUpper"></param>
public static string hashMd5String(string willMd5Str, bool isUpper = true)
{
//就是比string往后一直加要好的优化容器
StringBuilder sb = new StringBuilder();
using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
{
//将输入字符串转换为字节数组并计算哈希。
byte[] data = md5.ComputeHash(Encoding.UTF8.GetBytes(willMd5Str));
//X为 十六进制 X都是大写 x都为小写
//2为 每次都是两位数
//假设有两个数10和26正常情况十六进制显示0xA、0x1A这样看起来不整齐为了好看可以指定"X2"这样显示出来就是0x0A、0x1A。
//遍历哈希数据的每个字节
//并将每个字符串格式化为十六进制字符串。
int length = data.Length;
for (int i = 0; i < length; i++)
sb.Append(data[i].ToString("X2"));
}
if (isUpper)
{
return sb.ToString().ToUpper();
}
else
{
return sb.ToString().ToLower();
}
}
}

View File

@@ -0,0 +1,100 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class FackbookAuthRequest : DefaultAuthRequest
{
public FackbookAuthRequest(ClientConfig config) : base(config, new FackbookAuthSource())
{
}
public FackbookAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new FackbookAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
var response = doPostAuthorizationCode(authCallback.code);
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken
{
accessToken = accessTokenObject.getString("access_token"),
expireIn = accessTokenObject.getInt32("expires_in"),
tokenType = accessTokenObject.getString("token_type"),
code = authCallback.code
};
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
var response = doGetUserInfo(authToken);
var userObj = response.parseObject();
this.checkResponse(userObj);
var authUser = new AuthUser
{
uuid = userObj.getString("id"),
username = userObj.getString("name"),
nickname = userObj.getString("name"),
avatar = getUserPicture(userObj),
location = userObj.getString("locale"),
email = userObj.getString("email"),
gender = GlobalAuthUtil.getRealGender(userObj.getString("gender")),
token = authToken,
source = source.getName(),
originalUser = userObj,
originalUserStr = response
};
return authUser;
}
private string getUserPicture(Dictionary<string, object> userObj)
{
string picture = null;
if (userObj.ContainsKey("picture"))
{
var pictureObj = userObj.getString("picture").parseObject();
pictureObj = pictureObj.getString("data").parseObject();
if (null != pictureObj)
{
picture = pictureObj.getString("url");
}
}
return picture;
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户token
* @return 返回获取userInfo的url
*/
protected override string userInfoUrl(AuthToken authToken)
{
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.accessToken)
.queryParam("fields", "id,name,birthday,gender,hometown,email,devices,picture.width(400)")
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("error"))
{
throw new Exception($"{dic.getString("error").parseObject().getString("message")}");
}
}
}

View File

@@ -0,0 +1,143 @@
using JNPF.Common.Security;
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using System.Web;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class FeiShuAuthRequest : DefaultAuthRequest
{
public FeiShuAuthRequest(ClientConfig config) : base(config, new FeiShuAuthSource())
{
}
public FeiShuAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new FeiShuAuthSource(), authStateCache)
{
}
private string getAppAccessToken()
{
var cacheKey = this.source.getName() + this.config.clientId;
var cacheAppAccessToken = this.authStateCache.get(cacheKey);
if (!cacheAppAccessToken.IsNullOrEmpty())
{
return cacheAppAccessToken;
}
else
{
var url = "https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal/";
var requestObject = new Dictionary<string, object>();
requestObject.Add("app_id", this.config.clientId);
requestObject.Add("app_secret", this.config.clientSecret);
var response = HttpUtils.RequestPost(url, requestObject.ToJsonString(), new Dictionary<string, object>() { { "Content-Type", "application/json" } });
var jsonObject = response.parseObject();
this.checkResponse(jsonObject);
var appAccessToken = jsonObject.getString("app_access_token");
this.authStateCache.cache(cacheKey, appAccessToken, 9 * 1000);
return appAccessToken;
}
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
var requestObject = new Dictionary<string, object>();
requestObject.Add("app_access_token", this.getAppAccessToken());
requestObject.Add("grant_type", "authorization_code");
requestObject.Add("code", authCallback.code);
return this.getToken(requestObject, this.source.accessToken());
}
private AuthToken getToken(Dictionary<string, object> param, string url)
{
var response = HttpUtils.RequestPost(url, param.ToJsonString(), new Dictionary<string, object>() { { "Content-Type", "application/json" } });
var jsonObject = response.parseObject();
this.checkResponse(jsonObject);
var data = jsonObject["data"].ToObject<Dictionary<string, object>>();
var authToken = new AuthToken();
authToken.accessToken = data.getString("access_token");
authToken.refreshToken = data.getString("refresh_token");
authToken.expireIn = data.getInt32("expires_in");
authToken.tokenType = data.getString("token_type");
authToken.openId = data.getString("open_id");
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
var accessToken = authToken.accessToken;
string response = doGetUserInfo(authToken);
var jsonObject = response.parseObject();
this.checkResponse(jsonObject);
var data = jsonObject["data"].ToObject<Dictionary<string, object>>();
var authUser = new AuthUser();
authUser.uuid = data.getString("union_id");
authUser.username = data.getString("name");
authUser.nickname = data.getString("name");
authUser.avatar = data.getString("avatar_url");
authUser.email = data.getString("email");
//authUser.location = location;
//authUser.remark = data.getString("bio");
//authUser.gender = GlobalAuthUtil.getRealGender(userObj.getString("gender"));
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUserStr = response;
return authUser;
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
public override string authorize(string state)
{
config.redirectUri = HttpUtility.UrlEncode(config.redirectUri);
config.redirectUri = config.redirectUri.Replace("+", "%20").Replace("*", "%2A").Replace("~", "%7E").Replace("/", "%2F");
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("app_id", this.config.clientId)
.queryParam("redirect_uri", config.redirectUri)
.queryParam("state", getRealState(state))
.build();
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常则返回JSONObject
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("errcode") && dic.getInt32("errcode") != 0)
{
throw new Exception($"errcode: {dic.getString("errcode")}, errmsg: {dic.getString("errmsg")}");
}
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
protected override string accessTokenUrl(string code)
{
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("app_id", config.clientId)
.queryParam("app_secret", config.clientSecret)
.build();
}
}

View File

@@ -0,0 +1,73 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Enums;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class GiteeAuthRequest : DefaultAuthRequest
{
public GiteeAuthRequest(ClientConfig config) : base(config, new GiteeAuthSource())
{
}
public GiteeAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new GiteeAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
string response = doPostAuthorizationCode(authCallback.code);
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken();
authToken.accessToken = accessTokenObject.getString("access_token");
authToken.refreshToken = accessTokenObject.getString("refresh_token");
authToken.tokenType = accessTokenObject.getString("token_type");
authToken.expireIn = accessTokenObject.getInt32("expires_in");
authToken.scope = accessTokenObject.getString("scope");
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
string response = doGetUserInfo(authToken);
var userObj = response.parseObject();
this.checkResponse(userObj);
var authUser = new AuthUser();
authUser.uuid = userObj.getString("id");
authUser.username = userObj.getString("login");
authUser.nickname = userObj.getString("name");
authUser.avatar = userObj.getString("avatar_url");
authUser.blog = userObj.getString("blog");
authUser.company = userObj.getString("company");
authUser.location = userObj.getString("address");
authUser.email = userObj.getString("email");
authUser.remark = userObj.getString("bio");
authUser.gender = AuthUserGender.UNKNOWN;
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = userObj;
authUser.originalUserStr = response;
return authUser;
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常则返回Exception
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("error"))
{
throw new Exception($"{dic.getString("error_description")}");
}
}
}

View File

@@ -0,0 +1,100 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class GithubAuthRequest : DefaultAuthRequest
{
public GithubAuthRequest(ClientConfig config) : base(config, new GithubAuthSource())
{
}
public GithubAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new GithubAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
string response = doPostAuthorizationCode(authCallback.code);
var accessTokenObject = response.parseStringObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken();
authToken.accessToken = accessTokenObject.getString("access_token");
authToken.tokenType = accessTokenObject.getString("token_type");
authToken.scope = accessTokenObject.getString("scope");
authToken.code = authCallback.code;
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
string response = doGetUserInfo(authToken);
var userObj = response.parseObject();
this.checkResponse(userObj);
var authUser = new AuthUser();
authUser.uuid = userObj.getString("id");
authUser.username = userObj.getString("login");
authUser.nickname = userObj.getString("name");
authUser.avatar = userObj.getString("avatar_url");
authUser.blog = userObj.getString("blog");
authUser.company = userObj.getString("company");
authUser.location = userObj.getString("location");
authUser.email = userObj.getString("email");
authUser.remark = userObj.getString("bio");
authUser.gender = AuthUserGender.UNKNOWN;
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = userObj;
authUser.originalUserStr = response;
return authUser;
}
/// <summary>
/// 重写获取用户信息方法
/// </summary>
/// <param name="authToken"></param>
/// <returns></returns>
protected override string doGetUserInfo(AuthToken authToken)
{
return HttpUtils.RequestJsonGet(userInfoUrl(authToken));
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("client_id", config.clientId)
.queryParam("response_type", "code")
.queryParam("redirect_uri", config.redirectUri)
.queryParam("scope", config.scope.IsNullOrWhiteSpace() ? "user" : config.scope)
.queryParam("state", getRealState(state) + "#wechat_redirect")
.build();
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常则返回Exception
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("error"))
{
throw new Exception($"{dic.getString("error_description")}");
}
}
}

View File

@@ -0,0 +1,103 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class GitlabAuthRequest : DefaultAuthRequest
{
public GitlabAuthRequest(ClientConfig config) : base(config, new GitlabAuthSource())
{
}
public GitlabAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new GitlabAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
var response = doPostAuthorizationCode(authCallback.code);
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken();
authToken.accessToken = accessTokenObject.getString("access_token");
authToken.refreshToken = accessTokenObject.getString("refresh_token");
authToken.idToken = accessTokenObject.getString("id_token");
authToken.tokenType = accessTokenObject.getString("token_type");
authToken.scope = accessTokenObject.getString("scope");
authToken.code = authCallback.code;
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
var response = doGetUserInfo(authToken);
var userObj = response.parseObject();
this.checkResponse(userObj);
var authUser = new AuthUser();
authUser.uuid = userObj.getString("id");
authUser.username = userObj.getString("username");
authUser.nickname = userObj.getString("name");
authUser.avatar = userObj.getString("avatar_url");
authUser.blog = userObj.getString("web_url");
authUser.company = userObj.getString("organization");
authUser.location = userObj.getString("location");
authUser.email = userObj.getString("email");
authUser.remark = userObj.getString("bio");
authUser.gender = AuthUserGender.UNKNOWN;
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = userObj;
authUser.originalUserStr = response;
return authUser;
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.11.0
*/
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.clientId)
.queryParam("redirect_uri", config.redirectUri)
.queryParam("state", getRealState(state))
.queryParam("scope", config.scope.IsNullOrWhiteSpace() ? "read_user+openid+profile+email" : config.scope)
.build();
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常则返回Exception
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("error"))
{
throw new Exception($"{dic.getString("error_description")}");
}
// user 验证异常
if (dic.ContainsKey("message"))
{
throw new Exception($"{dic.getString("message")}");
}
}
}

View File

@@ -0,0 +1,106 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class GoogleAuthRequest : DefaultAuthRequest
{
public GoogleAuthRequest(ClientConfig config) : base(config, new GoogleAuthSource())
{
}
public GoogleAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new GoogleAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
var response = doPostAuthorizationCode(authCallback.code);
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken();
authToken.accessToken = accessTokenObject.getString("access_token");
authToken.expireIn = accessTokenObject.getInt32("expires_in");
authToken.idToken = accessTokenObject.getString("id_token");
authToken.tokenType = accessTokenObject.getString("token_type");
authToken.scope = accessTokenObject.getString("scope");
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
var reqParams = new Dictionary<string, object>
{
{ "Authorization", "Bearer " + authToken.accessToken }
};
var response = HttpUtils.RequestPost(userInfoUrl(authToken), null, reqParams);
var userInfo = response;
var userObj = userInfo.parseObject();
this.checkResponse(userObj);
var authUser = new AuthUser();
authUser.uuid = userObj.getString("sub");
authUser.username = userObj.getString("email");
authUser.nickname = userObj.getString("name");
authUser.avatar = userObj.getString("picture");
authUser.location = userObj.getString("locale");
authUser.email = userObj.getString("email");
authUser.gender = AuthUserGender.UNKNOWN;
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = userObj;
authUser.originalUserStr = response;
return authUser;
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.clientId)
.queryParam("scope", config.scope.IsNullOrWhiteSpace() ? "openid%20email%20profile" : config.scope)
.queryParam("redirect_uri", config.redirectUri)
.queryParam("state", getRealState(state))
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
protected override string userInfoUrl(AuthToken authToken)
{
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.accessToken)
.build();
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常则返回Exception
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("error") || dic.ContainsKey("error_description"))
{
throw new Exception($"{dic.getString("error")}: {dic.getString("error_description")}");
}
}
}

View File

@@ -0,0 +1,196 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class HuaweiAuthRequest : DefaultAuthRequest
{
public HuaweiAuthRequest(ClientConfig config) : base(config, new HuaweiAuthSource())
{
}
public HuaweiAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new HuaweiAuthSource(), authStateCache)
{
}
/**
* 获取access token
*
* @param authCallback 授权成功后的回调参数
* @return token
* @see AuthDefaultRequest#authorize()
* @see AuthDefaultRequest#authorize(String)
*/
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
var reqParams = new Dictionary<string, object>
{
{ "grant_type", "authorization_code" },
{ "code", authCallback.authorization_code },
{ "client_id", config.clientId },
{ "client_secret", config.clientSecret },
{ "redirect_uri", config.redirectUri },
};
var response = HttpUtils.RequestFormPost(source.accessToken(), reqParams.spellParams());
return getAuthToken(response);
}
/**
* 使用token换取用户信息
*
* @param authToken token信息
* @return 用户信息
* @see AuthDefaultRequest#getAccessToken(AuthCallback)
*/
protected override AuthUser getUserInfo(AuthToken authToken)
{
var reqParams = new Dictionary<string, object>
{
{ "nsp_ts", DateTime.Now.Ticks },
{ "access_token", authToken.accessToken },
{ "nsp_fmt", "JS" },
{ "nsp_svc", "OpenUP.User.getInfo" },
};
var response = HttpUtils.RequestFormPost(source.userInfo(), reqParams.spellParams());
var userObj = response.parseObject();
this.checkResponse(userObj);
AuthUserGender gender = getRealGender(userObj);
var authUser = new AuthUser();
authUser.uuid = userObj.getString("userID");
authUser.username = userObj.getString("userName");
authUser.nickname = userObj.getString("userName");
authUser.gender = gender;
authUser.avatar = userObj.getString("headPictureURL");
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = userObj;
authUser.originalUserStr = response;
return authUser;
}
/**
* 刷新access token (续期)
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
*/
public override AuthResponse refresh(AuthToken authToken)
{
var reqParams = new Dictionary<string, object>
{
{ "client_id", config.clientId },
{ "client_secret", config.clientSecret },
{ "refresh_token", authToken.refreshToken },
{ "grant_type", "refresh_token" },
};
var response = HttpUtils.RequestFormPost(source.refresh(), reqParams.spellParams());
return new AuthResponse(AuthResponseStatus.SUCCESS.GetCode(), AuthResponseStatus.SUCCESS.GetDesc(), getAuthToken(response));
}
private AuthToken getAuthToken(string response)
{
var authTokenObj = response.parseObject();
this.checkResponse(authTokenObj);
var authToken = new AuthToken();
authToken.accessToken = authTokenObj.getString("access_token");
authToken.refreshToken = authTokenObj.getString("refresh_token");
authToken.expireIn = authTokenObj.getInt32("expires_in");
return authToken;
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.clientId)
.queryParam("redirect_uri", config.redirectUri)
.queryParam("access_type", "offline")
.queryParam("scope", config.scope.IsNullOrWhiteSpace() ? "https%3A%2F%2Fwww.huawei.com%2Fauth%2Faccount%2Fbase.profile" : config.scope)
.queryParam("state", getRealState(state))
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
protected override string accessTokenUrl(string code)
{
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("grant_type", "authorization_code")
.queryParam("code", code)
.queryParam("client_id", config.clientId)
.queryParam("client_secret", config.clientSecret)
.queryParam("redirect_uri", config.redirectUri)
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken token
* @return 返回获取userInfo的url
*/
protected override string userInfoUrl(AuthToken authToken)
{
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("nsp_ts", DateTime.Now.Ticks)
.queryParam("access_token", authToken.accessToken)
.queryParam("nsp_fmt", "JS")
.queryParam("nsp_svc", "OpenUP.User.getInfo")
.build();
}
/**
* 获取用户的实际性别。华为系统中用户的性别1表示女0表示男
*
* @param object obj
* @return AuthUserGender
*/
private AuthUserGender getRealGender(Dictionary<string, object> userObj)
{
int genderCodeInt = userObj.getInt32("gender");
string genderCode = genderCodeInt == 1 ? "0" : (genderCodeInt == 0) ? "1" : genderCodeInt + "";
return GlobalAuthUtil.getRealGender(genderCode);
}
/**
* 校验响应结果
*
* @param object 接口返回的结果
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("NSP_STATUS"))
{
throw new Exception(dic.getString("error"));
}
if (dic.ContainsKey("error"))
{
throw new Exception(dic.getString("sub_error") + ":" + dic.getString("error_description"));
}
}
}

View File

@@ -0,0 +1,132 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class KujialeAuthRequest : DefaultAuthRequest
{
public KujialeAuthRequest(ClientConfig config) : base(config, new KujialeAuthSource())
{
}
public KujialeAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new KujialeAuthSource(), authStateCache)
{
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
* 默认只向用户请求用户信息授权
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.11.0
*/
public override string authorize(string state)
{
var urlBuilder = UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.clientId)
.queryParam("redirect_uri", config.redirectUri)
.queryParam("state", getRealState(state))
.queryParam("scope", config.scope.IsNullOrWhiteSpace() ? "get_user_info" : config.scope)
.build();
return urlBuilder;
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
var response = doPostAuthorizationCode(authCallback.code);
return getAuthToken(response);
}
private AuthToken getAuthToken(string response)
{
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var resultObject = accessTokenObject.getJSONObject("d");
var authToken = new AuthToken();
authToken.accessToken = resultObject.getString("accessToken");
authToken.refreshToken = resultObject.getString("refreshToken");
authToken.expireIn = resultObject.getInt32("expiresIn");
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
string openId = this.getOpenId(authToken);
var userInfoUrl = UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.accessToken)
.queryParam("open_id", openId)
.build();
var response = HttpUtils.RequestGet(userInfoUrl);
var resObj = response.parseObject();
this.checkResponse(resObj);
var userObj = resObj.getJSONObject("d");
var authUser = new AuthUser();
authUser.uuid = userObj.getString("openId");
authUser.username = userObj.getString("userName");
authUser.nickname = userObj.getString("userName");
authUser.avatar = userObj.getString("avatar");
authUser.gender = AuthUserGender.UNKNOWN;
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = resObj;
authUser.originalUserStr = response;
return authUser;
}
/**
* 获取酷家乐的openId此id在当前client范围内可以唯一识别授权用户
*
* @param authToken 通过{@link AuthKujialeRequest#getAccessToken(AuthCallback)}获取到的{@code authToken}
* @return openId
*/
private string getOpenId(AuthToken authToken)
{
var openIdUrl = UrlBuilder.fromBaseUrl("https://oauth.kujiale.com/oauth2/auth/user")
.queryParam("access_token", authToken.accessToken)
.build();
var response = HttpUtils.RequestGet(openIdUrl);
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
return accessTokenObject.getString("d");
}
public override AuthResponse refresh(AuthToken authToken)
{
var refreshUrl = refreshTokenUrl(authToken.refreshToken);
var response = HttpUtils.RequestPost(refreshUrl);
return new AuthResponse(AuthResponseStatus.SUCCESS.GetCode(), AuthResponseStatus.SUCCESS.GetDesc(), getAuthToken(response));
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常则返回Exception
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.Count == 0)
{
throw new Exception("请求所返回的数据为空!");
}
if (!"0".Equals(dic.getString("c")))
{
throw new Exception($"{dic.getString("m")}");
}
}
}

View File

@@ -0,0 +1,234 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class LinkedInAuthRequest : DefaultAuthRequest
{
public LinkedInAuthRequest(ClientConfig config) : base(config, new LinkedInAuthSource())
{
}
public LinkedInAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new LinkedInAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
return this.getToken(accessTokenUrl(authCallback.code));
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
var accessToken = authToken.accessToken;
var reqParams = new Dictionary<string, object>
{
{ "Host", "api.linkedin.com" },
{ "Connection", "Keep-Alive" },
{ "Authorization", "Bearer " + accessToken }
};
var response = HttpUtils.RequestGet(userInfoUrl(authToken), reqParams);
var userObj = response.parseObject();
this.checkResponse(userObj);
string userName = getUserName(userObj);
// 获取用户头像
string avatar = this.getAvatar(userObj);
// 获取用户邮箱地址
string email = this.getUserEmail(accessToken);
var authUser = new AuthUser
{
uuid = userObj.getString("id"),
username = userName,
nickname = userName,
avatar = avatar,
email = email,
gender = AuthUserGender.UNKNOWN,
token = authToken,
source = source.getName(),
originalUser = userObj,
originalUserStr = response
};
return authUser;
}
/**
* 获取用户的真实名
*
* @param userInfoObject 用户json对象
* @return 用户名
*/
private string getUserName(Dictionary<string, object> userInfoObject)
{
string firstName, lastName;
// 获取firstName
if (userInfoObject.ContainsKey("localizedFirstName"))
{
firstName = userInfoObject.getString("localizedFirstName");
}
else
{
firstName = getUserName(userInfoObject, "firstName");
}
// 获取lastName
if (userInfoObject.ContainsKey("localizedLastName"))
{
lastName = userInfoObject.getString("localizedLastName");
}
else
{
lastName = getUserName(userInfoObject, "lastName");
}
return firstName + " " + lastName;
}
/**
* 获取用户的头像
*
* @param userInfoObject 用户json对象
* @return 用户的头像地址
*/
private string getAvatar(Dictionary<string, object> userInfoObject)
{
string avatar = null;
var profilePictureObject = userInfoObject.getJSONObject("profilePicture");
if (profilePictureObject.ContainsKey("displayImage~"))
{
var displayImageElements = profilePictureObject.getJSONObject("displayImage~")
.getJSONArray("elements");
if (null != displayImageElements && displayImageElements.Count > 0)
{
var largestImageObj = displayImageElements[displayImageElements.Count - 1];
avatar = largestImageObj.getJSONArray("identifiers")[0].getString("identifier");
}
}
return avatar;
}
/**
* 获取用户的email
*
* @param accessToken 用户授权后返回的token
* @return 用户的邮箱地址
*/
private string getUserEmail(string accessToken)
{
string email = null;
var reqParams = new Dictionary<string, object>();
reqParams.Add("Host", "api.linkedin.com");
reqParams.Add("Connection", "Keep-Alive");
reqParams.Add("Authorization", "Bearer " + accessToken);
var emailResponse = HttpUtils.RequestGet("https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))", reqParams);
var emailObj = emailResponse.parseObject();
this.checkResponse(emailObj);
var listObject = emailObj.getJSONArray("elements");
if (listObject != null && listObject.Count > 0)
{
email = listObject[listObject.Count - 1].getJSONObject("handle~").getString("emailAddress");
}
return email;
}
private string getUserName(Dictionary<string, object> userInfoObject, string nameKey)
{
string firstName;
var firstNameObj = userInfoObject.getJSONObject(nameKey);
var localizedObj = firstNameObj.getJSONObject("localized");
var preferredLocaleObj = firstNameObj.getJSONObject("preferredLocale");
firstName = localizedObj.getString(preferredLocaleObj.getString("language") + "_" + preferredLocaleObj.getString("country"));
return firstName;
}
public override AuthResponse refresh(AuthToken oldToken)
{
string refreshToken = oldToken.refreshToken;
if (refreshToken.IsNullOrWhiteSpace())
{
throw new Exception(AuthResponseStatus.REQUIRED_REFRESH_TOKEN.GetDesc());
}
string refreshTokenUrl = this.refreshTokenUrl(refreshToken);
return new AuthResponse(AuthResponseStatus.SUCCESS.GetCode(), AuthResponseStatus.SUCCESS.GetDesc(), this.getToken(refreshTokenUrl));
}
/**
* 获取token适用于获取access_token和刷新token
*
* @param accessTokenUrl 实际请求token的地址
* @return token对象
*/
private AuthToken getToken(string accessTokenUrl)
{
var reqParams = new Dictionary<string, object>();
reqParams.Add("Host", "www.linkedin.com");
reqParams.Add("Content-Type", "application/x-www-form-urlencoded");
string response = HttpUtils.RequestPost(accessTokenUrl, null, reqParams);
string accessTokenStr = response;
var accessTokenObject = accessTokenStr.parseObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken();
authToken.accessToken = accessTokenObject.getString("access_token");
authToken.refreshToken = accessTokenObject.getString("refresh_token");
authToken.expireIn = accessTokenObject.getInt32("expire_in");
return authToken;
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.clientId)
.queryParam("redirect_uri", config.redirectUri)
.queryParam("scope", config.scope.IsNullOrWhiteSpace() ? "r_liteprofile%20r_emailaddress%20w_member_social" : config.scope)
.queryParam("state", getRealState(state))
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
protected override string userInfoUrl(AuthToken authToken)
{
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("projection", "(id,firstName,lastName,profilePicture(displayImage~:playableStreams))")
.build();
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常则返回Exception
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("error"))
{
throw new Exception($"{dic.getString("error_description")}");
}
}
}

View File

@@ -0,0 +1,125 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class MeituanAuthRequest : DefaultAuthRequest
{
public MeituanAuthRequest(ClientConfig config) : base(config, new MeituanAuthSource())
{
}
public MeituanAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new MeituanAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
var reqParams = new Dictionary<string, object>
{
{ "app_id", config.clientId },
{ "secret", config.clientSecret },
{ "code", authCallback.code },
{ "grant_type", "authorization_code" },
};
var response = HttpUtils.RequestFormPost(source.accessToken(), reqParams.spellParams());
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken
{
accessToken = accessTokenObject.getString("access_token"),
expireIn = accessTokenObject.getInt32("expires_in"),
refreshToken = accessTokenObject.getString("refresh_token"),
code = authCallback.code
};
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
var reqParams = new Dictionary<string, object>
{
{ "app_id", config.clientId },
{ "secret", config.clientSecret },
{ "access_token", authToken.accessToken },
};
var response = HttpUtils.RequestFormPost(source.userInfo(), reqParams.spellParams());
var userObj = response.parseObject();
this.checkResponse(userObj);
var authUser = new AuthUser
{
uuid = userObj.getString("openid"),
username = userObj.getString("nickname"),
nickname = userObj.getString("nickname"),
avatar = userObj.getString("avatar"),
gender = AuthUserGender.UNKNOWN,
token = authToken,
source = source.getName(),
originalUser = userObj,
originalUserStr = response
};
return authUser;
}
public override AuthResponse refresh(AuthToken oldToken)
{
var reqParams = new Dictionary<string, object>
{
{ "app_id", config.clientId },
{ "secret", config.clientSecret },
{ "refresh_token", oldToken.refreshToken },
{ "grant_type", "refresh_token" },
};
var response = HttpUtils.RequestFormPost(source.refresh(), reqParams.spellParams());
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken
{
accessToken = accessTokenObject.getString("access_token"),
refreshToken = accessTokenObject.getString("refresh_token"),
expireIn = accessTokenObject.getInt32("expires_in")
};
return new AuthResponse(AuthResponseStatus.SUCCESS.GetCode(), AuthResponseStatus.SUCCESS.GetDesc(), authToken);
}
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("app_id", config.clientId)
.queryParam("redirect_uri", config.redirectUri)
.queryParam("state", getRealState(state))
.queryParam("scope", config.scope)
.build();
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常则返回Exception
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("error_code"))
{
throw new Exception($"{dic.getString("error_msg")}");
}
}
}

View File

@@ -0,0 +1,176 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
using Newtonsoft.Json;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class MicrosoftAuthRequest : DefaultAuthRequest
{
public MicrosoftAuthRequest(ClientConfig config) : base(config, new MicrosoftAuthSource())
{
}
public MicrosoftAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new MicrosoftAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
return getToken(accessTokenUrl(authCallback.code));
}
/**
* 获取token适用于获取access_token和刷新token
*
* @param accessTokenUrl 实际请求token的地址
* @return token对象
*/
private AuthToken getToken(string accessTokenUrl)
{
var reqParams = new Dictionary<string, object>
{
{ "Host", "https://login.microsoftonline.com" },
{ "Content-Type", "application/x-www-form-urlencoded" },
};
var reqParamDic = GlobalAuthUtil.parseUrlObject(accessTokenUrl);
var response = HttpUtils.RequestPost(accessTokenUrl, JsonConvert.SerializeObject(reqParamDic), reqParams);
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken();
authToken.accessToken = accessTokenObject.getString("access_token");
authToken.tokenType = accessTokenObject.getString("token_type");
authToken.expireIn = accessTokenObject.getInt32("expires_in");
authToken.refreshToken = accessTokenObject.getString("refresh_token");
authToken.scope = accessTokenObject.getString("scope");
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
var token = authToken.accessToken;
var tokenType = authToken.tokenType;
var jwt = tokenType + " " + token;
var reqParams = new Dictionary<string, object>
{
{ "Authorization", jwt },
};
var response = HttpUtils.RequestGet(userInfoUrl(authToken), reqParams);
var userObj = response.parseObject();
this.checkResponse(userObj);
var authUser = new AuthUser();
authUser.uuid = userObj.getString("id");
authUser.username = userObj.getString("userPrincipalName");
authUser.nickname = userObj.getString("displayName");
authUser.location = userObj.getString("officeLocation");
authUser.email = userObj.getString("mail");
authUser.gender = AuthUserGender.UNKNOWN;
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = userObj;
authUser.originalUserStr = response;
return authUser;
}
/**
* 刷新access token (续期)
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
*/
public override AuthResponse refresh(AuthToken authToken)
{
var token = getToken(refreshTokenUrl(authToken.refreshToken));
return new AuthResponse(AuthResponseStatus.SUCCESS.GetCode(), AuthResponseStatus.SUCCESS.GetDesc(), token);
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.clientId)
.queryParam("redirect_uri", config.redirectUri)
.queryParam("response_mode", "query")
.queryParam("scope", "offline_access%20" + (config.scope.IsNullOrWhiteSpace() ? "user.read%20mail.read" : config.scope))
.queryParam("state", getRealState(state))
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权code
* @return 返回获取accessToken的url
*/
protected override string accessTokenUrl(string code)
{
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("code", code)
.queryParam("client_id", config.clientId)
.queryParam("client_secret", config.clientSecret)
.queryParam("grant_type", "authorization_code")
.queryParam("scope", config.scope.IsNullOrWhiteSpace() ? "user.read%20mail.read" : config.scope)
.queryParam("redirect_uri", config.redirectUri)
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
protected override string userInfoUrl(AuthToken authToken)
{
return UrlBuilder.fromBaseUrl(source.userInfo()).build();
}
/**
* 返回获取accessToken的url
*
* @param refreshToken 用户授权后的token
* @return 返回获取accessToken的url
*/
protected override string refreshTokenUrl(string refreshToken)
{
return UrlBuilder.fromBaseUrl(source.refresh())
.queryParam("client_id", config.clientId)
.queryParam("client_secret", config.clientSecret)
.queryParam("refresh_token", refreshToken)
.queryParam("grant_type", "refresh_token")
.queryParam("scope", config.scope.IsNullOrWhiteSpace() ? "user.read%20mail.read" : config.scope)
.queryParam("redirect_uri", config.redirectUri)
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("error"))
{
throw new Exception(dic.getString("error_description"));
}
}
}

View File

@@ -0,0 +1,104 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class OschinaAuthRequest : DefaultAuthRequest
{
public OschinaAuthRequest(ClientConfig config) : base(config, new OschinaAuthSource())
{
}
public OschinaAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new OschinaAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
string response = doPostAuthorizationCode(authCallback.code);
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken();
authToken.accessToken = accessTokenObject.getString("access_token");
authToken.refreshToken = accessTokenObject.getString("refresh_token");
authToken.uid = accessTokenObject.getString("uid");
authToken.expireIn = accessTokenObject.getInt32("expires_in");
authToken.code = authCallback.code;
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
string response = doGetUserInfo(authToken);
var userObj = response.parseObject();
this.checkResponse(userObj);
var authUser = new AuthUser();
authUser.uuid = userObj.getString("id");
authUser.username = userObj.getString("name");
authUser.nickname = userObj.getString("name");
authUser.avatar = userObj.getString("avatar");
authUser.blog = userObj.getString("url");
authUser.location = userObj.getString("location");
authUser.email = userObj.getString("email");
authUser.gender = GlobalAuthUtil.getRealGender(userObj.getString("gender"));
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = userObj;
authUser.originalUserStr = response;
return authUser;
}
/**
* 返回获取accessToken的url
*
* @param code 授权回调时带回的授权码
* @return 返回获取accessToken的url
*/
protected override string accessTokenUrl(string code)
{
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("code", code)
.queryParam("client_id", config.clientId)
.queryParam("client_secret", config.clientSecret)
.queryParam("grant_type", "authorization_code")
.queryParam("redirect_uri", config.redirectUri)
.queryParam("dataType", "json")
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
protected override string userInfoUrl(AuthToken authToken)
{
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.accessToken)
.queryParam("dataType", "json")
.build();
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常则返回Exception
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("error"))
{
throw new Exception($"{dic.getString("error_description")}");
}
}
}

View File

@@ -0,0 +1,116 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class PinterestAuthRequest : DefaultAuthRequest
{
public PinterestAuthRequest(ClientConfig config) : base(config, new PinterestAuthSource())
{
}
public PinterestAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new PinterestAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
var response = doPostAuthorizationCode(authCallback.code);
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken
{
accessToken = accessTokenObject.getString("access_token"),
tokenType = accessTokenObject.getString("token_type"),
code = authCallback.code
};
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
string userinfoUrl = userInfoUrl(authToken);
var response = HttpUtils.RequestGet(userinfoUrl);
var responseObj = response.parseObject();
this.checkResponse(responseObj);
var userObj = responseObj.getString("data").parseObject();
var authUser = new AuthUser();
authUser.uuid = userObj.getString("id");
authUser.username = userObj.getString("username");
authUser.nickname = userObj.getString("first_name") + userObj.getString("last_name");
authUser.avatar = getAvatarUrl(userObj);
authUser.remark = userObj.getString("bio");
authUser.gender = AuthUserGender.UNKNOWN;
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = responseObj;
authUser.originalUserStr = response;
return authUser;
}
private string getAvatarUrl(Dictionary<string, object> userObj)
{
// image is a map data structure
var jsonObject = userObj.getString("image").parseObject();
if (jsonObject.Count == 0)
{
return null;
}
return jsonObject.getString("60x60").parseObject().getString("url");
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.clientId)
.queryParam("redirect_uri", config.redirectUri)
.queryParam("scope", config.scope.IsNullOrWhiteSpace() ? "read_public" : config.scope)
.queryParam("state", getRealState(state))
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken token
* @return 返回获取userInfo的url
*/
protected override string userInfoUrl(AuthToken authToken)
{
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.accessToken)
.queryParam("fields", "id,username,first_name,last_name,bio,image")
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("status") && "failure".Equals(dic.getString("status")))
{
throw new Exception($"{dic.getString("message")}");
}
}
}

View File

@@ -0,0 +1,132 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class QQAuthRequest : DefaultAuthRequest
{
public QQAuthRequest(ClientConfig config) : base(config, new QQAuthSource())
{
}
public QQAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new QQAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
string response = doGetAuthorizationCode(authCallback.code);
return getAuthToken(response);
}
public override AuthResponse refresh(AuthToken authToken)
{
string response = HttpUtils.RequestGet(refreshTokenUrl(authToken.refreshToken));
return new AuthResponse(AuthResponseStatus.SUCCESS.GetCode(), AuthResponseStatus.SUCCESS.GetDesc(), getAuthToken(response));
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
string openId = this.getOpenId(authToken);
string response = doGetUserInfo(authToken);
var userObj = response.parseObject();
if (userObj.getInt32("ret") != 0)
{
throw new Exception(userObj.getString("msg"));
}
string avatar = userObj.getString("figureurl_qq_2");
if (avatar.IsNullOrWhiteSpace())
{
avatar = userObj.getString("figureurl_qq_1");
}
string location = $"{userObj.getString("province")}-{userObj.getString("city")}";
var authUser = new AuthUser();
authUser.uuid = openId;
authUser.username = userObj.getString("nickname");
authUser.nickname = userObj.getString("nickname");
authUser.avatar = avatar;
authUser.location = location;
authUser.email = userObj.getString("email");
authUser.remark = userObj.getString("bio");
authUser.gender = GlobalAuthUtil.getRealGender(userObj.getString("gender"));
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = userObj;
authUser.originalUserStr = response;
return authUser;
}
/**
* 获取QQ用户的OpenId支持自定义是否启用查询unionid的功能如果启用查询unionid的功能
* 那就需要开发者先通过邮件申请unionid功能参考链接 {@see http://wiki.connect.qq.com/unionid%E4%BB%8B%E7%BB%8D}
*
* @param authToken 通过{@link AuthQqRequest#getAccessToken(AuthCallback)}获取到的{@code authToken}
* @return openId
*/
private string getOpenId(AuthToken authToken)
{
var getOpenIdUrl = UrlBuilder.fromBaseUrl("https://graph.qq.com/oauth2.0/me")
.queryParam("access_token", authToken.accessToken)
.queryParam("unionid", config.unionId)
.build();
string response = HttpUtils.RequestGet(getOpenIdUrl);
if (!response.IsNullOrWhiteSpace())
{
string body = response;
string removePrefix = body.Replace("callback(", "");
string removeSuffix = removePrefix.Replace(");", "");
string openId = removeSuffix.Trim();
var openIdObj = openId.parseObject();
if (openIdObj.ContainsKey("error"))
{
throw new Exception(openIdObj.getString("error") + ":" + openIdObj.getString("error_description"));
}
authToken.openId = openIdObj.getString("openid");
if (openIdObj.ContainsKey("unionid"))
{
authToken.unionId = openIdObj.getString("unionid");
}
return authToken.unionId.IsNullOrWhiteSpace() ? authToken.openId : authToken.unionId;
}
throw new Exception("request error");
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权token
* @return 返回获取userInfo的url
*/
protected override string userInfoUrl(AuthToken authToken)
{
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.accessToken)
.queryParam("oauth_consumer_key", config.clientId)
.queryParam("openid", authToken.openId)
.build();
}
private AuthToken getAuthToken(string response)
{
var accessTokenObject = response.parseStringObject();
if (!accessTokenObject.ContainsKey("access_token") || accessTokenObject.ContainsKey("code"))
{
throw new Exception(accessTokenObject.getString("msg"));
}
var authToken = new AuthToken();
authToken.accessToken = accessTokenObject.getString("access_token");
authToken.expireIn = accessTokenObject.getInt32("expires_in");
authToken.refreshToken = accessTokenObject.getString("refresh_token");
return authToken;
}
}

View File

@@ -0,0 +1,124 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class RenrenAuthRequest : DefaultAuthRequest
{
public RenrenAuthRequest(ClientConfig config) : base(config, new RenrenAuthSource())
{
}
public RenrenAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new RenrenAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
return this.getToken(accessTokenUrl(authCallback.code));
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
var response = doGetUserInfo(authToken);
var userObj = response.parseObject().getJSONObject("response");
var authUser = new AuthUser();
authUser.uuid = userObj.getString("id");
authUser.username = userObj.getString("name");
authUser.nickname = userObj.getString("name");
authUser.avatar = getAvatarUrl(userObj);
authUser.company = getCompany(userObj);
authUser.gender = getGender(userObj);
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = userObj;
authUser.originalUserStr = response;
return authUser;
}
public override AuthResponse refresh(AuthToken authToken)
{
var token = getToken(this.refreshTokenUrl(authToken.refreshToken));
return new AuthResponse(AuthResponseStatus.SUCCESS.GetCode(), AuthResponseStatus.SUCCESS.GetDesc(), token);
}
private AuthToken getToken(string url)
{
var response = HttpUtils.RequestPost(url);
var jsonObject = response.parseObject();
if (jsonObject.ContainsKey("error"))
{
throw new Exception("Failed to get token from Renren: " + jsonObject);
}
var authToken = new AuthToken();
authToken.accessToken = jsonObject.getString("access_token");
authToken.tokenType = jsonObject.getString("token_type");
authToken.expireIn = jsonObject.getInt32("expires_in");
authToken.refreshToken = jsonObject.getString("refresh_token");
authToken.openId = jsonObject.getJSONObject("user").getString("id");
return authToken;
}
private string getAvatarUrl(Dictionary<string, object> userObj)
{
var jsonArray = userObj.getJSONArray("avatar");
if (jsonArray.Count == 0)
{
return null;
}
return jsonArray[0].getString("url");
}
private AuthUserGender getGender(Dictionary<string, object> userObj)
{
var basicInformation = userObj.getJSONObject("basicInformation");
if (basicInformation.Count == 0)
{
return AuthUserGender.UNKNOWN;
}
return GlobalAuthUtil.getRealGender(basicInformation.getString("sex"));
}
private string getCompany(Dictionary<string, object> userObj)
{
var jsonArray = userObj.getJSONArray("work");
if (jsonArray.Count == 0)
{
return null;
}
return jsonArray[0].getString("name");
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
protected override string userInfoUrl(AuthToken authToken)
{
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.accessToken)
.queryParam("userId", authToken.openId)
.build();
}
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.clientId)
.queryParam("redirect_uri", config.redirectUri)
.queryParam("state", getRealState(state))
.queryParam("display", "page")
.build();
}
}

View File

@@ -0,0 +1,101 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class StackOverflowAuthRequest : DefaultAuthRequest
{
public StackOverflowAuthRequest(ClientConfig config) : base(config, new StackOverflowAuthSource())
{
}
public StackOverflowAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new StackOverflowAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
string accessTokenUrl = this.accessTokenUrl(authCallback.code);
var reqHeaders = new Dictionary<string, object>
{
{ "Content-Type", "application/x-www-form-urlencoded" },
};
var reqParams = accessTokenUrl.parseUrlObject();
var response = HttpUtils.RequestPost(source.accessToken(), reqParams.spellParams(), reqHeaders);
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken();
authToken.accessToken = accessTokenObject.getString("access_token");
authToken.expireIn = accessTokenObject.getInt32("expires");
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
string userInfoUrl = UrlBuilder.fromBaseUrl(this.source.userInfo())
.queryParam("access_token", authToken.accessToken)
.queryParam("site", "stackoverflow")
.queryParam("key", this.config.stackOverflowKey)
.build();
var response = HttpUtils.RequestGet(userInfoUrl);
var responseObj = response.parseObject();
this.checkResponse(responseObj);
var userObj = responseObj.getString("items").parseListObject()[0];
var authUser = new AuthUser();
authUser.uuid = userObj.getString("user_id");
authUser.username = userObj.getString("username");
authUser.nickname = userObj.getString("display_name");
authUser.avatar = userObj.getString("profile_image");
authUser.location = userObj.getString("location");
authUser.gender = AuthUserGender.UNKNOWN;
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = responseObj;
authUser.originalUserStr = response;
return authUser;
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.clientId)
.queryParam("redirect_uri", config.redirectUri)
.queryParam("scope", config.scope.IsNullOrWhiteSpace() ? "read_inbox" : config.scope)
.queryParam("state", getRealState(state))
.build();
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("error"))
{
throw new Exception($"{dic.getString("error_description")}");
}
}
}

View File

@@ -0,0 +1,119 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class TeambitionAuthRequest : DefaultAuthRequest
{
public TeambitionAuthRequest(ClientConfig config) : base(config, new GithubAuthSource())
{
}
public TeambitionAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new GithubAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
var reqHeaders = new Dictionary<string, object>
{
{ "Content-Type", "application/x-www-form-urlencoded" },
};
var reqParams = new Dictionary<string, object>
{
{ "client_id", config.clientId },
{ "client_secret", config.clientSecret },
{ "code", authCallback.code },
{ "grant_type", "code" },
};
var response = HttpUtils.RequestPost(source.accessToken(), reqParams.spellParams(), reqHeaders);
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken();
authToken.accessToken = accessTokenObject.getString("access_token");
authToken.refreshToken = accessTokenObject.getString("refresh_token");
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
var accessToken = authToken.accessToken;
var reqHeaders = new Dictionary<string, object>
{
{ "Authorization", "OAuth2 " + accessToken },
};
var response = HttpUtils.RequestGet(source.userInfo(), reqHeaders);
var userObj = response.parseObject();
this.checkResponse(userObj);
authToken.uid = userObj.getString("_id");
var authUser = new AuthUser();
authUser.uuid = userObj.getString("_id");
authUser.username = userObj.getString("name");
authUser.nickname = userObj.getString("name");
authUser.avatar = userObj.getString("avatarUrl");
authUser.blog = userObj.getString("website");
authUser.location = userObj.getString("location");
authUser.email = userObj.getString("email");
authUser.gender = AuthUserGender.UNKNOWN;
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = userObj;
authUser.originalUserStr = response;
return authUser;
}
public override AuthResponse refresh(AuthToken oldToken)
{
string uid = oldToken.uid;
string refreshToken = oldToken.refreshToken;
var reqHeaders = new Dictionary<string, object>
{
{ "Content-Type", "application/x-www-form-urlencoded" },
};
var reqParams = new Dictionary<string, object>
{
{ "_userId", uid },
{ "refresh_token", refreshToken },
};
var response = HttpUtils.RequestPost(source.refresh(), reqParams.spellParams(), reqHeaders);
var refreshTokenObject = response.parseObject();
this.checkResponse(refreshTokenObject);
var authToken = new AuthToken();
authToken.accessToken = refreshTokenObject.getString("access_token");
authToken.refreshToken = refreshTokenObject.getString("refresh_token");
return new AuthResponse(AuthResponseStatus.SUCCESS.GetCode(), AuthResponseStatus.SUCCESS.GetDesc(), authToken);
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常则返回Exception
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("message") && dic.ContainsKey("name"))
{
throw new Exception($"{dic.getString("getString")}, {dic.getString("name")}");
}
}
}

View File

@@ -0,0 +1,136 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class ToutiaoAuthRequest : DefaultAuthRequest
{
public ToutiaoAuthRequest(ClientConfig config) : base(config, new ToutiaoAuthSource())
{
}
public ToutiaoAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new ToutiaoAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
var response = doGetAuthorizationCode(authCallback.code);
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken();
authToken.accessToken = accessTokenObject.getString("access_token");
authToken.expireIn = accessTokenObject.getInt32("expires_in");
authToken.openId = accessTokenObject.getString("open_id");
authToken.code = authCallback.code;
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
string userResponse = doGetUserInfo(authToken);
var userProfile = userResponse.parseObject();
this.checkResponse(userProfile);
var userObj = userProfile.getString("data").parseObject();
bool isAnonymousUser = userObj.getInt32("uid_type") == 14;
string anonymousUserName = "匿名用户";
var authUser = new AuthUser();
authUser.uuid = userObj.getString("uid");
authUser.username = isAnonymousUser ? anonymousUserName : userObj.getString("screen_name");
authUser.nickname = isAnonymousUser ? anonymousUserName : userObj.getString("screen_name");
authUser.avatar = userObj.getString("avatar_url");
authUser.remark = userObj.getString("description");
authUser.gender = GlobalAuthUtil.getRealGender(userObj.getString("gender"));
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = userProfile;
authUser.originalUserStr = userResponse;
return authUser;
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_key", config.clientId)
.queryParam("redirect_uri", config.redirectUri)
.queryParam("auth_only", 1)
.queryParam("display", 0)
.queryParam("state", getRealState(state))
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
protected override string accessTokenUrl(string code)
{
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("code", code)
.queryParam("client_key", config.clientId)
.queryParam("client_secret", config.clientSecret)
.queryParam("grant_type", "authorization_code")
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
protected override string userInfoUrl(AuthToken authToken)
{
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("client_key", config.clientId)
.queryParam("access_token", authToken.accessToken)
.build();
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常则返回Exception
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("error_code"))
{
throw new Exception(getToutiaoErrorCode(dic.getInt32("error_code")).GetDesc());
}
}
private AuthToutiaoErrorCode getToutiaoErrorCode(int errorCode)
{
var enumObjects = typeof(AuthToutiaoErrorCode).ToList();
var codeEnum = enumObjects.Where(a => a.ID == errorCode).ToList();
if (codeEnum.Count > 0)
{
return GlobalAuthUtil.enumFromString<AuthToutiaoErrorCode>(codeEnum[0].Name);
}
else
{
return AuthToutiaoErrorCode.EC999;
}
}
}

View File

@@ -0,0 +1,173 @@
//using JNPF.Extras.CollectiveOAuth.Cache;
//using JNPF.Extras.CollectiveOAuth.Config;
//using JNPF.Extras.CollectiveOAuth.Models;
//using JNPF.Extras.CollectiveOAuth.Utils;
//using System;
//using System.Collections.Generic;
//using JNPF.Extras.CollectiveOAuth.Enums;
//
//namespace JNPF.Extras.CollectiveOAuth.Request
//{
// public class TwitteAuthRequest : DefaultAuthRequest
// {
// public TwitteAuthRequest(ClientConfig config) : base(config, new TwitterAuthSource())
// {
// }
//
// public TwitteAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
// : base(config, new TwitterAuthSource(), authStateCache)
// {
// }
//
// /**
// * Obtaining a request token
// * https://developer.twitter.com/en/docs/twitter-for-websites/log-in-with-twitter/guides/implementing-sign-in-with-twitter
// *
// * @return request token
// */
// public AuthToken getRequestToken()
// {
// String baseUrl = "https://api.twitter.com/oauth/request_token";
//
// Map<String, Object> oauthParams = buildOauthParams();
// oauthParams.put("oauth_callback", config.getRedirectUri());
// oauthParams.put("oauth_signature", generateTwitterSignature(oauthParams, "POST", baseUrl, config.getClientSecret(), null));
// String header = buildHeader(oauthParams);
// HttpResponse requestToken = HttpRequest.post(baseUrl).header("Authorization", header).execute();
// checkResponse(requestToken);
//
// Map<String, Object> res = GlobalAuthUtil.parseQueryToMap(requestToken.body());
//
// return AuthToken.builder()
// .oauthToken(res.get("oauth_token").toString())
// .oauthTokenSecret(res.get("oauth_token_secret").toString())
// .oauthCallbackConfirmed(Boolean.valueOf(res.get("oauth_callback_confirmed").toString()))
// .build();
// }
//
// /**
// * Convert request token to access token
// * https://developer.twitter.com/en/docs/twitter-for-websites/log-in-with-twitter/guides/implementing-sign-in-with-twitter
// *
// * @return access token
// */
// protected override AuthToken getAccessToken(AuthCallback authCallback)
// {
// //Sign the request
// /*using (HMACSHA1 hashAlgorithm = new HMACSHA1(new ASCIIEncoding().GetBytes(signingKey)))
// {
//
// return Convert.ToBase64String(
// hashAlgorithm.ComputeHash(
// new ASCIIEncoding().GetBytes(baseSignatureString)
// )
// );
// }*/
//
// Map<String, Object> oauthParams = buildOauthParams();
// oauthParams.put("oauth_token", authCallback.getOauthToken());
// oauthParams.put("oauth_verifier", authCallback.getOauthVerifier());
// oauthParams.put("oauth_signature", generateTwitterSignature(oauthParams, "POST", source.accessToken(), config.getClientSecret(), authCallback.getOauthToken()));
// String header = buildHeader(oauthParams);
// HttpResponse response = HttpRequest.post(source.accessToken())
// .header("Authorization", header)
// .header("Content-Type", "application/x-www-form-urlencoded")
// .form("oauth_verifier", authCallback.getOauthVerifier())
// .execute();
// checkResponse(response);
//
// Map<String, Object> requestToken = GlobalAuthUtil.parseQueryToMap(response.body());
//
// return AuthToken.builder()
// .oauthToken(requestToken.get("oauth_token").toString())
// .oauthTokenSecret(requestToken.get("oauth_token_secret").toString())
// .userId(requestToken.get("user_id").toString())
// .screenName(requestToken.get("screen_name").toString())
// .build();
// }
//
// protected override AuthUser getUserInfo(AuthToken authToken)
// {
// Map<String, Object> queryParams = new HashMap<>();
// queryParams.put("user_id", authToken.getUserId());
// queryParams.put("screen_name", authToken.getScreenName());
// queryParams.put("include_entities", true);
//
// Map<String, Object> oauthParams = buildOauthParams();
// oauthParams.put("oauth_token", authToken.getOauthToken());
//
// Map < String, Object > params = new HashMap<>(oauthParams);
// params.putAll(queryParams);
// oauthParams.put("oauth_signature", generateTwitterSignature(params, "GET", source.userInfo(), config.getClientSecret(), authToken.getOauthTokenSecret()));
// String header = buildHeader(oauthParams);
// HttpResponse response = HttpRequest.get(userInfoUrl(authToken)).header("Authorization", header).execute();
// checkResponse(response);
// JSONObject userInfo = JSONObject.parseObject(response.body());
//
// return AuthUser.builder()
// .uuid(userInfo.getString("id_str"))
// .username(userInfo.getString("screen_name"))
// .nickname(userInfo.getString("name"))
// .remark(userInfo.getString("description"))
// .avatar(userInfo.getString("profile_image_url_https"))
// .blog(userInfo.getString("url"))
// .location(userInfo.getString("location"))
// .source(source.toString())
// .token(authToken)
// .build();
// }
//
// protected override string userInfoUrl(AuthToken authToken)
// {
// return UrlBuilder.fromBaseUrl(source.userInfo())
// .queryParam("user_id", authToken.userId)
// .queryParam("screen_name", authToken.screenName)
// .queryParam("include_entities", true)
// .build();
// }
//
// private Map<String, Object> buildOauthParams()
// {
// Map < String, Object > params = new HashMap<>();
// params.put("oauth_consumer_key", config.getClientId());
// params.put("oauth_nonce", GlobalAuthUtil.generateNonce(32));
// params.put("oauth_signature_method", "HMAC-SHA1");
// params.put("oauth_timestamp", GlobalAuthUtil.getTimestamp());
// params.put("oauth_version", "1.0");
// return params;
// }
//
// private string buildHeader(Map<String, Object> oauthParams)
// {
// final StringBuilder sb = new StringBuilder(PREAMBLE);
//
// for (Map.Entry<String, Object> param : oauthParams.entrySet())
// {
// if (sb.length() > PREAMBLE.length())
// {
// sb.append(", ");
// }
// sb.append(param.getKey())
// .append("=\"")
// .append(urlEncode(param.getValue().toString()))
// .append('"');
// }
//
// return sb.toString();
// }
//
// /**
// * 校验请求结果
// *
// * @param response 请求结果
// * @return 如果请求结果正常则返回Exception
// */
// private void checkResponse(Dictionary<string, object> dic)
// {
// if (dic.Count == 0)
// {
// throw new Exception("请求返回数据为空");
// }
// }
// }
//}

View File

@@ -0,0 +1,148 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Enums;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class WeChatEnterpriseAuthRequest : DefaultAuthRequest
{
public WeChatEnterpriseAuthRequest(ClientConfig config) : base(config, new WechatEnterpriseAuthSource())
{
}
public WeChatEnterpriseAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new WechatEnterpriseAuthSource(), authStateCache)
{
}
/**
* 微信的特殊性,此时返回的信息同时包含 openid 和 access_token
*
* @param authCallback 回调返回的参数
* @return 所有信息
*/
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
string response = doGetAuthorizationCode(accessTokenUrl(authCallback.code));
var jsonObj = response.parseObject();
this.checkResponse(jsonObj);
var authToken = new AuthToken();
authToken.accessToken = jsonObj.getString("access_token");
authToken.expireIn = jsonObj.getInt32("expires_in");
authToken.code = authCallback.code;
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
string response = doGetUserInfo(authToken);
var jsonObj = response.parseObject();
this.checkResponse(jsonObj);
// 返回 OpenId 或其他,均代表非当前企业用户,不支持
if (!jsonObj.ContainsKey("UserId"))
{
throw new Exception(AuthResponseStatus.UNIDENTIFIED_PLATFORM.GetDesc());
}
string userId = jsonObj.getString("UserId");
string userDetailResponse = getUserDetail(authToken.accessToken, userId);
var userDetailObj = userDetailResponse.parseObject();
this.checkResponse(userDetailObj);
var authUser = new AuthUser();
authUser.username = userDetailObj.getString("name");
authUser.nickname = userDetailObj.getString("alias");
authUser.avatar = userDetailObj.getString("avatar");
authUser.location = userDetailObj.getString("address");
authUser.email = userDetailObj.getString("email");
authUser.uuid = userDetailObj.getString("userId");
authUser.token = authToken;
authUser.source = source.getName();
authUser.gender = GlobalAuthUtil.getWechatRealGender(userDetailObj.getString("gender"));
authUser.originalUser = userDetailObj;
authUser.originalUserStr = response;
return authUser;
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常则返回JSONObject
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("errcode") && dic.getInt32("errcode") != 0)
{
throw new Exception($"errcode: {dic.getString("errcode")}, errmsg: {dic.getString("errmsg")}");
}
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("appid", config.clientId)
.queryParam("response_type", "code")
.queryParam("redirect_uri", config.redirectUri)
.queryParam("scope", config.scope.IsNullOrWhiteSpace() ? "snsapi_userinfo" : config.scope)
.queryParam("state", getRealState(state) + "#wechat_redirect")
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
protected override string accessTokenUrl(String code)
{
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("corpid", config.clientId)
.queryParam("corpsecret", config.clientSecret)
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
protected override string userInfoUrl(AuthToken authToken)
{
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.accessToken)
.queryParam("code", authToken.code)
.build();
}
/**
* 用户详情
*
* @param accessToken accessToken
* @param userId 企业内用户id
* @return 用户详情
*/
private string getUserDetail(string accessToken, string userId)
{
string userDetailUrl = UrlBuilder.fromBaseUrl("https://qyapi.weixin.qq.com/cgi-bin/user/get")
.queryParam("access_token", accessToken)
.queryParam("userid", userId)
.build();
return HttpUtils.RequestGet(userDetailUrl);
}
}

View File

@@ -0,0 +1,148 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Enums;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class WeChatEnterpriseScanAuthRequest : DefaultAuthRequest
{
public WeChatEnterpriseScanAuthRequest(ClientConfig config) : base(config, new WechatEnterpriseScanAuthSource())
{
}
public WeChatEnterpriseScanAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new WechatEnterpriseScanAuthSource(), authStateCache)
{
}
/**
* 微信的特殊性,此时返回的信息同时包含 openid 和 access_token
*
* @param authCallback 回调返回的参数
* @return 所有信息
*/
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
string response = doGetAuthorizationCode(accessTokenUrl(authCallback.code));
var jsonObj = response.parseObject();
this.checkResponse(jsonObj);
var authToken = new AuthToken();
authToken.accessToken = jsonObj.getString("access_token");
authToken.expireIn = jsonObj.getInt32("expires_in");
authToken.code = authCallback.code;
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
string response = doGetUserInfo(authToken);
var jsonObj = response.parseObject();
this.checkResponse(jsonObj);
// 返回 OpenId 或其他,均代表非当前企业用户,不支持
if (!jsonObj.ContainsKey("UserId"))
{
throw new Exception(AuthResponseStatus.UNIDENTIFIED_PLATFORM.GetDesc());
}
string userId = jsonObj.getString("UserId");
string userDetailResponse = getUserDetail(authToken.accessToken, userId);
var userDetailObj = userDetailResponse.parseObject();
this.checkResponse(userDetailObj);
var authUser = new AuthUser();
authUser.username = userDetailObj.getString("name");
authUser.nickname = userDetailObj.getString("alias");
authUser.avatar = userDetailObj.getString("avatar");
authUser.location = userDetailObj.getString("address");
authUser.email = userDetailObj.getString("email");
authUser.uuid = userDetailObj.getString("userId");
if(authUser.uuid.IsNullOrWhiteSpace()) authUser.uuid = userDetailObj.getString("userid");
authUser.token = authToken;
authUser.source = source.getName();
authUser.gender = GlobalAuthUtil.getWechatRealGender(userDetailObj.getString("gender"));
authUser.originalUser = userDetailObj;
authUser.originalUserStr = response;
return authUser;
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常则返回JSONObject
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("errcode") && dic.getInt32("errcode") != 0)
{
throw new Exception($"errcode: {dic.getString("errcode")}, errmsg: {dic.getString("errmsg")}");
}
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("appid", config.clientId)
.queryParam("agentid", config.agentId)
.queryParam("redirect_uri", config.redirectUri)
.queryParam("state", getRealState(state))
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
protected override string accessTokenUrl(String code)
{
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("corpid", config.clientId)
.queryParam("corpsecret", config.clientSecret)
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
protected override string userInfoUrl(AuthToken authToken)
{
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.accessToken)
.queryParam("code", authToken.code)
.build();
}
/**
* 用户详情
*
* @param accessToken accessToken
* @param userId 企业内用户id
* @return 用户详情
*/
private string getUserDetail(string accessToken, string userId)
{
string userDetailUrl = UrlBuilder.fromBaseUrl("https://qyapi.weixin.qq.com/cgi-bin/user/get")
.queryParam("access_token", accessToken)
.queryParam("userid", userId)
.build();
return HttpUtils.RequestGet(userDetailUrl);
}
}

View File

@@ -0,0 +1,168 @@
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
using JNPF.Extras.CollectiveOAuth.Cache;
namespace JNPF.Extras.CollectiveOAuth.Request;
public partial class WeChatMpAuthRequest : DefaultAuthRequest
{
public WeChatMpAuthRequest(ClientConfig config) : base(config, new WechatMPAuthSource())
{
}
public WeChatMpAuthRequest(ClientConfig config, IAuthStateCache authStateCache) : base(config, new WechatMPAuthSource(), authStateCache)
{
}
/**
* 微信的特殊性,此时返回的信息同时包含 openid 和 access_token
*
* @param authCallback 回调返回的参数
* @return 所有信息
*/
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
return this.getToken(accessTokenUrl(authCallback.code));
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
string openId = authToken.openId;
string response = doGetUserInfo(authToken);
var jsonObj = response.parseObject();
this.checkResponse(jsonObj);
//string location = String.format("%s-%s-%s", object.getString("country"), object.getString("province"), object.getString("city"));
string location = $"{jsonObj.getString("country")}-{jsonObj.getString("province")}-{jsonObj.getString("city")}";
if (jsonObj.ContainsKey("unionid"))
{
authToken.unionId = jsonObj.getString("unionid");
}
var authUser = new AuthUser();
authUser.username = jsonObj.getString("nickname");
authUser.nickname = jsonObj.getString("nickname");
authUser.avatar = jsonObj.getString("headimgurl");
authUser.location = location;
authUser.uuid = openId;
authUser.gender = GlobalAuthUtil.getWechatRealGender(jsonObj.getString("sex"));
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = jsonObj;
authUser.originalUserStr = response;
return authUser;
}
public override AuthResponse refresh(AuthToken oldToken)
{
return new AuthResponse(Convert.ToInt32(AuthResponseStatus.SUCCESS), null, this.getToken(refreshTokenUrl(oldToken.refreshToken)));
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("errcode"))
{
throw new Exception($"errcode: {dic.getString("errcode")}, errmsg: {dic.getString("errmsg")}");
}
}
/**
* 获取token适用于获取access_token和刷新token
*
* @param accessTokenUrl 实际请求token的地址
* @return token对象
*/
private AuthToken getToken(string accessTokenUrl)
{
string response = HttpUtils.RequestGet(accessTokenUrl);
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken();
authToken.accessToken = accessTokenObject.getString("access_token");
authToken.refreshToken = accessTokenObject.getString("refresh_token");
authToken.expireIn = accessTokenObject.getInt32("expires_in");
authToken.openId = accessTokenObject.getString("openid");
authToken.scope = accessTokenObject.getString("scope");
return authToken;
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("appid", config.clientId)
.queryParam("redirect_uri", GlobalAuthUtil.urlEncode(config.redirectUri))
.queryParam("response_type", "code")
.queryParam("scope", config.scope.IsNullOrWhiteSpace() ? "snsapi_userinfo" : config.scope)
.queryParam("state", getRealState(state) + "#wechat_redirect")
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
protected override string accessTokenUrl(string code)
{
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("appid", config.clientId)
.queryParam("secret", config.clientSecret)
.queryParam("code", code)
.queryParam("grant_type", "authorization_code")
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
protected override string userInfoUrl(AuthToken authToken)
{
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.accessToken)
.queryParam("openid", authToken.openId)
.queryParam("lang", "zh_CN")
.build();
}
/**
* 返回获取userInfo的url
*
* @param refreshToken getAccessToken方法返回的refreshToken
* @return 返回获取userInfo的url
*/
protected override string refreshTokenUrl(String refreshToken)
{
return UrlBuilder.fromBaseUrl(source.refresh())
.queryParam("appid", config.clientId)
.queryParam("grant_type", "refresh_token")
.queryParam("refresh_token", refreshToken)
.build();
}
}

View File

@@ -0,0 +1,168 @@
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
using JNPF.Extras.CollectiveOAuth.Cache;
namespace JNPF.Extras.CollectiveOAuth.Request;
public partial class WeChatOpenAuthRequest : DefaultAuthRequest
{
public WeChatOpenAuthRequest(ClientConfig config) : base(config, new WechatOpenAuthSource())
{
}
public WeChatOpenAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new WechatOpenAuthSource(), authStateCache)
{
}
/**
* 微信的特殊性,此时返回的信息同时包含 openid 和 access_token
*
* @param authCallback 回调返回的参数
* @return 所有信息
*/
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
return this.getToken(accessTokenUrl(authCallback.code));
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
string openId = authToken.openId;
string response = doGetUserInfo(authToken);
var jsonObj = response.parseObject();
this.checkResponse(jsonObj);
//string location = String.format("%s-%s-%s", object.getString("country"), object.getString("province"), object.getString("city"));
string location = $"{jsonObj.getString("country")}-{jsonObj.getString("province")}-{jsonObj.getString("city")}";
if (jsonObj.ContainsKey("unionid"))
{
authToken.unionId = jsonObj.getString("unionid");
}
var authUser = new AuthUser();
authUser.username = jsonObj.getString("nickname");
authUser.nickname = jsonObj.getString("nickname");
authUser.avatar = jsonObj.getString("headimgurl");
authUser.location = location;
authUser.uuid = openId;
authUser.gender = GlobalAuthUtil.getWechatRealGender(jsonObj.getString("sex"));
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = jsonObj;
authUser.originalUserStr = response;
return authUser;
}
public override AuthResponse refresh(AuthToken oldToken)
{
return new AuthResponse(Convert.ToInt32(AuthResponseStatus.SUCCESS), null, this.getToken(refreshTokenUrl(oldToken.refreshToken)));
}
/**
* 检查响应内容是否正确
*
* @param object 请求响应内容
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("errcode"))
{
throw new Exception($"errcode: {dic.getString("errcode")}, errmsg: {dic.getString("errmsg")}");
}
}
/**
* 获取token适用于获取access_token和刷新token
*
* @param accessTokenUrl 实际请求token的地址
* @return token对象
*/
private AuthToken getToken(string accessTokenUrl)
{
string response = HttpUtils.RequestGet(accessTokenUrl);
var accessTokenObject = response.parseObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken();
authToken.accessToken = accessTokenObject.getString("access_token");
authToken.refreshToken = accessTokenObject.getString("refresh_token");
authToken.expireIn = accessTokenObject.getInt32("expires_in");
authToken.openId = accessTokenObject.getString("openid");
return authToken;
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("appid", config.clientId)
.queryParam("redirect_uri", GlobalAuthUtil.urlEncode(config.redirectUri))
.queryParam("response_type", "code")
.queryParam("scope", config.scope.IsNullOrWhiteSpace() ? "snsapi_login" : config.scope)
.queryParam("state", getRealState(state))
.build();
}
/**
* 返回获取accessToken的url
*
* @param code 授权码
* @return 返回获取accessToken的url
*/
protected override string accessTokenUrl(string code)
{
return UrlBuilder.fromBaseUrl(source.accessToken())
.queryParam("appid", config.clientId)
.queryParam("secret", config.clientSecret)
.queryParam("code", code)
.queryParam("grant_type", "authorization_code")
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
protected override string userInfoUrl(AuthToken authToken)
{
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.accessToken)
.queryParam("openid", authToken.openId)
.queryParam("lang", "zh_CN")
.build();
}
/**
* 返回获取userInfo的url
*
* @param refreshToken getAccessToken方法返回的refreshToken
* @return 返回获取userInfo的url
*/
protected override string refreshTokenUrl(string refreshToken)
{
return UrlBuilder.fromBaseUrl(source.refresh())
.queryParam("appid", config.clientId)
.queryParam("grant_type", "refresh_token")
.queryParam("refresh_token", refreshToken)
.build();
}
}

View File

@@ -0,0 +1,99 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class WeiboAuthRequest : DefaultAuthRequest
{
public WeiboAuthRequest(ClientConfig config) : base(config, new WeiboAuthSource())
{
}
public WeiboAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new WeiboAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
var response = doPostAuthorizationCode(authCallback.code);
var accessTokenObject = response.parseObject();
if (accessTokenObject.ContainsKey("error"))
{
throw new Exception(accessTokenObject.getString("error_description"));
}
var authToken = new AuthToken();
authToken.accessToken = accessTokenObject.getString("access_token");
authToken.uid = accessTokenObject.getString("uid");
authToken.openId = accessTokenObject.getString("uid");
authToken.expireIn = accessTokenObject.getInt32("expires_in");
authToken.code = authCallback.code;
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
var accessToken = authToken.accessToken;
var uid = authToken.uid;
var oauthParam = $"uid={uid}&access_token={accessToken}";
var reqParams = new Dictionary<string, object>();
reqParams.Add("Authorization", "OAuth2 " + oauthParam);
reqParams.Add("API-RemoteIP", "application/x-www-form-urlencoded");
string response = HttpUtils.RequestGet(userInfoUrl(authToken), reqParams);
var userObj = response.parseObject();
if (userObj.ContainsKey("error"))
{
throw new Exception(userObj.getString("error"));
}
var authUser = new AuthUser();
authUser.uuid = userObj.getString("id");
authUser.username = userObj.getString("name");
authUser.nickname = userObj.getString("screen_name");
authUser.avatar = userObj.getString("profile_image_url");
authUser.blog = userObj.getString("url").IsNullOrWhiteSpace() ? $"{"https://weibo.com/"}{userObj.getString("profile_url")}" : userObj.getString("url");
authUser.location = userObj.getString("location");
authUser.remark = userObj.getString("description");
authUser.gender = GlobalAuthUtil.getRealGender(userObj.getString("gender"));
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = userObj;
authUser.originalUserStr = response;
return authUser;
}
/**
* 返回获取userInfo的url
*
* @param authToken authToken
* @return 返回获取userInfo的url
*/
protected override string userInfoUrl(AuthToken authToken)
{
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("access_token", authToken.accessToken)
.queryParam("uid", authToken.uid)
.build();
}
public override AuthResponse revoke(AuthToken authToken)
{
var response = doGetRevoke(authToken);
var retObj = response.parseObject();
if (retObj.ContainsKey("error"))
{
return new AuthResponse(AuthResponseStatus.FAILURE.GetCode(), retObj.getString("error"));
}
// 返回 result = true 表示取消授权成功,否则失败
AuthResponseStatus status = retObj.getBool("result") ? AuthResponseStatus.SUCCESS : AuthResponseStatus.FAILURE;
return new AuthResponse(status.GetCode(), status.GetDesc());
}
}

View File

@@ -0,0 +1,141 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Enums;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class XiaoMiAuthRequest : DefaultAuthRequest
{
private static readonly string PREFIX = "&&&START&&&";
public XiaoMiAuthRequest(ClientConfig config) : base(config, new XiaoMiAuthSource())
{
}
public XiaoMiAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new XiaoMiAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
return getToken(accessTokenUrl(authCallback.code));
}
private AuthToken getToken(string accessTokenUrl)
{
string response = HttpUtils.RequestGet(accessTokenUrl);
string jsonStr = response.Replace(PREFIX, "");
var accessTokenObject = jsonStr.parseObject();
if (accessTokenObject.ContainsKey("error"))
{
throw new Exception(accessTokenObject.getString("error_description"));
}
var authToken = new AuthToken();
authToken.accessToken = accessTokenObject.getString("access_token");
authToken.refreshToken = accessTokenObject.getString("refresh_token");
authToken.tokenType = accessTokenObject.getString("token_type");
authToken.expireIn = accessTokenObject.getInt32("expires_in");
authToken.scope = accessTokenObject.getString("scope");
authToken.openId = accessTokenObject.getString("openId");
authToken.macAlgorithm = accessTokenObject.getString("mac_algorithm");
authToken.macKey = accessTokenObject.getString("mac_key");
return authToken;
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
// 获取用户信息
string userResponse = doGetUserInfo(authToken);
var userProfile = userResponse.parseObject();
if ("error".Equals(userProfile.getString("result"), StringComparison.OrdinalIgnoreCase))
{
throw new Exception(userProfile.getString("description"));
}
var userObj = userProfile.getString("data").parseObject();
var authUser = new AuthUser();
authUser.uuid = userObj.getString("id");
authUser.username = userObj.getString("miliaoNick");
authUser.nickname = userObj.getString("miliaoNick");
authUser.avatar = userObj.getString("miliaoIcon");
authUser.email = userObj.getString("mail");
authUser.gender = AuthUserGender.UNKNOWN;
authUser.token = authToken;
authUser.source = source.getName();
authUser.originalUser = userObj;
authUser.originalUserStr = userResponse;
//return authUser;
// 获取用户邮箱手机号等信息
string emailPhoneUrl = $"{{https://open.account.xiaomi.com/user/phoneAndEmail}}?clientId={config.clientId}&token={authToken.accessToken}";
string emailResponse = HttpUtils.RequestGet(emailPhoneUrl);
var userEmailPhone = emailResponse.parseObject();
if (!"error".Equals(userEmailPhone.getString("result"), StringComparison.OrdinalIgnoreCase))
{
var emailPhone = userEmailPhone.getString("data").parseObject();
authUser.email = emailPhone.getString("email");
}
else
{
//Log.warn("小米开发平台暂时不对外开放用户手机及邮箱信息的获取");
}
return authUser;
}
/**
* 刷新access token (续期)
*
* @param authToken 登录成功后返回的Token信息
* @return AuthResponse
*/
public override AuthResponse refresh(AuthToken authToken)
{
var token = getToken(refreshTokenUrl(authToken.refreshToken));
return new AuthResponse(AuthResponseStatus.SUCCESS.GetCode(), AuthResponseStatus.SUCCESS.GetDesc(), token);
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
public override String authorize(String state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.clientId)
.queryParam("redirect_uri", config.redirectUri)
.queryParam("scope", config.scope.IsNullOrWhiteSpace() ? "user/profile%20user/openIdV2%20user/phoneAndEmail" : config.scope)
.queryParam("skip_confirm", "false")
.queryParam("state", getRealState(state))
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
protected override string userInfoUrl(AuthToken authToken)
{
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("clientId", config.clientId)
.queryParam("token", authToken.accessToken)
.build();
}
}