添加项目文件。

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,20 @@
namespace JNPF.Extras.CollectiveOAuth.Utils;
/// <summary>
/// 应用程序设置帮助类.
/// </summary>
public class AppSettingUtils
{
/// <summary>
/// 根据Key取Value值.
/// </summary>
/// <param name="key"></param>
public static string GetStrValue(string key)
{
var value = string.Empty;
value = JNPF.Extras.CollectiveOAuth.Utils.ConfigurationManager.AppSettings[key];
if (!string.IsNullOrWhiteSpace(value))
return value.ToString().Trim();
return value;
}
}

View File

@@ -0,0 +1,107 @@
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Enums;
using JNPF.Extras.CollectiveOAuth.Models;
namespace JNPF.Extras.CollectiveOAuth.Utils;
public class AuthChecker
{
/**
* 是否支持第三方登录
*
* @param config config
* @param source source
* @return true or false
* @since 1.6.1-beta
*/
public static bool isSupportedAuth(ClientConfig config, IAuthSource source)
{
bool isSupported = !string.IsNullOrWhiteSpace(config.clientId) && !string.IsNullOrWhiteSpace(config.clientSecret) && !string.IsNullOrWhiteSpace(config.redirectUri);
/*if (isSupported && DefaultAuthSource.ALIPAY == source)
{
isSupported = StringUtils.isNotEmpty(config.getAlipayPublicKey());
}
if (isSupported && AuthDefaultSource.STACK_OVERFLOW == source)
{
isSupported = StringUtils.isNotEmpty(config.getStackOverflowKey());
}
if (isSupported && AuthDefaultSource.WECHAT_ENTERPRISE == source)
{
isSupported = StringUtils.isNotEmpty(config.getAgentId());
}*/
return isSupported;
}
/**
* 检查配置合法性。针对部分平台, 对redirect uri有特定要求。一般来说redirect uri都是http://而对于facebook平台 redirect uri 必须是https的链接
*
* @param config config
* @param source source
* @since 1.6.1-beta
*/
public static void checkConfig(ClientConfig config, IAuthSource source)
{
string redirectUri = config.redirectUri;
if (!GlobalAuthUtil.isHttpProtocol(redirectUri) && !GlobalAuthUtil.isHttpsProtocol(redirectUri))
{
throw new Exception(AuthResponseStatus.ILLEGAL_REDIRECT_URI.GetDesc());
}
// facebook的回调地址必须为https的链接
if ("FACEBOOK".Equals(source.getName().ToUpper()) && !GlobalAuthUtil.isHttpsProtocol(redirectUri))
{
// Facebook's redirect uri must use the HTTPS protocol
throw new Exception(AuthResponseStatus.ILLEGAL_REDIRECT_URI.GetDesc());
}
// 支付宝在创建回调地址时不允许使用localhost或者127.0.0.1
if ("ALIPAY".Equals(source.getName().ToUpper()) && GlobalAuthUtil.isLocalHost(redirectUri))
{
// The redirect uri of alipay is forbidden to use localhost or 127.0.0.1
throw new Exception(AuthResponseStatus.ILLEGAL_REDIRECT_URI.GetDesc());
}
}
/**
* 校验回调传回的code
* <p>
* {@code v1.10.0}版本中改为传入{@code source}和{@code callback}对于不同平台使用不同参数接受code的情况统一做处理
*
* @param source 当前授权平台
* @param callback 从第三方授权回调回来时传入的参数集合
* @since 1.8.0.
*/
public static void checkCode(IAuthSource source, AuthCallback callback)
{
string code = callback.code;
if (source.getName().ToUpper().Equals(DefaultAuthSourceEnum.ALIPAY_MP.ToString()))
{
code = callback.auth_code;
}
else if ("HUAWEI".Equals(source.getName().ToUpper()))
{
code = callback.authorization_code;
}
if (string.IsNullOrWhiteSpace(code))
{
throw new Exception(AuthResponseStatus.ILLEGAL_CODE.GetDesc());
}
}
/// <summary>
/// 校验回调传回的{@code state},为空或者不存在.
/// </summary>
/// <param name="state">{@code state}一定不为空.</param>
/// <param name="source">当前授权平台.</param>
/// <param name="authStateCache">{@code authStateCache} state缓存实现.</param>
public static void checkState(string state, IAuthSource source, IAuthStateCache authStateCache)
{
if (string.IsNullOrWhiteSpace(state) || !authStateCache.containsKey(state))
{
//throw new Exception(AuthResponseStatus.ILLEGAL_STATUS.GetDesc());
}
}
}

View File

@@ -0,0 +1,13 @@
namespace JNPF.Extras.CollectiveOAuth.Utils;
public class AuthStateUtils
{
/// <summary>
/// 生成随机state采用https://github.com/lets-mica/mica的UUID工具.
/// </summary>
/// <returns>随机的state字符串.</returns>
public static string createState()
{
return Guid.NewGuid().ToString();
}
}

View File

