添加项目文件。

This commit is contained in:
DK
2022-03-28 15:27:40 +08:00
parent b620fcb0cf
commit dc78fd1a09
898 changed files with 173053 additions and 0 deletions
@@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
namespace IRaCIS.Core.API.Filter
{
public class EnableBufferingAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
context.HttpContext.Request.EnableBuffering();
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
}
}
}
+270
View File
@@ -0,0 +1,270 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Net.Http.Headers;
namespace IRaCIS.Core.API.Utility
{
public static class FileHelpers
{
private static readonly byte[] _allowedChars = { };
// For more file signatures, see the File Signatures Database (https://www.filesignatures.net/)
// and the official specifications for the file types you wish to add.
private static readonly Dictionary<string, List<byte[]>> _fileSignature = new Dictionary<string, List<byte[]>>
{
{ ".gif", new List<byte[]> { new byte[] { 0x47, 0x49, 0x46, 0x38 } } },
{ ".png", new List<byte[]> { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A } } },
{ ".jpeg", new List<byte[]>
{
new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 },
new byte[] { 0xFF, 0xD8, 0xFF, 0xE2 },
new byte[] { 0xFF, 0xD8, 0xFF, 0xE3 },
}
},
{ ".jpg", new List<byte[]>
{
new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 },
new byte[] { 0xFF, 0xD8, 0xFF, 0xE1 },
new byte[] { 0xFF, 0xD8, 0xFF, 0xE8 },
}
},
{ ".zip", new List<byte[]>
{
new byte[] { 0x50, 0x4B, 0x03, 0x04 },
new byte[] { 0x50, 0x4B, 0x4C, 0x49, 0x54, 0x45 },
new byte[] { 0x50, 0x4B, 0x53, 0x70, 0x58 },
new byte[] { 0x50, 0x4B, 0x05, 0x06 },
new byte[] { 0x50, 0x4B, 0x07, 0x08 },
new byte[] { 0x57, 0x69, 0x6E, 0x5A, 0x69, 0x70 },
}
},
};
// **WARNING!**
// In the following file processing methods, the file's content isn't scanned.
// In most production scenarios, an anti-virus/anti-malware scanner API is
// used on the file before making the file available to users or other
// systems. For more information, see the topic that accompanies this sample
// app.
public static async Task<byte[]> ProcessFormFile<T>(IFormFile formFile,
ModelStateDictionary modelState, string[] permittedExtensions,
long sizeLimit)
{
var fieldDisplayName = string.Empty;
// Use reflection to obtain the display name for the model
// property associated with this IFormFile. If a display
// name isn't found, error messages simply won't show
// a display name.
MemberInfo property =
typeof(T).GetProperty(
formFile.Name.Substring(formFile.Name.IndexOf(".",
StringComparison.Ordinal) + 1));
if (property != null)
{
if (property.GetCustomAttribute(typeof(DisplayAttribute)) is
DisplayAttribute displayAttribute)
{
fieldDisplayName = $"{displayAttribute.Name} ";
}
}
// Don't trust the file name sent by the client. To display
// the file name, HTML-encode the value.
var trustedFileNameForDisplay = WebUtility.HtmlEncode(
formFile.FileName);
// Check the file length. This check doesn't catch files that only have
// a BOM as their content.
if (formFile.Length == 0)
{
modelState.AddModelError(formFile.Name,
$"{fieldDisplayName}({trustedFileNameForDisplay}) is empty.");
return new byte[0];
}
if (formFile.Length > sizeLimit)
{
var megabyteSizeLimit = sizeLimit / 1048576;
modelState.AddModelError(formFile.Name,
$"{fieldDisplayName}({trustedFileNameForDisplay}) exceeds " +
$"{megabyteSizeLimit:N1} MB.");
return new byte[0];
}
try
{
using (var memoryStream = new MemoryStream())
{
await formFile.CopyToAsync(memoryStream);
// Check the content length in case the file's only
// content was a BOM and the content is actually
// empty after removing the BOM.
if (memoryStream.Length == 0)
{
modelState.AddModelError(formFile.Name,
$"{fieldDisplayName}({trustedFileNameForDisplay}) is empty.");
}
if (!IsValidFileExtensionAndSignature(
formFile.FileName, memoryStream, permittedExtensions))
{
modelState.AddModelError(formFile.Name,
$"{fieldDisplayName}({trustedFileNameForDisplay}) file " +
"type isn't permitted or the file's signature " +
"doesn't match the file's extension.");
}
else
{
return memoryStream.ToArray();
}
}
}
catch (Exception ex)
{
modelState.AddModelError(formFile.Name,
$"{fieldDisplayName}({trustedFileNameForDisplay}) upload failed. " +
$"Please contact the Help Desk for support. Error: {ex.HResult}");
}
return new byte[0];
}
public static async Task<byte[]> ProcessStreamedFile(
MultipartSection section, ContentDispositionHeaderValue contentDisposition,
ModelStateDictionary modelState, string[] permittedExtensions, long sizeLimit)
{
try
{
using (var memoryStream = new MemoryStream())
{
await section.Body.CopyToAsync(memoryStream);
// Check if the file is empty or exceeds the size limit.
if (memoryStream.Length == 0)
{
modelState.AddModelError("File", "The file is empty.");
}
else if (memoryStream.Length > sizeLimit)
{
var megabyteSizeLimit = sizeLimit / 1048576;
modelState.AddModelError("File",
$"The file exceeds {megabyteSizeLimit:N1} MB.");
}
else if (!IsValidFileExtensionAndSignature(
contentDisposition.FileName.Value, memoryStream,
permittedExtensions))
{
modelState.AddModelError("File",
"The file type isn't permitted or the file's " +
"signature doesn't match the file's extension.");
}
else
{
return memoryStream.ToArray();
}
}
}
catch (Exception ex)
{
modelState.AddModelError("File",
"The upload failed. Please contact the Help Desk " +
$" for support. Error: {ex.HResult}");
// Log the exception
}
return new byte[0];
}
private static bool IsValidFileExtensionAndSignature(string fileName, Stream data, string[] permittedExtensions)
{
if (string.IsNullOrEmpty(fileName) || data == null || data.Length == 0)
{
return false;
}
var ext = Path.GetExtension(fileName).ToLowerInvariant();
if (string.IsNullOrEmpty(ext) || !permittedExtensions.Contains(ext))
{
return false;
}
data.Position = 0;
using (var reader = new BinaryReader(data))
{
if (ext.Equals(".txt") || ext.Equals(".csv") || ext.Equals(".prn"))
{
if (_allowedChars.Length == 0)
{
// Limits characters to ASCII encoding.
for (var i = 0; i < data.Length; i++)
{
if (reader.ReadByte() > sbyte.MaxValue)
{
return false;
}
}
}
else
{
// Limits characters to ASCII encoding and
// values of the _allowedChars array.
for (var i = 0; i < data.Length; i++)
{
var b = reader.ReadByte();
if (b > sbyte.MaxValue ||
!_allowedChars.Contains(b))
{
return false;
}
}
}
return true;
}
// Uncomment the following code block if you must permit
// files whose signature isn't provided in the _fileSignature
// dictionary. We recommend that you add file signatures
// for files (when possible) for all file types you intend
// to allow on the system and perform the file signature
// check.
//if (!_fileSignature.ContainsKey(ext))
//{
// return true;
//}
// File signature check
// --------------------
// With the file signatures provided in the _fileSignature
// dictionary, the following code tests the input content's
// file signature.
//var signatures = _fileSignature[ext];
//var headerBytes = reader.ReadBytes(signatures.Max(m => m.Length));
//return signatures.Any(signature =>
// headerBytes.Take(signature.Length).SequenceEqual(signature));
//test
return true;
}
}
}
}
@@ -0,0 +1,65 @@
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
{
/// <summary>
/// 对称可逆加密
/// </summary>
public class CustomHSJWTService : ICustomJWTService
{
#region Option注入
private readonly JWTTokenOptions _JWTTokenOptions;
public CustomHSJWTService(IOptionsMonitor<JWTTokenOptions> jwtTokenOptions)
{
this._JWTTokenOptions = jwtTokenOptions.CurrentValue;
}
#endregion
/// <summary>
/// 用户登录成功以后,用来生成Token的方法
/// </summary>
/// <param name="UserName"></param>
/// <param name="password"></param>
/// <returns></returns>
public string GetToken(string UserName, string password)
{
#region
var claims = new[]
{
new Claim(ClaimTypes.Name, UserName),
new Claim("NickName",UserName),
new Claim("Role","Administrator"),//传递其他信息
new Claim("ABCC","ABCC"),
new Claim("ABCCDDDDD","ABCCDDDDD"),
new Claim("Student","甜酱油")
};
//需要加密:需要加密key:
//Nuget引入:Microsoft.IdentityModel.Tokens
SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_JWTTokenOptions.SecurityKey));
SigningCredentials creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
//Nuget引入:System.IdentityModel.Tokens.Jwt
JwtSecurityToken token = new JwtSecurityToken(
issuer: _JWTTokenOptions.Issuer,
audience: _JWTTokenOptions.Audience,
claims: claims,
expires: DateTime.Now.AddMinutes(5),//5分钟有效期
signingCredentials: creds);
string returnToken = new JwtSecurityTokenHandler().WriteToken(token);
return returnToken;
#endregion
}
}
}
@@ -0,0 +1,60 @@
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Threading.Tasks;
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
{
/// <summary>
/// 非对称可逆加密
/// </summary>
public class CustomRSSJWTervice : ICustomJWTService
{
#region Option注入
private readonly JWTTokenOptions _JWTTokenOptions;
public CustomRSSJWTervice(IOptionsMonitor<JWTTokenOptions> jwtTokenOptions)
{
this._JWTTokenOptions = jwtTokenOptions.CurrentValue;
}
#endregion
public string GetToken(string userName, string password)
{
#region 使Key
string keyDir = Directory.GetCurrentDirectory();
if (RSAHelper.TryGetKeyParameters(keyDir, true, out RSAParameters keyParams) == false)
{
keyParams = RSAHelper.GenerateAndSaveKey(keyDir);
}
#endregion
//string jtiCustom = Guid.NewGuid().ToString();//用来标识 Token
Claim[] claims = new[]
{
new Claim(ClaimTypes.Name, userName),
new Claim(ClaimTypes.Role,"admin"),
new Claim("password",password)
};
SigningCredentials credentials = new SigningCredentials(new RsaSecurityKey(keyParams), SecurityAlgorithms.RsaSha256Signature);
var token = new JwtSecurityToken(
issuer: this._JWTTokenOptions.Issuer,
audience: this._JWTTokenOptions.Audience,
claims: claims,
expires: DateTime.Now.AddMinutes(60),//5分钟有效期
signingCredentials: credentials);
var handler = new JwtSecurityTokenHandler();
string tokenString = handler.WriteToken(token);
return tokenString;
}
}
}
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
{
public interface ICustomJWTService
{
string GetToken(string UserName, string password);
}
}
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
{
public class JWTTokenOptions
{
public string Audience
{
get;
set;
}
public string SecurityKey
{
get;
set;
}
//public SigningCredentials Credentials
//{
// get;
// set;
//}
public string Issuer
{
get;
set;
}
}
}
+61
View File
@@ -0,0 +1,61 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
{
public class RSAHelper
{
/// <summary>
/// 从本地文件中读取用来签发 Token 的 RSA Key
/// </summary>
/// <param name="filePath">存放密钥的文件夹路径</param>
/// <param name="withPrivate"></param>
/// <param name="keyParameters"></param>
/// <returns></returns>
public static bool TryGetKeyParameters(string filePath, bool withPrivate, out RSAParameters keyParameters)
{
string filename = withPrivate ? "key.json" : "key.public.json";
string fileTotalPath = Path.Combine(filePath, filename);
keyParameters = default(RSAParameters);
if (!File.Exists(fileTotalPath))
{
return false;
}
else
{
keyParameters = JsonConvert.DeserializeObject<RSAParameters>(File.ReadAllText(fileTotalPath));
return true;
}
}
/// <summary>
/// 生成并保存 RSA 公钥与私钥
/// </summary>
/// <param name="filePath"></param>
/// <param name="withPrivate"></param>
/// <returns></returns>
public static RSAParameters GenerateAndSaveKey(string filePath, bool withPrivate = true)
{
RSAParameters publicKeys, privateKeys;
using (var rsa = new RSACryptoServiceProvider(2048))//即时生成
{
try
{
privateKeys = rsa.ExportParameters(true);
publicKeys = rsa.ExportParameters(false);
}
finally
{
rsa.PersistKeyInCsp = false;
}
}
File.WriteAllText(Path.Combine(filePath, "key.json"), JsonConvert.SerializeObject(privateKeys));
File.WriteAllText(Path.Combine(filePath, "key.public.json"), JsonConvert.SerializeObject(publicKeys));
return withPrivate ? privateKeys : publicKeys;
}
}
}
@@ -0,0 +1,53 @@
using System;
using System.IO;
using Microsoft.Net.Http.Headers;
namespace IRaCIS.Core.API.Utility
{
public static class MultipartRequestHelper
{
// Content-Type: multipart/form-data; boundary="----WebKitFormBoundarymx2fSWqWSd0OxQqq"
// The spec at https://tools.ietf.org/html/rfc2046#section-5.1 states that 70 characters is a reasonable limit.
public static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit)
{
var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary).Value;
if (string.IsNullOrWhiteSpace(boundary))
{
throw new InvalidDataException("Missing content-type boundary.");
}
if (boundary.Length > lengthLimit)
{
throw new InvalidDataException(
$"Multipart boundary length limit {lengthLimit} exceeded.");
}
return boundary;
}
public static bool IsMultipartContentType(string contentType)
{
return !string.IsNullOrEmpty(contentType)
&& contentType.IndexOf("multipart/", StringComparison.OrdinalIgnoreCase) >= 0;
}
public static bool HasFormDataContentDisposition(ContentDispositionHeaderValue contentDisposition)
{
// Content-Disposition: form-data; name="key";
return contentDisposition != null
&& contentDisposition.DispositionType.Equals("form-data")
&& string.IsNullOrEmpty(contentDisposition.FileName.Value)
&& string.IsNullOrEmpty(contentDisposition.FileNameStar.Value);
}
public static bool HasFileContentDisposition(ContentDispositionHeaderValue contentDisposition)
{
// Content-Disposition: form-data; name="myfile1"; filename="Misc 002.jpg"
return contentDisposition != null
&& contentDisposition.DispositionType.Equals("form-data")
&& (!string.IsNullOrEmpty(contentDisposition.FileName.Value)
|| !string.IsNullOrEmpty(contentDisposition.FileNameStar.Value));
}
}
}