mirror of
https://gitee.com/jisol/jisol-game/
synced 2025-09-27 02:36:14 +00:00
提交Unity 联机Pro
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
namespace BestHTTP.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// Authentication types that supported by BestHTTP.
|
||||
/// The authentication is defined by the server, so the Basic and Digest are not interchangeable. If you don't know what to use, the preferred way is to choose Unknow.
|
||||
/// </summary>
|
||||
public enum AuthenticationTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// If the authentication type is not known this will do a challenge turn to receive what methode should be choosen.
|
||||
/// </summary>
|
||||
Unknown,
|
||||
|
||||
/// <summary>
|
||||
/// The most basic authentication type. It's easy to do, and easy to crack. ;)
|
||||
/// </summary>
|
||||
Basic,
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
Digest
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hold all information that required to authenticate to a remote server.
|
||||
/// </summary>
|
||||
public sealed class Credentials
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of the Authentication. If you don't know what to use, the preferred way is to choose Unknow.
|
||||
/// </summary>
|
||||
public AuthenticationTypes Type { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The username to authenticate on the remote server.
|
||||
/// </summary>
|
||||
public string UserName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The password to use in the authentication process. The password will be stored only in this class.
|
||||
/// </summary>
|
||||
public string Password { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set up the authentication credentials with the username and password. The Type will be set to Unknown.
|
||||
/// </summary>
|
||||
public Credentials(string userName, string password)
|
||||
:this(AuthenticationTypes.Unknown, userName, password)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set up the authentication credentials with the given authentication type, username and password.
|
||||
/// </summary>
|
||||
public Credentials(AuthenticationTypes type, string userName, string password)
|
||||
{
|
||||
this.Type = type;
|
||||
this.UserName = userName;
|
||||
this.Password = password;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6a6217993b733a4e8555355d5d62776
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,281 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BestHTTP.Authentication
|
||||
{
|
||||
using BestHTTP.Extensions;
|
||||
using BestHTTP.PlatformSupport.Memory;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// Internal class that stores all information that received from a server in a WWW-Authenticate and need to construct a valid Authorization header. Based on rfc 2617 (http://tools.ietf.org/html/rfc2617).
|
||||
/// Used only internally by the plugin.
|
||||
/// </summary>
|
||||
public sealed class Digest
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// The Uri that this Digest is bound to.
|
||||
/// </summary>
|
||||
public Uri Uri { get; private set; }
|
||||
|
||||
public AuthenticationTypes Type { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A string to be displayed to users so they know which username and password to use.
|
||||
/// This string should contain at least the name of the host performing the authentication and might additionally indicate the collection of users who might have access.
|
||||
/// </summary>
|
||||
public string Realm { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A flag, indicating that the previous request from the client was rejected because the nonce value was stale.
|
||||
/// If stale is TRUE (case-insensitive), the client may wish to simply retry the request with a new encrypted response, without the user for a new username and password.
|
||||
/// The server should only set stale to TRUE if it receives a request for which the nonce is invalid but with a valid digest for that nonce
|
||||
/// (indicating that the client knows the correct username/password).
|
||||
/// If stale is FALSE, or anything other than TRUE, or the stale directive is not present, the username and/or password are invalid, and new values must be obtained.
|
||||
/// </summary>
|
||||
public bool Stale { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Properties
|
||||
|
||||
/// <summary>
|
||||
/// A server-specified data string which should be uniquely generated each time a 401 response is made.
|
||||
/// Specifically, since the string is passed in the header lines as a quoted string, the double-quote character is not allowed.
|
||||
/// </summary>
|
||||
private string Nonce { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A string of data, specified by the server, which should be returned by the client unchanged in the Authorization header of subsequent requests with URIs in the same protection space.
|
||||
/// It is recommended that this string be base64 or data.
|
||||
/// </summary>
|
||||
private string Opaque { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A string indicating a pair of algorithms used to produce the digest and a checksum. If this is not present it is assumed to be "MD5".
|
||||
/// If the algorithm is not understood, the challenge should be ignored (and a different one used, if there is more than one).
|
||||
/// </summary>
|
||||
private string Algorithm { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of URIs, as specified in RFC XURI, that define the protection space.
|
||||
/// If a URI is an abs_path, it is relative to the canonical root URL (see section 1.2 above) of the server being accessed.
|
||||
/// An absoluteURI in this list may refer to a different server than the one being accessed.
|
||||
/// The client can use this list to determine the set of URIs for which the same authentication information may be sent:
|
||||
/// any URI that has a URI in this list as a prefix (after both have been made absolute) may be assumed to be in the same protection space.
|
||||
/// If this directive is omitted or its value is empty, the client should assume that the protection space consists of all URIs on the responding server.
|
||||
/// </summary>
|
||||
public List<string> ProtectedUris { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// If present, it is a quoted string of one or more tokens indicating the "quality of protection" values supported by the server.
|
||||
/// The value "auth" indicates authentication. The value "auth-int" indicates authentication with integrity protection.
|
||||
/// </summary>
|
||||
private string QualityOfProtections { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// his MUST be specified if a qop directive is sent (see above), and MUST NOT be specified if the server did not send a qop directive in the WWW-Authenticate header field.
|
||||
/// The nc-value is the hexadecimal count of the number of requests (including the current request) that the client has sent with the nonce value in this request.
|
||||
/// </summary>
|
||||
private int NonceCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used to store the last HA1 that can be used in the next header generation when Algorithm is set to "md5-sess".
|
||||
/// </summary>
|
||||
private string HA1Sess { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
internal Digest(Uri uri)
|
||||
{
|
||||
this.Uri = uri;
|
||||
this.Algorithm = "md5";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a WWW-Authenticate header's value to retrive all information.
|
||||
/// </summary>
|
||||
public void ParseChallange(string header)
|
||||
{
|
||||
// Reset some values to its defaults.
|
||||
this.Type = AuthenticationTypes.Unknown;
|
||||
this.Stale = false;
|
||||
this.Opaque = null;
|
||||
this.HA1Sess = null;
|
||||
this.NonceCount = 0;
|
||||
this.QualityOfProtections = null;
|
||||
|
||||
if (this.ProtectedUris != null)
|
||||
this.ProtectedUris.Clear();
|
||||
|
||||
// Parse the header
|
||||
WWWAuthenticateHeaderParser qpl = new WWWAuthenticateHeaderParser(header);
|
||||
|
||||
// Then process
|
||||
foreach (var qp in qpl.Values)
|
||||
switch (qp.Key)
|
||||
{
|
||||
case "basic": this.Type = AuthenticationTypes.Basic; break;
|
||||
case "digest": this.Type = AuthenticationTypes.Digest; break;
|
||||
case "realm": this.Realm = qp.Value; break;
|
||||
case "domain":
|
||||
{
|
||||
if (string.IsNullOrEmpty(qp.Value) || qp.Value.Length == 0)
|
||||
break;
|
||||
|
||||
if (this.ProtectedUris == null)
|
||||
this.ProtectedUris = new List<string>();
|
||||
|
||||
int idx = 0;
|
||||
string val = qp.Value.Read(ref idx, ' ');
|
||||
do
|
||||
{
|
||||
this.ProtectedUris.Add(val);
|
||||
val = qp.Value.Read(ref idx, ' ');
|
||||
} while (idx < qp.Value.Length);
|
||||
|
||||
break;
|
||||
}
|
||||
case "nonce": this.Nonce = qp.Value; break;
|
||||
case "qop": this.QualityOfProtections = qp.Value; break;
|
||||
case "stale": this.Stale = bool.Parse(qp.Value); break;
|
||||
case "opaque": this.Opaque = qp.Value; break;
|
||||
case "algorithm": this.Algorithm = qp.Value; break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a string that can be set to an Authorization header.
|
||||
/// </summary>
|
||||
public string GenerateResponseHeader(HTTPRequest request, Credentials credentials, bool isProxy = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case AuthenticationTypes.Basic:
|
||||
return string.Concat("Basic ", Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Format("{0}:{1}", credentials.UserName, credentials.Password))));
|
||||
|
||||
case AuthenticationTypes.Digest:
|
||||
{
|
||||
NonceCount++;
|
||||
|
||||
string HA1 = string.Empty;
|
||||
|
||||
// The cnonce-value is an opaque quoted string value provided by the client and used by both client and server to avoid chosen plaintext attacks, to provide mutual
|
||||
// authentication, and to provide some message integrity protection.
|
||||
string cnonce = new System.Random(request.GetHashCode()).Next(int.MinValue, int.MaxValue).ToString("X8");
|
||||
|
||||
string ncvalue = NonceCount.ToString("X8");
|
||||
switch (Algorithm.TrimAndLower())
|
||||
{
|
||||
case "md5":
|
||||
HA1 = string.Format("{0}:{1}:{2}", credentials.UserName, Realm, credentials.Password).CalculateMD5Hash();
|
||||
break;
|
||||
|
||||
case "md5-sess":
|
||||
if (string.IsNullOrEmpty(this.HA1Sess))
|
||||
this.HA1Sess = string.Format("{0}:{1}:{2}:{3}:{4}", credentials.UserName, Realm, credentials.Password, Nonce, ncvalue).CalculateMD5Hash();
|
||||
HA1 = this.HA1Sess;
|
||||
break;
|
||||
|
||||
default: //throw new NotSupportedException("Not supported hash algorithm found in Web Authentication: " + Algorithm);
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// A string of 32 hex digits, which proves that the user knows a password. Set according to the qop value.
|
||||
string response = string.Empty;
|
||||
|
||||
// The server sent QoP-value can be a list of supported methodes(if sent at all - in this case it's null).
|
||||
// The rfc is not specify that this is a space or comma separeted list. So it can be "auth, auth-int" or "auth auth-int".
|
||||
// We will first check the longer value("auth-int") then the short one ("auth"). If one matches we will reset the qop to the exact value.
|
||||
string qop = this.QualityOfProtections != null ? this.QualityOfProtections.TrimAndLower() : null;
|
||||
|
||||
// When we authenticate with a proxy and we want to tunnel the request, we have to use the CONNECT method instead of the
|
||||
// request's, as the proxy will not know about the request itself.
|
||||
string method = isProxy ? "CONNECT" : request.MethodType.ToString().ToUpper();
|
||||
|
||||
// When we authenticate with a proxy and we want to tunnel the request, the uri must match what we are sending in the CONNECT request's
|
||||
// Host header.
|
||||
string uri = isProxy ? request.CurrentUri.Host + ":" + request.CurrentUri.Port : request.CurrentUri.GetRequestPathAndQueryURL();
|
||||
|
||||
if (qop == null)
|
||||
{
|
||||
string HA2 = string.Concat(request.MethodType.ToString().ToUpper(), ":", request.CurrentUri.GetRequestPathAndQueryURL()).CalculateMD5Hash();
|
||||
response = string.Format("{0}:{1}:{2}", HA1, Nonce, HA2).CalculateMD5Hash();
|
||||
}
|
||||
else if (qop.Contains("auth-int"))
|
||||
{
|
||||
qop = "auth-int";
|
||||
|
||||
byte[] entityBody = request.GetEntityBody();
|
||||
|
||||
if (entityBody == null)
|
||||
entityBody = BufferPool.NoData; //string.Empty.GetASCIIBytes();
|
||||
|
||||
string HA2 = string.Format("{0}:{1}:{2}", method, uri, new BufferSegment(entityBody, 0, entityBody.Length).CalculateMD5Hash()).CalculateMD5Hash();
|
||||
|
||||
response = string.Format("{0}:{1}:{2}:{3}:{4}:{5}", HA1, Nonce, ncvalue, cnonce, qop, HA2).CalculateMD5Hash();
|
||||
}
|
||||
else if (qop.Contains("auth"))
|
||||
{
|
||||
qop = "auth";
|
||||
string HA2 = string.Concat(method, ":", uri).CalculateMD5Hash();
|
||||
|
||||
response = string.Format("{0}:{1}:{2}:{3}:{4}:{5}", HA1, Nonce, ncvalue, cnonce, qop, HA2).CalculateMD5Hash();
|
||||
}
|
||||
else //throw new NotSupportedException("Unrecognized Quality of Protection value found: " + this.QualityOfProtections);
|
||||
return string.Empty;
|
||||
|
||||
string result = string.Format("Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", cnonce=\"{4}\", response=\"{5}\"",
|
||||
credentials.UserName, Realm, Nonce, uri, cnonce, response);
|
||||
|
||||
if (qop != null)
|
||||
result += String.Concat(", qop=\"", qop, "\", nc=", ncvalue);
|
||||
|
||||
if (!string.IsNullOrEmpty(Opaque))
|
||||
result = String.Concat(result, ", opaque=\"", Opaque, "\"");
|
||||
|
||||
return result;
|
||||
}// end of case "digest":
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public bool IsUriProtected(Uri uri)
|
||||
{
|
||||
// http://tools.ietf.org/html/rfc2617#section-3.2.1
|
||||
// An absoluteURI in this list may refer to
|
||||
// a different server than the one being accessed. The client can use
|
||||
// this list to determine the set of URIs for which the same
|
||||
// authentication information may be sent: any URI that has a URI in
|
||||
// this list as a prefix (after both have been made absolute) may be
|
||||
// assumed to be in the same protection space. If this directive is
|
||||
// omitted or its value is empty, the client should assume that the
|
||||
// protection space consists of all URIs on the responding server.
|
||||
|
||||
if (string.CompareOrdinal(uri.Host, this.Uri.Host) != 0)
|
||||
return false;
|
||||
|
||||
string uriStr = uri.ToString();
|
||||
|
||||
if (ProtectedUris != null && ProtectedUris.Count > 0)
|
||||
for (int i = 0; i < ProtectedUris.Count; ++i)
|
||||
if (uriStr.Contains(ProtectedUris[i]))
|
||||
return true;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 424d976119ee3fc438fe98095013859f
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BestHTTP.PlatformSupport.Threading;
|
||||
|
||||
namespace BestHTTP.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores and manages already received digest infos.
|
||||
/// </summary>
|
||||
public static class DigestStore
|
||||
{
|
||||
private static Dictionary<string, Digest> Digests = new Dictionary<string, Digest>();
|
||||
|
||||
private static System.Threading.ReaderWriterLockSlim rwLock = new System.Threading.ReaderWriterLockSlim(System.Threading.LockRecursionPolicy.NoRecursion);
|
||||
|
||||
/// <summary>
|
||||
/// Array of algorithms that the plugin supports. It's in the order of priority(first has the highest priority).
|
||||
/// </summary>
|
||||
private static string[] SupportedAlgorithms = new string[] { "digest", "basic" };
|
||||
|
||||
public static Digest Get(Uri uri)
|
||||
{
|
||||
using (new ReadLock(rwLock))
|
||||
{
|
||||
Digest digest = null;
|
||||
if (Digests.TryGetValue(uri.Host, out digest))
|
||||
if (!digest.IsUriProtected(uri))
|
||||
return null;
|
||||
return digest;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// It will retrieve or create a new Digest for the given Uri.
|
||||
/// </summary>
|
||||
/// <param name="uri"></param>
|
||||
/// <returns></returns>
|
||||
public static Digest GetOrCreate(Uri uri)
|
||||
{
|
||||
using (new WriteLock(rwLock))
|
||||
{
|
||||
Digest digest = null;
|
||||
if (!Digests.TryGetValue(uri.Host, out digest))
|
||||
Digests.Add(uri.Host, digest = new Digest(uri));
|
||||
return digest;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Remove(Uri uri)
|
||||
{
|
||||
using (new WriteLock(rwLock))
|
||||
Digests.Remove(uri.Host);
|
||||
}
|
||||
|
||||
public static string FindBest(List<string> authHeaders)
|
||||
{
|
||||
if (authHeaders == null || authHeaders.Count == 0)
|
||||
return string.Empty;
|
||||
|
||||
List<string> headers = new List<string>(authHeaders.Count);
|
||||
for (int i = 0; i < authHeaders.Count; ++i)
|
||||
headers.Add(authHeaders[i].ToLowerInvariant());
|
||||
|
||||
for (int i = 0; i < SupportedAlgorithms.Length; ++i)
|
||||
{
|
||||
int idx = headers.FindIndex((header) => header.StartsWith(SupportedAlgorithms[i]));
|
||||
if (idx != -1)
|
||||
return authHeaders[idx];
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f808b023553ee2419249f92eb8ac31f
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user