@@ -0,0 +1,225 @@
using Newtonsoft.Json.Linq;
using System.Collections.Specialized;
using System.Text;
namespace JNPF.Extras.CollectiveOAuth.Utils;
/// <summary>
/// 配置管理.
/// </summary>
public class ConfigurationManager
{
/// <summary>
/// 配置内容.
/// </summary>
private static NameValueCollection _configurationCollection = new NameValueCollection();
/// <summary>
/// 配置监听响应链堆栈.
/// </summary>
private static Stack<KeyValuePair<string, FileSystemWatcher>> FileListeners = new Stack<KeyValuePair<string, FileSystemWatcher>>();
/// <summary>
/// 默认路径.
/// </summary>
private static string _defaultPath = Directory.GetCurrentDirectory() + "\\appsettings.json";
/// <summary>
/// 最终配置文件路径.
/// </summary>
private static string _configPath = null;
/// <summary>
/// 配置节点关键字.
/// </summary>
private static string _configSection = "AppSettings";
/// <summary>
/// 配置外连接的后缀.
/// </summary>
private static string _configUrlPostfix = "Url";
/// <summary>
/// 最终修改时间戳.
/// </summary>
private static long _timeStamp = 0L;
/// <summary>
/// 配置外链关键词例如AppSettings.Url.
/// </summary>
private static string _configUrlSection { get { return _configSection + "." + _configUrlPostfix; } }
static ConfigurationManager()
{
ConfigFinder(_defaultPath);
}
/// <summary>
/// 确定配置文件路径.
/// </summary>
private static void ConfigFinder(string Path)
{
_configPath = Path;
JObject config_json = new JObject();
while (config_json != null)
{
config_json = null;
FileInfo config_info = new FileInfo(_configPath);
if (!config_info.Exists) break;
FileListeners.Push(CreateListener(config_info));
config_json = LoadJsonFile(_configPath);
if (config_json[_configUrlSection] != null)
_configPath = config_json[_configUrlSection].ToString();
else break;
}
if (config_json == null || config_json[_configSection] == null) return;
LoadConfiguration();
}
/// <summary>
/// 读取配置文件内容.
/// </summary>
private static void LoadConfiguration()
{
FileInfo config = new FileInfo(_configPath);
var configColltion = new NameValueCollection();
JObject config_object = LoadJsonFile(_configPath);
if (config_object == null || !(config_object is JObject)) return;
if (config_object[_configSection] != null)
{
foreach (JProperty prop in config_object[_configSection])
{
configColltion[prop.Name] = prop.Value.ToString();
}
}
_configurationCollection = configColltion;
}
/// <summary>
/// 解析Json文件.
/// </summary>
/// <param name="FilePath">文件路径</param>
/// <returns></returns>
private static JObject LoadJsonFile(string FilePath)
{
JObject config_object = null;
try
{
StreamReader sr = new StreamReader(FilePath, Encoding.Default);
config_object = JObject.Parse(sr.ReadToEnd());
sr.Close();
}
catch { }
return config_object;
}
/// <summary>
/// 添加监听树节点.
/// </summary>
/// <param name="info"></param>
/// <returns></returns>
private static KeyValuePair<string, FileSystemWatcher> CreateListener(FileInfo info)
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.BeginInit();
watcher.Path = info.DirectoryName;
watcher.Filter = info.Name;
watcher.IncludeSubdirectories = false;
watcher.EnableRaisingEvents = true;
watcher.NotifyFilter = NotifyFilters.Attributes | NotifyFilters.CreationTime | NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.Size;
watcher.Changed += new FileSystemEventHandler(ConfigChangeListener);
watcher.EndInit();
return new KeyValuePair<string, FileSystemWatcher>(info.FullName, watcher);
}
/// <summary>
/// 配置改变监听器.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void ConfigChangeListener(object sender, FileSystemEventArgs e)
{
long time = TimeStamp();
lock (FileListeners)
{
if (time > _timeStamp)
{
_timeStamp = time;
if (e.FullPath != _configPath || e.FullPath == _defaultPath)
{
while (FileListeners.Count > 0)
{
var listener = FileListeners.Pop();
listener.Value.Dispose();
if (listener.Key == e.FullPath) break;
}
ConfigFinder(e.FullPath);
}
else
{
LoadConfiguration();
}
}
}
}
private static long TimeStamp()
{
return (long)((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds * 100);
}
private static string c_configSection = null;
public static string ConfigSection
{
get { return _configSection; }
set { c_configSection = value; }
}
private static string c_configUrlPostfix = null;
public static string ConfigUrlPostfix
{
get { return _configUrlPostfix; }
set { c_configUrlPostfix = value; }
}
private static string c_defaultPath = null;
public static string DefaultPath
{
get { return _defaultPath; }
set { c_defaultPath = value; }
}
public static NameValueCollection AppSettings
{
get { return _configurationCollection; }
}
/// <summary>
/// 手动刷新配置,修改配置后,请手动调用此方法,以便更新配置参数
/// </summary>
public static void RefreshConfiguration()
{
lock (FileListeners)
{
// 修改配置
if (c_configSection != null) { _configSection = c_configSection; c_configSection = null; }
if (c_configUrlPostfix != null) { _configUrlPostfix = c_configUrlPostfix; c_configUrlPostfix = null; }
if (c_defaultPath != null) { _defaultPath = c_defaultPath; c_defaultPath = null; }
// 释放掉全部监听响应链
while (FileListeners.Count > 0)
FileListeners.Pop().Value.Dispose();
ConfigFinder(_defaultPath);
}
}
}

View File

