标签:fun 问题 off 用户 write match hashcode isp lambda表达式
2020/01/28, ASP.NET Core 3.1, VS2019, Snowflake雪花算法ID, Enum枚举方法扩展, Lambda方法扩展, Json方法封装
摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站架构【2-公共基础库】
Snowflake雪花算法ID、Enum枚举方法扩展、Lambda方法扩展、Json方法封装
文章目录
此分支项目代码
本章节介绍了MS.Common类库中一些常用公共方法,可以自行添加自己积累的一些库
添加包引用
向MS.Common
类库中添加包引用:
其中Microsoft.AspNetCore.Cryptography.KeyDerivation是为了支持PBKDF2加密方式,这个后文会用到
枚举扩展方法
在MS.Common
类库中新建Extensions文件夹,在其中添加EnumExtension.cs
类:
using System;
using System.ComponentModel;
using System.Reflection;
namespace MS.Common.Extensions
{
public static class EnumExtension
{
///
/// 根据名称拿到枚举
///
///
///
///
public static T GetEnum(this string itemName)
{
return (T)Enum.Parse(typeof(T), itemName);
}
///
/// 根据枚举值拿到枚举
///
///
///
///
public static T GetEnum(this int itemValue)
{
return (T)Enum.Parse(typeof(T), Enum.GetName(typeof(T), itemValue));
}
///
/// 根据枚举值拿到枚举名称
///
///
///
///
public static string GetEnumName(this int itemValue)
{
return Enum.GetName(typeof(T), itemValue);
}
///
/// 根据名称拿到枚举值
///
///
///
///
public static int GetEnumValue(this string itemName)
{
return itemName.GetEnum().GetHashCode();
}
///
/// 枚举获取描述
///
///
///
public static string GetDescription(this Enum item)
{
Type type = item.GetType();
MemberInfo[] memInfo = type.GetMember(item.ToString());
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Length > 0)
return ((DescriptionAttribute)attrs[0]).Description;
}
return item.ToString();//如果不存在描述,则返回枚举名称
}
}
}
Lambda表达式扩展方法
在Extensions中继续添加LambdaExtension.cs
类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace MS.Common.Extensions
{
//add by yzh 2019/04/26 -用于lambda表达式拼接
public class ParameterRebinder : ExpressionVisitor
{
private readonly Dictionary map;
public ParameterRebinder(Dictionary map)
{
this.map = map ?? new Dictionary();
}
public static Expression ReplaceParameters(Dictionary map, Expression exp)
{
return new ParameterRebinder(map).Visit(exp);
}
protected override Expression VisitParameter(ParameterExpression p)
{
ParameterExpression replacement;
if (map.TryGetValue(p, out replacement))
{
p = replacement;
}
return base.VisitParameter(p);
}
}
public static class LambdaExtension
{
public static Expression Compose(this Expression first, Expression second, Func merge)
{
var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);
var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
return Expression.Lambda(merge(first.Body, secondBody), first.Parameters);
}
public static Expression> And(this Expression> first, Expression> second)
{
return first.Compose(second, Expression.And);
}
public static Expression> Or(this Expression> first, Expression> second)
{
return first.Compose(second, Expression.Or);
}
}
}
原生的Lambda表达式不支持动态拼接表达式条件,有了这个扩展方法,就弥补了这个缺点。
Json扩展方法
在Extensions中添加JsonExtension.cs
类:
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace MS.Common.Extensions
{
public static class JsonExtension
{
public static JsonSerializerSettings jsonSetting = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
///
/// 序列化对象,默认禁止循环引用
///
///
///
public static string ToJsonString(this object data)
{
return JsonConvert.SerializeObject(data, jsonSetting);
}
///
/// 序列化对象
///
///
///
///
public static string ToJsonString(this object data, IsoDateTimeConverter timeConverter)
{
return JsonConvert.SerializeObject(data, timeConverter);
}
///
/// 反序列化字符串
///
///
///
///
public static T GetDeserializeObject(this string data)
{
if (string.IsNullOrWhiteSpace(data)) return default;
return JsonConvert.DeserializeObject(data, jsonSetting);
}
///
/// 使用序列化和反序列化获得一次深拷贝
///
///
///
///
public static T GetMemberwiseCopy(this T data)
{
return data.ToJsonString().GetDeserializeObject();
}
}
}
每个方法都写好了注释,默认禁止循环引用
PBKDF2加密方法
在类库中添加Security文件夹,向其中添加Crypto.cs
类:
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
using System;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
namespace MS.Common.Security
{
///
/// Provides helper methods for hashing/salting and verifying passwords.
///
public static class Crypto
{
/* =======================
* HASHED PASSWORD FORMATS
* =======================
*
* Version 3:
* PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations.
* Format: { 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey }
* (All UInt32s are stored big-endian.)
*/
private const int PBKDF2IterCount = 10000;
private const int PBKDF2SubkeyLength = 256 / 8; // 256 bits
private const int SaltSize = 128 / 8; // 128 bits
///
/// Returns a hashed representation of the specified .
///
/// The password to generate a hash value for.
/// The hash value for as a base-64-encoded string.
/// is null.
public static string HashPassword(string password)
{
if (password == null)
{
throw new ArgumentNullException(nameof(password));
}
return HashPasswordInternal(password);
}
///
/// Determines whether the specified RFC 2898 hash and password are a cryptographic match.
///
/// The previously-computed RFC 2898 hash value as a base-64-encoded string.
/// The plaintext password to cryptographically compare with hashedPassword.
/// true if the hash value is a cryptographic match for the password; otherwise, false.
///
/// must be of the format of HashPassword (salt + Hash(salt+input).
///
///
/// or is null.
///
public static bool VerifyHashedPassword(string hashedPassword, string password)
{
if (hashedPassword == null)
{
throw new ArgumentNullException(nameof(hashedPassword));
}
if (password == null)
{
throw new ArgumentNullException(nameof(password));
}
return VerifyHashedPasswordInternal(hashedPassword, password);
}
private static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create();
private static string HashPasswordInternal(string password)
{
var bytes = HashPasswordInternal(password, KeyDerivationPrf.HMACSHA256, PBKDF2IterCount, SaltSize, PBKDF2SubkeyLength);
return Convert.ToBase64String(bytes);
}
private static byte[] HashPasswordInternal(
string password,
KeyDerivationPrf prf,
int iterCount,
int saltSize,
int numBytesRequested)
{
// Produce a version 3 (see comment above) text hash.
var salt = new byte[saltSize];
_rng.GetBytes(salt);
var subkey = KeyDerivation.Pbkdf2(password, salt, prf, iterCount, numBytesRequested);
var outputBytes = new byte[13 + salt.Length + subkey.Length];
// Write format marker.
outputBytes[0] = 0x01;
// Write hashing algorithm version.
WriteNetworkByteOrder(outputBytes, 1, (uint)prf);
// Write iteration count of the algorithm.
WriteNetworkByteOrder(outputBytes, 5, (uint)iterCount);
// Write size of the salt.
WriteNetworkByteOrder(outputBytes, 9, (uint)saltSize);
// Write the salt.
Buffer.BlockCopy(salt, 0, outputBytes, 13, salt.Length);
// Write the subkey.
Buffer.BlockCopy(subkey, 0, outputBytes, 13 + saltSize, subkey.Length);
return outputBytes;
}
private static bool VerifyHashedPasswordInternal(string hashedPassword, string password)
{
var decodedHashedPassword = Convert.FromBase64String(hashedPassword);
if (decodedHashedPassword.Length == 0)
{
return false;
}
try
{
// Verify hashing format.
if (decodedHashedPassword[0] != 0x01)
{
// Unknown format header.
return false;
}
// Read hashing algorithm version.
var prf = (KeyDerivationPrf)ReadNetworkByteOrder(decodedHashedPassword, 1);
// Read iteration count of the algorithm.
var iterCount = (int)ReadNetworkByteOrder(decodedHashedPassword, 5);
// Read size of the salt.
var saltLength = (int)ReadNetworkByteOrder(decodedHashedPassword, 9);
// Verify the salt size: >= 128 bits.
if (saltLength = 128 bits.
var subkeyLength = decodedHashedPassword.Length - 13 - salt.Length;
if (subkeyLength > 24);
buffer[offset + 1] = (byte)(value >> 16);
buffer[offset + 2] = (byte)(value >> 8);
buffer[offset + 3] = (byte)(value >> 0);
}
// Compares two byte arrays for equality.
// The method is specifically written so that the loop is not optimized.
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
private static bool ByteArraysEqual(byte[] a, byte[] b)
{
if (ReferenceEquals(a, b))
{
return true;
}
if (a == null || b == null || a.Length != b.Length)
{
return false;
}
var areSame = true;
for (var i = 0; i
简单说明,网站用户密码加密就使用该方法,这段代码是从开源nuget包CryptoHelper中扒下来的。
雪花算法实现
在类库中添加IDCode文件夹,在IDCode文件夹中继续添加Snowflake文件夹,该文件夹下新建三个类:DisposableAction.cs、IdWorker.cs、TimeExtensions.cs
DisposableAction.cs:
using System;
namespace MS.Common.IDCode
{
public class DisposableAction : IDisposable
{
readonly Action _action;
public DisposableAction(Action action)
{
if (action == null)
throw new ArgumentNullException("action");
_action = action;
}
public void Dispose()
{
_action();
}
}
}
IdWorker.cs:
/** Copyright 2010-2012 Twitter, Inc.*/
/**
* An object that generates IDs.
* This is broken into a separate class in case
* we ever want to support multiple worker threads
* per process
*/
using System;
namespace MS.Common.IDCode
{
public class IdWorker
{
//基准时间
public const long Twepoch = 1288834974657L;
//机器标识位数
const int WorkerIdBits = 5;
//数据标志位数
const int DatacenterIdBits = 5;
//序列号识位数
const int SequenceBits = 12;
//机器ID最大值
const long MaxWorkerId = -1L ^ (-1L MaxWorkerId || workerId MaxDatacenterId || datacenterId
TimeExtensions.cs:
using System;
namespace MS.Common.IDCode
{
public static class TimeExtensions
{
public static Func currentTimeFunc = InternalCurrentTimeMillis;
public static long CurrentTimeMillis()
{
return currentTimeFunc();
}
public static IDisposable StubCurrentTime(Func func)
{
currentTimeFunc = func;
return new DisposableAction(() =>
{
currentTimeFunc = InternalCurrentTimeMillis;
});
}
public static IDisposable StubCurrentTime(long millis)
{
currentTimeFunc = () => millis;
return new DisposableAction(() =>
{
currentTimeFunc = InternalCurrentTimeMillis;
});
}
private static readonly DateTime Jan1st1970 = new DateTime
(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private static long InternalCurrentTimeMillis()
{
return (long)(DateTime.UtcNow - Jan1st1970).TotalMilliseconds;
}
}
}
说明:这部分代码是从snowflake-net中扒来的,使用方法readme里也有,注意应尽量保证全局单例的情况下使用该方法生成ID
总结/说明
- 主要添加了一些常用扩展方法(我自己常用的)
- 实际上传至github项目中还有封装的guid、随机数方法,没有在文中贴出来,有兴趣可以去项目中查看,在IDCode-Guid、IDCode-Random中
ASP.NET Core搭建多层网站架构【2-公共基础库】
标签:fun 问题 off 用户 write match hashcode isp lambda表达式
原文地址:https://www.cnblogs.com/kasnti/p/12237686.html