|
@@ -0,0 +1,299 @@
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.IO;
|
|
|
+using System.Net;
|
|
|
+using System.Security.Cryptography;
|
|
|
+using System.Text;
|
|
|
+using TEAMModelOS.SDK.Extension;
|
|
|
+
|
|
|
+namespace TEAMModelOS.Controllers.Third.Xkw
|
|
|
+{
|
|
|
+ public abstract class OpenAuthClient
|
|
|
+ {
|
|
|
+ public string ClientName { get; protected set; }
|
|
|
+ public string AppKey { get; protected set; }
|
|
|
+ public string AppSecret { get; protected set; }
|
|
|
+ public string RedirectUrl { get; set; }
|
|
|
+ public string AccessToken { get; set; }
|
|
|
+
|
|
|
+ public bool IsAuthorized
|
|
|
+ {
|
|
|
+ get { return isAccessTokenSet && !string.IsNullOrEmpty(AccessToken); }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected bool isAccessTokenSet = false;
|
|
|
+ public abstract string GetAuthorizationUrl();
|
|
|
+ public abstract void GetAccessTokenByCode(string code, string schoolId);
|
|
|
+
|
|
|
+
|
|
|
+ public OpenAuthClient(string clientId, string appSecret, string redirectUrl, string accessToken = null)
|
|
|
+ {
|
|
|
+ this.AppKey = clientId;
|
|
|
+ this.AppSecret = appSecret;
|
|
|
+ this.RedirectUrl = redirectUrl;
|
|
|
+ this.AccessToken = accessToken;
|
|
|
+
|
|
|
+ if (!string.IsNullOrEmpty(accessToken))
|
|
|
+ {
|
|
|
+ isAccessTokenSet = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public class XkwOAuthClient : OpenAuthClient
|
|
|
+ {
|
|
|
+ public string AUTH_HOST { get; set; }
|
|
|
+ public string AUTH_URL { get; set; }
|
|
|
+ public string TOKEN_URL { get; set; }
|
|
|
+ public string PROFILE_URL { get; set; }
|
|
|
+
|
|
|
+ public string SERVICE_URL { get; set; }
|
|
|
+ public string OpenId { get; set; }
|
|
|
+ public string UserId { get; set; }
|
|
|
+ /// <summary>
|
|
|
+ /// 静默注册参数
|
|
|
+ /// </summary>
|
|
|
+ public string Extra { get; set; }
|
|
|
+ public string ErrorMessage { get; set; }
|
|
|
+ public XkwOAuthClient(string appKey, string appSecret, string redirectUrl, string oauthHost, string accessToken = null, string openid = null, string userId = null)
|
|
|
+ : base(appKey, appSecret, redirectUrl, accessToken)
|
|
|
+ {
|
|
|
+ ClientName = "Xkw Demo Client";
|
|
|
+ OpenId = openid;
|
|
|
+ UserId = userId;
|
|
|
+ AUTH_HOST = oauthHost;
|
|
|
+ AUTH_URL = AUTH_HOST + "authorize";
|
|
|
+ TOKEN_URL = AUTH_HOST + "accessToken";
|
|
|
+ PROFILE_URL = AUTH_HOST + "profile";
|
|
|
+
|
|
|
+ if (!(string.IsNullOrEmpty(accessToken) && string.IsNullOrEmpty(openid)))
|
|
|
+ {
|
|
|
+ isAccessTokenSet = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 进行认证
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ public override string GetAuthorizationUrl()
|
|
|
+ {
|
|
|
+ string openSecret = "";
|
|
|
+ if (!string.IsNullOrEmpty(OpenId))
|
|
|
+ {
|
|
|
+ openSecret = CryptoUtils.EncryptAES(OpenId, AppSecret);
|
|
|
+ }
|
|
|
+ string timespan = CryptoUtils.EncryptAES(GetTimeStamp(), AppSecret);
|
|
|
+ string url = string.Format(AUTH_URL + "?client_id={0}&open_id={1}&service={2}&redirect_uri={3}×pan={4}",
|
|
|
+ AppKey, openSecret, SERVICE_URL, RedirectUrl, timespan);
|
|
|
+ if (!string.IsNullOrEmpty(Extra))
|
|
|
+ {
|
|
|
+ url = string.Format("{0}&extra={1}", url, Extra);
|
|
|
+ }
|
|
|
+ string retUrl = url + "&signature=" + SignatureHelper.GenerateSignature(url, AppSecret);
|
|
|
+ return retUrl.Replace("+", "%2B");
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 根据code获取accessToken和用户信息
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="code">code</param>
|
|
|
+ /// <param name="schoolId">学校ID</param>
|
|
|
+ public override void GetAccessTokenByCode(string code, string schoolId = null)
|
|
|
+ {
|
|
|
+ var client = new WebClient();
|
|
|
+ client.Encoding = System.Text.Encoding.UTF8;
|
|
|
+ try
|
|
|
+ {
|
|
|
+ //验证服务器证书回调自动验证
|
|
|
+ ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
|
|
|
+ string url = string.Format(TOKEN_URL + "?client_id={0}&code={1}&redirect_uri={2}", AppKey, code, RedirectUrl);
|
|
|
+ string retUrl = url + "&signature=" + SignatureHelper.GenerateSignature(url, AppSecret);
|
|
|
+ string data = client.DownloadString(retUrl);
|
|
|
+ IDictionary<string, string> access_token_dic = data.ToObject<IDictionary<string,string>>();
|
|
|
+ if (access_token_dic.ContainsKey("access_token"))
|
|
|
+ AccessToken = access_token_dic["access_token"];
|
|
|
+ //获取用户openid
|
|
|
+ string userProfileUrl = string.Format(PROFILE_URL + "?access_token={0}&schoolId={1}", AccessToken, schoolId);
|
|
|
+ string ret = client.DownloadString(userProfileUrl);
|
|
|
+ IDictionary<string, string> openId_dic = ret.ToObject<IDictionary<string, string>>();
|
|
|
+ if (openId_dic.ContainsKey("open_id"))
|
|
|
+ {
|
|
|
+ OpenId = openId_dic["open_id"];
|
|
|
+ }
|
|
|
+ if (!string.IsNullOrEmpty(OpenId))
|
|
|
+ {
|
|
|
+ isAccessTokenSet = true;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (openId_dic.ContainsKey("error"))
|
|
|
+ {
|
|
|
+ ErrorMessage = openId_dic["error"];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ ErrorMessage = "服务器异常:" + ex.Message;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取当前时间的时间戳
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ private string GetTimeStamp()
|
|
|
+ {
|
|
|
+ TimeSpan ts = new TimeSpan(DateTime.UtcNow.Ticks - new DateTime(1970, 1, 1, 0, 0, 0).Ticks);
|
|
|
+ return ((long)ts.TotalMilliseconds).ToString();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ class SignatureHelper
|
|
|
+ {
|
|
|
+ /// <summary>
|
|
|
+ /// 获取参数签名
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="url">需要生成签名的url</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public static string GenerateSignature(String url, String secret)
|
|
|
+ {
|
|
|
+ int index = url.IndexOf("?");
|
|
|
+ if (index > -1)
|
|
|
+ {
|
|
|
+ //将参数按参数名进行排序,然后对参数值进行加密
|
|
|
+ string paramStr = url.Substring(index + 1);
|
|
|
+ string[] paramKeyValues = paramStr.Split('&');
|
|
|
+ Array.Sort(paramKeyValues);
|
|
|
+ StringBuilder paramValueStr = new StringBuilder();
|
|
|
+ foreach (string param in paramKeyValues)
|
|
|
+ {
|
|
|
+ string[] paramKeyValue = param.Split(new char[] { '=' }, 2);
|
|
|
+ paramValueStr.Append(paramKeyValue[1]);
|
|
|
+ }
|
|
|
+ paramValueStr.Append(secret);
|
|
|
+ return CryptoUtils.EncryptMD5(paramValueStr.ToString());
|
|
|
+ }
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取参数签名
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="paramDic">参数键值对</param>
|
|
|
+ /// <param name="secret">加密秘钥</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public static string GenerateSignature(SortedDictionary<string, string> paramDic, String secret)
|
|
|
+ {
|
|
|
+ if (paramDic.Count > 0)
|
|
|
+ {
|
|
|
+ string paramStr = string.Concat(string.Join("", paramDic.Values), secret);
|
|
|
+ return CryptoUtils.EncryptMD5(paramStr);
|
|
|
+ }
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取参数签名
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="paramList">参数键值对</param>
|
|
|
+ /// <param name="secret">加密秘钥</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public static string GenerateSignature(SortedList<string, string> paramList, String secret)
|
|
|
+ {
|
|
|
+ if (paramList.Count > 0)
|
|
|
+ {
|
|
|
+ string paramStr = string.Concat(string.Join("", paramList.Values), secret);
|
|
|
+ return CryptoUtils.EncryptMD5(paramStr);
|
|
|
+ }
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ public class CryptoUtils
|
|
|
+ {
|
|
|
+ /// <summary>
|
|
|
+ /// MD5加密
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="encryptString">待加密的字符串</param>
|
|
|
+ /// <returns>加密过的字符串</returns>
|
|
|
+ public static string EncryptMD5(string encryptString)
|
|
|
+ {
|
|
|
+ byte[] result = Encoding.UTF8.GetBytes(encryptString);
|
|
|
+ MD5 md5 = new MD5CryptoServiceProvider();
|
|
|
+ byte[] output = md5.ComputeHash(result);
|
|
|
+ string encryptResult = BitConverter.ToString(output).Replace("-", "");
|
|
|
+ return encryptResult;
|
|
|
+ }
|
|
|
+
|
|
|
+ #region AES
|
|
|
+ /// <summary>
|
|
|
+ /// AES加密
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="str">待加密字符串</param>
|
|
|
+ /// <returns>加密后字符串</returns>
|
|
|
+ public static string EncryptAES(string str, string key)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ //分组加密算法
|
|
|
+ AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
|
|
|
+ byte[] inputByteArray = Encoding.UTF8.GetBytes(str);//得到需要加密的字节数组
|
|
|
+ //设置密钥及密钥向量
|
|
|
+ aes.Key = Encoding.UTF8.GetBytes(key);
|
|
|
+ //aes.IV = Encoding.UTF8.GetBytes(key);
|
|
|
+ aes.Mode = CipherMode.ECB;
|
|
|
+ aes.Padding = PaddingMode.PKCS7;
|
|
|
+ byte[] cipherBytes = null;
|
|
|
+ using (MemoryStream ms = new MemoryStream())
|
|
|
+ {
|
|
|
+ using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
|
|
|
+ {
|
|
|
+ cs.Write(inputByteArray, 0, inputByteArray.Length);
|
|
|
+ cs.FlushFinalBlock();
|
|
|
+ cipherBytes = ms.ToArray();//得到加密后的字节数组
|
|
|
+ cs.Close();
|
|
|
+ ms.Close();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Convert.ToBase64String(cipherBytes);
|
|
|
+ }
|
|
|
+ catch { }
|
|
|
+ return str;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// AES解密
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="str">待解密字符串</param>
|
|
|
+ /// <returns>解密后字符串</returns>
|
|
|
+ public static string DecryptAES(string str, string key)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ byte[] cipherText = Convert.FromBase64String(str);
|
|
|
+ AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
|
|
|
+ aes.Key = Encoding.UTF8.GetBytes(key);
|
|
|
+ //aes.IV = Encoding.UTF8.GetBytes(key);
|
|
|
+ aes.Mode = CipherMode.ECB;
|
|
|
+ aes.Padding = PaddingMode.PKCS7;
|
|
|
+ byte[] decryptBytes = new byte[cipherText.Length];
|
|
|
+ using (MemoryStream ms = new MemoryStream(cipherText))
|
|
|
+ {
|
|
|
+ using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Read))
|
|
|
+ {
|
|
|
+ cs.Read(decryptBytes, 0, decryptBytes.Length);
|
|
|
+ cs.Close();
|
|
|
+ ms.Close();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Encoding.UTF8.GetString(decryptBytes).Replace("\0", ""); //将字符串后尾的'\0'去掉
|
|
|
+ }
|
|
|
+ catch { }
|
|
|
+ return str;
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ }
|
|
|
+}
|