@@ -0,0 +1,543 @@
using JNPF.Extras.CollectiveOAuth.Enums;
using System.Text;
using Newtonsoft.Json;
using System.Web;
namespace JNPF.Extras.CollectiveOAuth.Utils;
public static class GlobalAuthUtil
{
/// <summary>
/// 把字典集合拼接成&符号链接的字符串.
/// </summary>
/// <param name="dicParams">参数字典.</param>
/// <returns></returns>
public static string parseMapToString(Dictionary<string, object> dicParams)
{
StringBuilder builder = new StringBuilder();
if (dicParams.Count > 0)
{
builder.Append(string.Empty);
int i = 0;
foreach (KeyValuePair<string, object> item in dicParams)
{
if (i > 0)
builder.Append("&");
builder.AppendFormat("{0}={1}", item.Key, Convert.ToString(item.Value));
i++;
}
}
return builder.ToString();
}
/// <summary>
/// 是否为http协议.
/// </summary>
/// <param name="url">待验证的url.</param>
/// <returns>true: http协议, false: 非http协议.</returns>
public static bool isHttpProtocol(string url)
{
if (string.IsNullOrWhiteSpace(url))
return false;
return url.StartsWith("http://");
}
/// <summary>
/// 是否为https协议.
/// </summary>
/// <param name="url">待验证的url.</param>
/// <returns>true: https协议, false: 非https协议.</returns>
public static bool isHttpsProtocol(string url)
{
if (string.IsNullOrWhiteSpace(url))
return false;
return url.StartsWith("https://");
}
/// <summary>
/// 是否为本地主机(域名).
/// </summary>
/// <param name="url">待验证的url.</param>
/// <returns>true: 本地主机(域名), false: 非本地主机(域名).</returns>
public static bool isLocalHost(string url)
{
return string.IsNullOrWhiteSpace(url) || url.Contains("127.0.0.1") || url.Contains("localhost");
}
/// <summary>
/// 获取用户的实际性别,常规网站.
/// </summary>
/// <param name="originalGender">用户第三方标注的原始性别.</param>
/// <returns>用户性别.</returns>
public static AuthUserGender getRealGender(string originalGender)
{
if (null == originalGender || Convert.ToInt32(AuthUserGender.UNKNOWN).ToString().Equals(originalGender))
{
return AuthUserGender.UNKNOWN;
}
string[] males = { "m", "男", "1", "male" };
if (males.ToList().Contains(originalGender.ToLower()))
{
return AuthUserGender.MALE;
}
return AuthUserGender.FEMALE;
}
/// <summary>
/// 获取微信平台用户的实际性别0表示未定义1表示男性2表示女性.
/// </summary>
/// <param name="originalGender">用户第三方标注的原始性别.</param>
/// <returns>用户性别.</returns>
public static AuthUserGender getWechatRealGender(string originalGender)
{
if (string.IsNullOrWhiteSpace(originalGender) || "0".Equals(originalGender))
{
return AuthUserGender.UNKNOWN;
}
return getRealGender(originalGender);
}
/// <summary>
/// url编码.
/// </summary>
/// <param name="value">url.</param>
/// <returns></returns>
public static string urlEncode(string value)
{
if (value == null)
return string.Empty;
try
{
return System.Web.HttpUtility.UrlEncode(value);
}
catch (Exception e)
{
throw new Exception("Failed To Encode Uri", e);
}
}
/// <summary>
/// url解码.
/// </summary>
/// <param name="value">url.</param>
/// <returns></returns>
public static string urlDecode(string value)
{
if (value == null)
return string.Empty;
try
{
return HttpUtility.UrlDecode(value); // utf-8 解码
}
catch (Exception e)
{
throw new Exception("Failed To Decode Uri", e);
}
}
/// <summary>
/// 字符串转换成枚举.
/// </summary>
/// <typeparam name="T">对象.</typeparam>
/// <param name="type">类型.</param>
/// <returns>对象.</returns>
public static T enumFromString<T>(string type)
{
if (type.IsNullOrEmpty())
throw new Exception($"没有找到授权类型: {type}");
try
{
T result = (T)Enum.Parse(typeof(T), type); // utf-8 解码
return result;
}
catch (Exception e)
{
throw new Exception($"授权类型解析失败: {type}", e);
}
}
/// <summary>
/// json字符串转换为字典集合.
/// </summary>
/// <param name="jsonStr"></param>
/// <returns></returns>
public static List<Dictionary<string, object>> parseListObject(this string jsonStr)
{
var retDic = new List<Dictionary<string, object>>();
if (!string.IsNullOrWhiteSpace(jsonStr))
{
try
{
retDic = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(jsonStr);
}
catch (Exception ex)
{
}
}
return retDic;
}
/// <summary>
/// json字符串转换为字典集合.
/// </summary>
/// <param name="jsonStr"></param>
/// <returns></returns>
public static Dictionary<string, object> parseObject(this string jsonStr)
{
var retDic = new Dictionary<string, object>();
if (!string.IsNullOrWhiteSpace(jsonStr))
{
try
{
retDic = JsonConvert.DeserializeObject<Dictionary<string, object>>(jsonStr);
}
catch (Exception ex)
{
}
}
return retDic;
}
/// <summary>
/// 将URL参数解析为Map也可以解析Post中的键值对参数.
/// </summary>
/// <param name="paramsStr">参数字符串或者带参数的Path.</param>
/// <returns>参数Map.</returns>
public static Dictionary<string, object> parseUrlObject(this string paramsStr)
{
Dictionary<string, object> res = new Dictionary<string, object>();
try
{
if (paramsStr.IsNullOrWhiteSpace())
{
return res;
}
// 去掉Path部分
int pathEndPos = paramsStr.IndexOf('?');
if (pathEndPos > -1)
{
paramsStr = paramsStr.Substring(pathEndPos + 1);
}
return parseStringObject(paramsStr);
}
catch (Exception e)
{
return res;
}
}
/// <summary>
/// string字符串转mapstr格式为 {@code xxx=xxx&xxx=xxx}.
/// </summary>
/// <param name="accessTokenStr">待转换的字符串.</param>
/// <returns>字段对象.</returns>
public static Dictionary<string, object> parseStringObject(this string accessTokenStr)
{
Dictionary<string, object> res = new Dictionary<string, object>();
if (accessTokenStr.Contains("&"))
{
string[] fields = accessTokenStr.Split("&");
foreach (var field in fields)
{
if (field.Contains("="))
{
string[] keyValue = field.Split("=");
res.Add(urlDecode(keyValue[0]), keyValue.Length == 2 ? urlDecode(keyValue[1]) : null);
}
}
}
return res;
}
/// <summary>
/// 把字典集合拼接成&符号链接的字符串.
/// </summary>
/// <param name="dicParams"></param>
/// <returns></returns>
public static string spellParams(this Dictionary<string, object> dicParams)
{
StringBuilder builder = new StringBuilder();
if (dicParams.Count > 0)
{
builder.Append(string.Empty);
int i = 0;
foreach (KeyValuePair<string, object> item in dicParams)
{
if (i > 0)
builder.Append("&");
builder.AppendFormat("{0}={1}", item.Key, Convert.ToString(item.Value));
i++;
}
}
return builder.ToString();
}
/// <summary>
/// object的字典集合.
/// </summary>
/// <param name="dic"></param>
/// <param name="key"></param>
/// <returns></returns>
public static string getString(this Dictionary<string, object> dic, string key)
{
if (dic == null)
return string.Empty;
if (dic.ContainsKey(key))
{
return Convert.ToString(dic[key]);
}
else
{
return string.Empty;
}
}
/// <summary>
/// 获取参数Int32类型.
/// </summary>
/// <param name="request"></param>
/// <param name="paramName"></param>
/// <returns></returns>
public static int getInt32(this Dictionary<string, object> request, string paramName)
{
var paramValue = request.getString(paramName);
if (!string.IsNullOrWhiteSpace(paramValue))
{
try
{
return Convert.ToInt32(paramValue);
}
catch (Exception ex)
{
return -1;
}
}
return -1;
}
/// <summary>
/// 获取参数Int64类型.
/// </summary>
/// <param name="request"></param>
/// <param name="paramName"></param>
/// <returns></returns>
public static long getLong(this Dictionary<string, object> request, string paramName)
{
var paramValue = request.getString(paramName);
if (!string.IsNullOrWhiteSpace(paramValue))
{
try
{
return Convert.ToInt64(paramValue);
}
catch (Exception ex)
{
return -1;
}
}
return -1;
}
/// <summary>
/// 获取参数Bool类型.
/// </summary>
/// <param name="request"></param>
/// <param name="paramName"></param>
/// <returns></returns>
public static bool getBool(this Dictionary<string, object> request, string paramName)
{
var paramValue = request.getString(paramName);
if (!string.IsNullOrWhiteSpace(paramValue))
{
try
{
return Convert.ToBoolean(paramValue);
}
catch (Exception ex)
{
return false;
}
}
return false;
}
/// <summary>
/// 获取参数字符串并且转换为字典集合.
/// </summary>
/// <param name="request"></param>
/// <param name="paramName"></param>
/// <returns></returns>
public static Dictionary<string, object> getJSONObject(this Dictionary<string, object> request, string paramName)
{
var paramValue = request.getString(paramName);
if (!string.IsNullOrWhiteSpace(paramValue))
{
try
{
return paramValue.parseObject();
}
catch (Exception ex)
{
return new Dictionary<string, object>();
}
}
return new Dictionary<string, object>();
}
/// <summary>
/// 获取参数字符串并且转换为字典集合.
/// </summary>
/// <param name="request"></param>
/// <param name="paramName"></param>
/// <returns></returns>
public static List<Dictionary<string, object>> getJSONArray(this Dictionary<string, object> request, string paramName)
{
var paramValue = request.getString(paramName);
if (!string.IsNullOrWhiteSpace(paramValue))
{
try
{
return paramValue.parseListObject();
}
catch (Exception ex)
{
return new List<Dictionary<string, object>>();
}
}
return new List<Dictionary<string, object>>();
}
/// <summary>
/// 获取参数字符串类型.
/// </summary>
/// <param name="request"></param>
/// <param name="paramName"></param>
/// <returns></returns>
/*public static string getString(this Dictionary<string, object> request, string paramName)
{
var paramValue = request.getString(paramName);
if (!string.IsNullOrWhiteSpace(paramValue))
{
try
{
return Convert.ToString(paramValue);
}
catch (Exception ex)
{
return null;
}
}
return null;
}*/
/// <summary>
/// 获取参数日期类型.
/// </summary>
/// <param name="request"></param>
/// <param name="paramName"></param>
/// <returns></returns>
public static DateTime? GetParamDateTime(this Dictionary<string, object> request, string paramName)
{
var paramValue = request.getString(paramName);
if (!string.IsNullOrWhiteSpace(paramValue))
{
try
{
return Convert.ToDateTime(paramValue);
}
catch (Exception ex)
{
return null;
}
}
return null;
}
/// <summary>
/// 获取参数double类型.
/// </summary>
/// <param name="request"></param>
/// <param name="paramName"></param>
/// <returns></returns>
public static double? GetParamDouble(this Dictionary<string, object> request, string paramName)
{
var paramValue = request.getString(paramName);
if (!string.IsNullOrWhiteSpace(paramValue))
{
try
{
return Convert.ToDouble(paramValue);
}
catch (Exception ex)
{
return null;
}
}
return null;
}
/// <summary>
/// 获取参数Decimal类型.
/// </summary>
/// <param name="request"></param>
/// <param name="paramName"></param>
/// <returns></returns>
public static decimal? GetParamDecimal(this Dictionary<string, object> request, string paramName)
{
var paramValue = request.getString(paramName);
if (!string.IsNullOrWhiteSpace(paramValue))
{
try
{
return Convert.ToDecimal(paramValue);
}
catch (Exception ex)
{
return null;
}
}
return null;
}
/// <summary>
/// 字典排序,asc排序.
/// </summary>
/// <param name="dic">需排序的字典对象</param>
/// <param name="isAsc">true=正序,反之倒序</param>
public static Dictionary<string, object> Sort(this Dictionary<string, object> dic, bool isAsc = true)
{
Dictionary<string, object> rdic = new Dictionary<string, object>();
if (dic.Count > 0)
{
List<KeyValuePair<string, object>> lst = new List<KeyValuePair<string, object>>(dic);
lst.Sort(delegate (KeyValuePair<string, object> s1, KeyValuePair<string, object> s2)
{
if (isAsc)
{
return string.CompareOrdinal(s1.Key, s2.Key);
}
else
{
return string.CompareOrdinal(s2.Key, s1.Key);
}
});
foreach (KeyValuePair<string, object> kvp in lst)
rdic.Add(kvp.Key, kvp.Value);
}
return rdic;
}
}

View File

@@ -0,0 +1,272 @@
using System.Collections.Specialized;
using System.Net;
using System.Text;
namespace JNPF.Extras.CollectiveOAuth.Utils;
public class HttpUtils
{
/// <summary>
/// 模拟Form表单post请求.
/// </summary>
/// <param name="postUrl"></param>
/// <param name="postData"></param>
/// <param name="header"></param>
/// <param name="charset"></param>
/// <returns></returns>
public static string RequestFormPost(string postUrl, string postData = null, Dictionary<string, object> header = null, string charset = null)
{
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; // 加上这一句
if (string.IsNullOrWhiteSpace(charset))
charset = "UTF-8";
if (string.IsNullOrWhiteSpace(postData))
postData = string.Empty;
Stream outstream = null;
Stream instream = null;
StreamReader sr = null;
HttpWebResponse response = null;
HttpWebRequest request = null;
Encoding encoding = System.Text.Encoding.GetEncoding(charset);
byte[] data = encoding.GetBytes(postData);
// 准备请求...
try
{
// 设置参数
request = WebRequest.Create(postUrl) as HttpWebRequest;
CookieContainer cookieContainer = new CookieContainer();
request.CookieContainer = cookieContainer;
request.AllowAutoRedirect = true;
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded;charset=" + charset.ToLower();
request.ContentLength = data.Length;
ComeSetRequestHeader(request, header);
outstream = request.GetRequestStream();
outstream.Write(data, 0, data.Length);
outstream.Close();
// 发送请求并获取相应回应数据
response = request.GetResponse() as HttpWebResponse;
// 直到request.GetResponse()程序才开始向目标网页发送Post请求
instream = response.GetResponseStream();
sr = new StreamReader(instream, encoding);
// 返回结果网页html代码
string content = sr.ReadToEnd();
string err = string.Empty;
return content;
}
catch (Exception ex)
{
string err = ex.Message;
return string.Empty;
}
}
/// <summary>
/// 普通post请求.
/// </summary>
/// <param name="postUrl"></param>
/// <param name="postData"></param>
/// <param name="header"></param>
/// <param name="charset"></param>
/// <returns></returns>
public static string RequestPost(string postUrl, string postData = null, Dictionary<string, object> header = null, string charset = null)
{
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; // 加上这一句
if (string.IsNullOrWhiteSpace(charset))
{
charset = "UTF-8";
}
if (string.IsNullOrWhiteSpace(postData))
{
postData = string.Empty;
}
Stream outstream = null;
Stream instream = null;
StreamReader sr = null;
HttpWebResponse response = null;
HttpWebRequest request = null;
Encoding encoding = System.Text.Encoding.GetEncoding(charset);
byte[] data = encoding.GetBytes(postData);
// 准备请求...
try
{
// 设置参数
request = WebRequest.Create(postUrl) as HttpWebRequest;
CookieContainer cookieContainer = new CookieContainer();
request.CookieContainer = cookieContainer;
request.AllowAutoRedirect = true;
request.Method = "POST";
request.ContentType = "application/json;charset=" + charset.ToLower();
request.ContentLength = data.Length;
ComeSetRequestHeader(request, header);
outstream = request.GetRequestStream();
outstream.Write(data, 0, data.Length);
outstream.Close();
// 发送请求并获取相应回应数据
response = request.GetResponse() as HttpWebResponse;
// 直到request.GetResponse()程序才开始向目标网页发送Post请求
instream = response.GetResponseStream();
sr = new StreamReader(instream, encoding);
// 返回结果网页html代码
string content = sr.ReadToEnd();
string err = string.Empty;
return content;
}
catch (Exception ex)
{
string err = ex.Message;
return string.Empty;
}
}
/// <summary>
/// 有一些请求比较特殊需要标记Accept为application/json.
/// </summary>
/// <param name="url">地址.</param>
/// <returns></returns>
public static string RequestJsonGet(string url, Dictionary<string, object> header = null)
{
StringBuilder builder = new StringBuilder();
builder.Append(url);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(builder.ToString());
request.UserAgent = "Foo";
request.Accept = "application/json";
ComeSetRequestHeader(request, header);
return ComeRequestGet(request);
}
/// <summary>
/// 发送Get请求.
/// </summary>
/// <param name="url">地址.</param>
/// <param name="header">请求参数定义.</param>
public static string RequestGet(string url, Dictionary<string, object> header = null)
{
StringBuilder builder = new StringBuilder();
builder.Append(url);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(builder.ToString());
request.ContentType = "application/json;charset=utf-8;";
ComeSetRequestHeader(request, header);
return ComeRequestGet(request);
}
/// <summary>
/// 通用的设置RequestHeader方法.
/// </summary>
/// <param name="request"></param>
/// <param name="header"></param>
public static void ComeSetRequestHeader(HttpWebRequest request, Dictionary<string, object> header = null)
{
if (header != null && header.Count > 0)
{
foreach (var item in header)
{
switch (item.Key.ToUpper())
{
case "HOST":
request.Host = Convert.ToString(item.Value);
break;
case "CONTENT-TYPE":
request.ContentType = Convert.ToString(item.Value);
break;
case "CONNECTION":
SetSpecialHeaderValue(request.Headers, item.Key, Convert.ToString(item.Value));
break;
default:
request.Headers.Add(item.Key, Convert.ToString(item.Value));
break;
}
}
}
}
/// <summary>
/// 设置特殊的header.
/// </summary>
/// <param name="header"></param>
/// <param name="name"></param>
/// <param name="value"></param>
public static void SetSpecialHeaderValue(WebHeaderCollection header, string name, string value)
{
var property = typeof(WebHeaderCollection).GetProperty("InnerCollection",
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
if (property != null)
{
var collection = property.GetValue(header, null) as NameValueCollection;
collection[name] = value;
}
}
/// <summary>
/// 通用的get请求.
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public static string ComeRequestGet(HttpWebRequest request)
{
string result = "";
request.AllowAutoRedirect = true;
request.Method = "GET";
request.CookieContainer = new CookieContainer();
request.Credentials = CredentialCache.DefaultCredentials;
HttpWebResponse resp = (HttpWebResponse)request.GetResponse();
Stream stream = resp.GetResponseStream();
try
{
// 获取内容
using (StreamReader reader = new StreamReader(stream))
{
result = reader.ReadToEnd();
}
}
finally
{
stream.Close();
}
return result;
}
/// <summary>
/// 通用的Post请求.
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public static string ComeRequestPost(HttpWebRequest request)
{
string result = "";
request.AllowAutoRedirect = true;
request.Method = "POST";
request.CookieContainer = new CookieContainer();
request.Credentials = CredentialCache.DefaultCredentials;
HttpWebResponse resp = (HttpWebResponse)request.GetResponse();
Stream stream = resp.GetResponseStream();
try
{
// 获取内容
using (StreamReader reader = new StreamReader(stream))
{
result = reader.ReadToEnd();
}
}
finally
{
stream.Close();
}
return result;
}
}

View File

@@ -0,0 +1,224 @@
using System.Text;
using System.Text.RegularExpressions;
namespace JNPF.Extras.CollectiveOAuth.Utils;
/// <summary>
/// 字符串<see cref="string"/>类型的扩展辅助操作类.
/// </summary>
public static class StringExtensions
{
#region
/// <summary>
/// 指示所指定的正则表达式在指定的输入字符串中是否找到了匹配项.
/// </summary>
/// <param name="value">要搜索匹配项的字符串.</param>
/// <param name="pattern">要匹配的正则表达式模式.</param>
/// <returns>如果正则表达式找到匹配项,则为 true否则为 false.</returns>
public static bool IsMatch(this string value, string pattern)
{
if (value == null)
{
return false;
}
return Regex.IsMatch(value, pattern);
}
/// <summary>
/// 在指定的输入字符串中搜索指定的正则表达式的第一个匹配项.
/// </summary>
/// <param name="value">要搜索匹配项的字符串.</param>
/// <param name="pattern">要匹配的正则表达式模式.</param>
/// <returns>一个对象,包含有关匹配项的信息.</returns>
public static string Match(this string value, string pattern)
{
if (value == null)
return null;
return Regex.Match(value, pattern).Value;
}
/// <summary>
/// 在指定的输入字符串中搜索指定的正则表达式的所有匹配项的字符串集合.
/// </summary>
/// <param name="value"> 要搜索匹配项的字符串.</param>
/// <param name="pattern"> 要匹配的正则表达式模式.</param>
/// <returns> 一个集合,包含有关匹配项的字符串值.</returns>
public static IEnumerable<string> Matches(this string value, string pattern)
{
if (value == null)
return new string[] { };
MatchCollection matches = Regex.Matches(value, pattern);
return from Match match in matches select match.Value;
}
/// <summary>
/// 是否电子邮件.
/// </summary>
public static bool IsEmail(this string value)
{
const string pattern = @"^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$";
return value.IsMatch(pattern);
}
/// <summary>
/// 是否是IP地址.
/// </summary>
public static bool IsIpAddress(this string value)
{
const string pattern = @"^(\d(25[0-5]|2[0-4][0-9]|1?[0-9]?[0-9])\d\.){3}\d(25[0-5]|2[0-4][0-9]|1?[0-9]?[0-9])\d$";
return value.IsMatch(pattern);
}
/// <summary>
/// 是否是整数.
/// </summary>
public static bool IsNumeric(this string value)
{
const string pattern = @"^\-?[0-9]+$";
return value.IsMatch(pattern);
}
/// <summary>
/// 是否是Unicode字符串.
/// </summary>
public static bool IsUnicode(this string value)
{
const string pattern = @"^[\u4E00-\u9FA5\uE815-\uFA29]+$";
return value.IsMatch(pattern);
}
/// <summary>
/// 是否Url字符串.
/// </summary>
public static bool IsUrl(this string value)
{
const string pattern = @"^(http|https|ftp|rtsp|mms):(\/\/|\\\\)[A-Za-z0-9%\-_@]+\.[A-Za-z0-9%\-_@]+[A-Za-z0-9\.\/=\?%\-&_~`@:\+!;]*$";
return value.IsMatch(pattern);
}
/// <summary>
/// 是否身份证号验证如下3种情况
/// 1.身份证号码为15位数字
/// 2.身份证号码为18位数字
/// 3.身份证号码为17位数字+1个字母.
/// </summary>
public static bool IsIdentityCard(this string value)
{
const string pattern = @"^(^\d{15}$|^\d{18}$|^\d{17}(\d|X|x))$";
return value.IsMatch(pattern);
}
/// <summary>
/// 是否手机号码.
/// </summary>
/// <param name="value"></param>
/// <param name="isRestrict">是否按严格格式验证.</param>
public static bool IsMobileNumber(this string value, bool isRestrict = false)
{
string pattern = isRestrict ? @"^[1][3-8]\d{9}$" : @"^[1]\d{10}$";
return value.IsMatch(pattern);
}
#endregion
#region
/// <summary>
/// 指示指定的字符串是 null 还是 System.String.Empty 字符串.
/// </summary>
public static bool IsNullOrEmpty(this string value)
{
return string.IsNullOrEmpty(value);
}
/// <summary>
/// 指示指定的字符串是 null、空还是仅由空白字符组成.
/// </summary>
public static bool IsNullOrWhiteSpace(this string value)
{
return string.IsNullOrWhiteSpace(value);
}
/// <summary>
/// 判断指定路径是否图片文件.
/// </summary>
public static bool IsImageFile(this string filename)
{
if (!File.Exists(filename))
{
return false;
}
byte[] filedata = File.ReadAllBytes(filename);
if (filedata.Length == 0)
return false;
switch (BitConverter.ToUInt16(filedata, 0))
{
case 0x4D42: // bmp
case 0xD8FF: // jpg
case 0x4947: // gif
case 0x5089: // png
return true;
default:
return false;
}
}
/// <summary>
/// 以指定字符串作为分隔符将指定字符串分隔成数组.
/// </summary>
/// <param name="value">要分割的字符串.</param>
/// <param name="strSplit">字符串类型的分隔符.</param>
/// <param name="removeEmptyEntries">是否移除数据中元素为空字符串的项.</param>
/// <returns>分割后的数据</returns>
public static string[] Split(this string value, string strSplit, bool removeEmptyEntries = false)
{
return value.Split(new[] { strSplit }, removeEmptyEntries ? StringSplitOptions.RemoveEmptyEntries : StringSplitOptions.None);
}
/// <summary>
/// 支持汉字的字符串长度汉字长度计为2.
/// </summary>
/// <param name="value">参数字符串.</param>
/// <returns>当前字符串的长度汉字长度为2.</returns>
public static int TextLength(this string value)
{
ASCIIEncoding ascii = new ASCIIEncoding();
int tempLen = 0;
byte[] bytes = ascii.GetBytes(value);
foreach (byte b in bytes)
{
if (b == 63)
tempLen += 2;
else
tempLen += 1;
}
return tempLen;
}
/// <summary>
/// 将字符串转换为<see cref="byte"/>[]数组,默认编码为<see cref="Encoding.UTF8"/>.
/// </summary>
public static byte[] ToBytes(this string value, Encoding encoding = null)
{
if (encoding == null)
encoding = Encoding.UTF8;
return encoding.GetBytes(value);
}
/// <summary>
/// 将<see cref="byte"/>[]数组转换为字符串,默认编码为<see cref="Encoding.UTF8"/>.
/// </summary>
public static string ToString(this byte[] bytes, Encoding encoding)
{
if (encoding == null)
encoding = Encoding.UTF8;
return encoding.GetString(bytes);
}
#endregion
}

View File

@@ -0,0 +1,372 @@
using System.Security.Cryptography;
using System.Text;
using System.Web;
namespace JNPF.Extras.CollectiveOAuth.Utils;
/// <summary>
/// <20><><EFBFBD>ػ<EFBFBD><D8BB><EFBFBD>.
/// </summary>
public class TwitterBase
{
/// <summary>
/// Provides a predefined set of algorithms that are supported officially by the protocol.
/// </summary>
public enum SignatureTypes
{
HMACSHA1,
PLAINTEXT,
RSASHA1
}
/// <summary>
/// Provides an internal structure to sort the query parameter.
/// </summary>
protected class QueryParameter
{
private string name = null;
private string value = null;
public QueryParameter(string name, string value)
{
this.name = name;
this.value = value;
}
public string Name
{
get { return name; }
}
public string Value
{
get { return value; }
}
}
/// <summary>
/// Comparer class used to perform the sorting of the query parameters.
/// </summary>
protected class QueryParameterComparer : IComparer<QueryParameter>
{
#region IComparer<QueryParameter> Members
public int Compare(QueryParameter x, QueryParameter y)
{
if (x.Name == y.Name)
{
return string.Compare(x.Value, y.Value);
}
else
{
return string.Compare(x.Name, y.Name);
}
}
#endregion
}
protected const string OAuthVersion = "1.0";
protected const string OAuthParameterPrefix = "oauth_";
// List of know and used oauth parameters' names.
protected const string OAuthConsumerKeyKey = "oauth_consumer_key";
protected const string OAuthCallbackKey = "oauth_callback";
protected const string OAuthVersionKey = "oauth_version";
protected const string OAuthSignatureMethodKey = "oauth_signature_method";
protected const string OAuthSignatureKey = "oauth_signature";
protected const string OAuthTimestampKey = "oauth_timestamp";
protected const string OAuthNonceKey = "oauth_nonce";
protected const string OAuthTokenKey = "oauth_token";
protected const string OAuthTokenSecretKey = "oauth_token_secret";
protected const string OAuthVerifierKey = "oauth_verifier";
protected const string HMACSHA1SignatureType = "HMAC-SHA1";
protected const string PlainTextSignatureType = "PLAINTEXT";
protected const string RSASHA1SignatureType = "RSA-SHA1";
protected Random random = new Random();
protected string unreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
/// <summary>
/// Helper function to compute a hash value.
/// </summary>
/// <param name="hashAlgorithm">The hashing algoirhtm used. If that algorithm needs some initialization, like HMAC and its derivatives, they should be initialized prior to passing it to this function.</param>
/// <param name="data">The data to hash.</param>
/// <returns>a Base64 string of the hash value.</returns>
private string ComputeHash(HashAlgorithm hashAlgorithm, string data)
{
if (hashAlgorithm == null)
{
throw new ArgumentNullException("hashAlgorithm");
}
if (string.IsNullOrEmpty(data))
{
throw new ArgumentNullException("data");
}
byte[] dataBuffer = System.Text.Encoding.ASCII.GetBytes(data);
byte[] hashBytes = hashAlgorithm.ComputeHash(dataBuffer);
return Convert.ToBase64String(hashBytes);
}
/// <summary>
/// Internal function to cut out all non oauth query string parameters (all parameters not begining with "oauth_").
/// </summary>
/// <param name="parameters">The query string part of the Url.</param>
/// <returns>A list of QueryParameter each containing the parameter name and value.</returns>
private List<QueryParameter> GetQueryParameters(string parameters)
{
if (parameters.StartsWith("?"))
{
parameters = parameters.Remove(0, 1);
}
List<QueryParameter> result = new List<QueryParameter>();
if (!string.IsNullOrEmpty(parameters))
{
string[] p = parameters.Split('&');
foreach (string s in p)
{
if (!string.IsNullOrEmpty(s) && !s.StartsWith(OAuthParameterPrefix))
{
if (s.IndexOf('=') > -1)
{
string[] temp = s.Split('=');
result.Add(new QueryParameter(temp[0], temp[1]));
}
else
{
result.Add(new QueryParameter(s, string.Empty));
}
}
}
}
return result;
}
/// <summary>
/// This is a different Url Encode implementation since the default .NET one outputs the percent encoding in lower case.
/// While this is not a problem with the percent encoding spec, it is used in upper case throughout OAuth.
/// </summary>
/// <param name="value">The value to Url encode.</param>
/// <returns>Returns a Url encoded string.</returns>
public string UrlEncode(string value)
{
StringBuilder result = new StringBuilder();
foreach (char symbol in value)
{
if (unreservedChars.IndexOf(symbol) != -1)
{
result.Append(symbol);
}
else
{
result.Append('%' + String.Format("{0:X2}", (int)symbol));
}
}
return result.ToString();
}
/// <summary>
/// Normalizes the request parameters according to the spec.
/// </summary>
/// <param name="parameters">The list of parameters already sorted.</param>
/// <returns>a string representing the normalized parameters.</returns>
protected string NormalizeRequestParameters(IList<QueryParameter> parameters)
{
StringBuilder sb = new StringBuilder();
QueryParameter p = null;
for (int i = 0; i < parameters.Count; i++)
{
p = parameters[i];
sb.AppendFormat("{0}={1}", p.Name, p.Value);
if (i < parameters.Count - 1)
{
sb.Append("&");
}
}
return sb.ToString();
}
/// <summary>
/// Generate the signature base that is used to produce the signature.
/// </summary>
/// <param name="url">The full url that needs to be signed including its non OAuth url parameters.</param>
/// <param name="consumerKey">The consumer key.</param>
/// <param name="token">The token, if available. If not available pass null or an empty string.</param>
/// <param name="tokenSecret">The token secret, if available. If not available pass null or an empty string.</param>
/// <param name="callBackUrl">The callback URL (for OAuth 1.0a).If your client cannot accept callbacks, the value MUST be 'oob'.</param>
/// <param name="oauthVerifier">This value MUST be included when exchanging Request Tokens for Access Tokens. Otherwise pass a null or an empty string.</param>
/// <param name="httpMethod">The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc).</param>
/// <param name="signatureType">The signature type. To use the default values use <see cref="OAuthBase.SignatureTypes">OAuthBase.SignatureTypes</see>.</param>
/// <returns>The signature base</returns>
public string GenerateSignatureBase(Uri url, string consumerKey, string token, string tokenSecret, string callBackUrl, string oauthVerifier, string httpMethod, string timeStamp, string nonce, string signatureType, out string normalizedUrl, out string normalizedRequestParameters)
{
if (token == null)
{
token = string.Empty;
}
if (tokenSecret == null)
{
tokenSecret = string.Empty;
}
if (string.IsNullOrEmpty(consumerKey))
{
throw new ArgumentNullException("consumerKey");
}
if (string.IsNullOrEmpty(httpMethod))
{
throw new ArgumentNullException("httpMethod");
}
if (string.IsNullOrEmpty(signatureType))
{
throw new ArgumentNullException("signatureType");
}
normalizedUrl = null;
normalizedRequestParameters = null;
List<QueryParameter> parameters = GetQueryParameters(url.Query);
parameters.Add(new QueryParameter(OAuthVersionKey, OAuthVersion));
parameters.Add(new QueryParameter(OAuthNonceKey, nonce));
parameters.Add(new QueryParameter(OAuthTimestampKey, timeStamp));
parameters.Add(new QueryParameter(OAuthSignatureMethodKey, signatureType));
parameters.Add(new QueryParameter(OAuthConsumerKeyKey, consumerKey));
if (!string.IsNullOrEmpty(callBackUrl))
{
parameters.Add(new QueryParameter(OAuthCallbackKey, UrlEncode(callBackUrl)));
}
if (!string.IsNullOrEmpty(oauthVerifier))
{
parameters.Add(new QueryParameter(OAuthVerifierKey, oauthVerifier));
}
if (!string.IsNullOrEmpty(token))
{
parameters.Add(new QueryParameter(OAuthTokenKey, token));
}
parameters.Sort(new QueryParameterComparer());
normalizedUrl = string.Format("{0}://{1}", url.Scheme, url.Host);
if (!((url.Scheme == "http" && url.Port == 80) || (url.Scheme == "https" && url.Port == 443)))
{
normalizedUrl += ":" + url.Port;
}
normalizedUrl += url.AbsolutePath;
normalizedRequestParameters = NormalizeRequestParameters(parameters);
StringBuilder signatureBase = new StringBuilder();
signatureBase.AppendFormat("{0}&", httpMethod.ToUpper());
signatureBase.AppendFormat("{0}&", UrlEncode(normalizedUrl));
signatureBase.AppendFormat("{0}", UrlEncode(normalizedRequestParameters));
return signatureBase.ToString();
}
/// <summary>
/// Generate the signature value based on the given signature base and hash algorithm.
/// </summary>
/// <param name="signatureBase">The signature based as produced by the GenerateSignatureBase method or by any other means.</param>
/// <param name="hash">The hash algorithm used to perform the hashing. If the hashing algorithm requires initialization or a key it should be set prior to calling this method.</param>
/// <returns>A base64 string of the hash value.</returns>
public string GenerateSignatureUsingHash(string signatureBase, HashAlgorithm hash)
{
return ComputeHash(hash, signatureBase);
}
/// <summary>
/// Generates a signature using the HMAC-SHA1 algorithm.
/// </summary>
/// <param name="url">The full url that needs to be signed including its non OAuth url parameters.</param>
/// <param name="consumerKey">The consumer key.</param>
/// <param name="consumerSecret">The consumer seceret.</param>
/// <param name="token">The token, if available. If not available pass null or an empty string.</param>
/// <param name="tokenSecret">The token secret, if available. If not available pass null or an empty string.</param>
/// <param name="callBackUrl">The callback URL (for OAuth 1.0a).If your client cannot accept callbacks, the value MUST be 'oob'.</param>
/// <param name="oauthVerifier">This value MUST be included when exchanging Request Tokens for Access Tokens. Otherwise pass a null or an empty string.</param>
/// <param name="httpMethod">The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc).</param>
/// <returns>A base64 string of the hash value.</returns>
public string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token, string tokenSecret, string callBackUrl, string oauthVerifier, string httpMethod, string timeStamp, string nonce, out string normalizedUrl, out string normalizedRequestParameters)
{
return GenerateSignature(url, consumerKey, consumerSecret, token, tokenSecret, callBackUrl, oauthVerifier, httpMethod, timeStamp, nonce, SignatureTypes.HMACSHA1, out normalizedUrl, out normalizedRequestParameters);
}
/// <summary>
/// Generates a signature using the specified signatureType.
/// </summary>
/// <param name="url">The full url that needs to be signed including its non OAuth url parameters.</param>
/// <param name="consumerKey">The consumer key.</param>
/// <param name="consumerSecret">The consumer seceret.</param>
/// <param name="token">The token, if available. If not available pass null or an empty string.</param>
/// <param name="tokenSecret">The token secret, if available. If not available pass null or an empty string.</param>
/// <param name="callBackUrl">The callback URL (for OAuth 1.0a).If your client cannot accept callbacks, the value MUST be 'oob'.</param>
/// <param name="oauthVerifier">This value MUST be included when exchanging Request Tokens for Access Tokens. Otherwise pass a null or an empty string.</param>
/// <param name="httpMethod">The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc).</param>
/// <param name="timeStamp">ǩ<><C7A9>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD>.</param>
/// <param name="nonce"><3E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.</param>
/// <param name="signatureType">The type of signature to use.</param>
/// <param name="normalizedUrl"><3E><EFBFBD><E6B7B6>URL</param>
/// <param name="normalizedRequestParameters"><3E><EFBFBD><E6B7B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.</param>
/// <returns>A base64 string of the hash value.</returns>
public string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token, string tokenSecret, string callBackUrl, string oauthVerifier, string httpMethod, string timeStamp, string nonce, SignatureTypes signatureType, out string normalizedUrl, out string normalizedRequestParameters)
{
normalizedUrl = null;
normalizedRequestParameters = null;
switch (signatureType)
{
case SignatureTypes.PLAINTEXT:
return HttpUtility.UrlEncode(string.Format("{0}&{1}", consumerSecret, tokenSecret));
case SignatureTypes.HMACSHA1:
string signatureBase = GenerateSignatureBase(url, consumerKey, token, tokenSecret, callBackUrl, oauthVerifier, httpMethod, timeStamp, nonce, HMACSHA1SignatureType, out normalizedUrl, out normalizedRequestParameters);
HMACSHA1 hmacsha1 = new HMACSHA1();
hmacsha1.Key = Encoding.ASCII.GetBytes(string.Format("{0}&{1}", UrlEncode(consumerSecret), string.IsNullOrEmpty(tokenSecret) ? "" : UrlEncode(tokenSecret)));
return GenerateSignatureUsingHash(signatureBase, hmacsha1);
case SignatureTypes.RSASHA1:
throw new NotImplementedException();
default:
throw new ArgumentException("Unknown signature type", "signatureType");
}
}
/// <summary>
/// <20><><EFBFBD><EFBFBD>ǩ<EFBFBD><C7A9>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD>.
/// </summary>
/// <returns></returns>
public virtual string GenerateTimeStamp()
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds).ToString();
}
/// <summary>
/// <20><><EFBFBD>ɸ<EFBFBD><C9B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
/// </summary>
/// <returns></returns>
public virtual string GenerateNonce()
{
// Just a simple implementation of a random number between 123400 and 9999999
return random.Next(123400, 9999999).ToString();
}
}

View File

@@ -0,0 +1,97 @@
namespace JNPF.Extras.CollectiveOAuth.Utils;
/// <summary>
/// 网址构造器.
/// </summary>
public class UrlBuilder
{
private Dictionary<string, object> paramDic = new Dictionary<string, object>();
private string baseUrl;
/// <summary>
/// 初始化一个<see cref="UrlBuilder"/>类型的新实例.
/// </summary>
private UrlBuilder()
{
}
/// <summary>
/// 从基本路径.
/// </summary>
/// <param name="baseUrl">基础路径.</param>
/// <returns>new {@code UrlBuilder}</returns>
public static UrlBuilder fromBaseUrl(string baseUrl)
{
UrlBuilder builder = new UrlBuilder();
builder.baseUrl = baseUrl;
return builder;
}
/// <summary>
/// 添加参数.
/// </summary>
/// <param name="key">参数名称.</param>
/// <param name="value">参数值.</param>
/// <returns>this UrlBuilder.</returns>
public UrlBuilder queryParam(string key, object value)
{
if (string.IsNullOrWhiteSpace(key))
{
throw new Exception("参数名不能为空");
}
string valueAsString = (value != null ? Convert.ToString(value) : null);
this.paramDic.Add(key, valueAsString);
return this;
}
/// <summary>
/// 构造url.
/// </summary>
/// <returns>url.</returns>
public string build()
{
return this.build(false);
}
/// <summary>
/// 构造url.
/// </summary>
/// <param name="encode">转码.</param>
/// <returns>url.</returns>
public string build(bool encode)
{
if (this.paramDic.Count == 0 || this.paramDic == null)
{
return this.baseUrl;
}
string baseUrl = this.appendIfNotContain(this.baseUrl, "?", "&");
string paramString = GlobalAuthUtil.parseMapToString(this.paramDic);
return baseUrl + paramString;
}
/// <summary>
/// 如果给定字符串{@code str}中不包含{@code appendStr},则在{@code str}后追加{@code appendStr}:
/// 如果已包含{@code appendStr},则在{@code str}后追加{@code otherwise}.
/// </summary>
/// <param name="str">给定的字符串.</param>
/// <param name="appendStr">需要追加的内容.</param>
/// <param name="otherwise">当{@code appendStr}不满足时追加到{@code str}后的内容.</param>
/// <returns>追加后的字符串.</returns>
public string appendIfNotContain(string str, string appendStr, string otherwise)
{
if (string.IsNullOrWhiteSpace(str) || string.IsNullOrWhiteSpace(appendStr))
{
return str;
}
if (str.Contains(appendStr))
{
return str + otherwise;
}
return str + appendStr;
}
}