Last active
March 1, 2023 07:00
-
-
Save zhaopan/8aac1993a1a61b6b50ecc69e563e32af to your computer and use it in GitHub Desktop.
EnumPower 角色权限
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Text; | |
namespace Common.Lib | |
{ | |
/// <summary> | |
/// 角色权限对象,拥有指定权限。(可支持64个独立的权限项) | |
/// </summary> | |
/// <typeparam name="T">泛型 T (权限列表)必须是一个枚举。</typeparam> | |
/// <remarks> | |
/// <para>此模块基于二进制操作编写。</para> | |
/// <para>要使用此模块,只需要定义一个枚举即可,这个枚举即表示了各个独立的权限项。但是这个枚举必须满足以下条件: | |
/// </para> | |
/// <ol> | |
/// <li>以ulong为基类。</li> | |
/// <li>枚举值必须为2的正整数次方或1。</li> | |
/// <li>枚举项不超过 64 项。</li> | |
/// </ol> | |
/// </remarks> | |
/// <example> | |
/// <para> | |
/// 例如下面这个枚举就是一个合法的权限枚举: | |
/// </para> | |
/// <code> | |
/// [Flags] | |
/// public enum PowerItemExample : ulong | |
/// { | |
/// 查看用户 = 1, | |
/// 增改用户 = 2, | |
/// 删除用户 = 4, | |
/// | |
/// 查看系统日志 = 8, | |
/// 管理其他管理员 = 16, | |
/// 关闭网站 = 32 | |
/// } | |
/// </code> | |
/// <para> | |
/// <strong>* 需特别注意:必须显式地为枚举项提供值,否则若有任何枚举项值为 0,就不符合权限枚举的前提条件了。</strong></para> | |
/// <para> | |
/// 如上所示,在这个权限枚举 <typeparamref name="T"/> 上声明上了 <see cref="System.FlagsAttribute"/> 属性,以便使权限项支持按位操作。<br /> | |
/// 按位操作时请务必注意逻辑关系。例如,同时拥有“查看用户”和“增改用户”的权限: | |
/// <ul> | |
/// <li><![CDATA[正确的:PowerItemExample.查看用户 | PowerItemExample.增改用户。]]></li> | |
/// <li><![CDATA[错误的:PowerItemExample.查看用户 & PowerItemExample.增改用户。(此表达式将恒等于 0,即无权限。)]]></li> | |
/// </ul> | |
/// 而要表示拥有“查看用户”或“增改用户”的权限时,只能分开表示这两个权限。在这一点上请尤为注意。 | |
/// </para> | |
/// <para> | |
/// 权限枚举 <typeparamref name="T"/> 在进行按位操作之后所得到的返回值,仍然是 <typeparamref name="T"/> 类型。 | |
/// </para> | |
/// <para></para> | |
/// <para>有了上面的枚举项,我们就可以使用这个模块进行权限相关的操作。如:</para> | |
/// | |
/// <para><strong>建立一个无权限的角色对象:</strong></para> | |
/// <code><![CDATA[ | |
/// EnumPower<PowerItemExample> m = new PoweredMember<PowerItemExample>(); | |
/// ]]></code> | |
/// | |
/// <para><strong>建立拥有“查看用户”和“增改用户”权限的角色对象:</strong></para> | |
/// <code><![CDATA[ | |
/// EnumPower<PowerItemExample> member = new PoweredMember<PowerItemExample>(PowerItemExample.查看用户 | PowerItemExample.增改用户); | |
/// ]]></code> | |
/// | |
/// <para><strong>为此角色对象赋予“查看用户”和“查看系统日志”的权限:</strong></para> | |
/// <code><![CDATA[ | |
/// // 此操作将在原有的权限基础上赋予新的权限。member的旧权限为“查看用户、增改用户”,现在的新权限为“查看用户、增改用户、查看系统日志”。 | |
/// // 即,在赋予权限时,将会把新的权限添加进去,而不会理会旧的权限。 | |
/// member.Append(PowerItemExample.查看用户 | PowerItemExample.查看系统日志); | |
/// ]]></code> | |
/// | |
/// <para><strong>剥夺此角色对象“删除用户”和“增改用户”的权限:</strong></para> | |
/// <code><![CDATA[ | |
/// // 此操作将从原有的权限中除去指定的权限。member的旧权限为“查看用户、增改用户、查看系统日志”,现在的新权限为“查看用户、查看系统日志”。 | |
/// // 即,在剥夺权限时,将会把指定的权限从原有权限中除去,如果原来本没用某种权限,将不会理睬。 | |
/// member.Remove(PowerItemExample.删除用户 | PowerItemExample.增改用户); | |
/// ]]></code> | |
/// | |
/// <para><strong>检查角色对象是否具有指定的权限:</strong></para> | |
/// <code><![CDATA[ | |
/// member.Check(PowerItemExample.删除para用户); // false | |
/// member.Check(PowerItemExample.查看用户); // true | |
/// member.Check(PowerItemExample.查看系统日志); // true | |
/// member.Check(PowerItemExample.查看用户 | PowerItemExample.查看系统日志); // true | |
/// ]]></code> | |
/// | |
/// <para>为方便调试输出,此模块重写了ToString方法用于输出权限细节。例如输出上文最后状态的 member.ToString(),则会有:</para> | |
/// <code> | |
/// 角色权限 | |
/// ------------------------------------- | |
/// 权限标识(ulong):9 | |
/// 二进制字符串:1001 | |
/// 权限详情:(查看用户, 查看系统日志) | |
/// 允许:查看用户 | |
/// 禁止:增改用户 | |
/// 禁止:删除用户 | |
/// 允许:查看系统日志 | |
/// 禁止:管理其他管理员 | |
/// 禁止:关闭网站 | |
/// </code> | |
/// </example> | |
/// <exception cref="ArgumentException">泛型 T (权限列表)必须是一个枚举!</exception> | |
/// <exception cref="ArgumentException">泛型 T (权限列表)所指定的枚举其基础类型必须是 ulong !</exception> | |
/// <exception cref="ArgumentException">泛型 T (权限列表)的值必须是2的正整数次方或1,且不能重复!</exception> | |
/// <exception cref="ArgumentOutOfRangeException">参数必须大于 -1 !</exception> | |
/// <exception cref="ArgumentOutOfRangeException">参数必须是由0、1构成的二进制字符串!</exception> | |
public class EnumPower<T> where T : IComparable, IConvertible, IFormattable | |
{ | |
#region CONST | |
private const string TTypeErr = "泛型 T (权限列表)必须是一个枚举!"; | |
private const string TBaseErr = "泛型 T (权限列表)所指定的枚举其基础类型必须是 ulong !"; | |
private const string TValueErr = "泛型 T (权限列表)的值必须是2的正整数次方或1,且不能重复!"; | |
private const string MinPowerErr = "参数必须大于 -1 !"; | |
private const string PowerStringErr = "参数必须是由0、1构成的二进制字符串!"; | |
#endregion CONST | |
#region Properties | |
private T _power; | |
/// <summary> | |
/// 获取当前对象的权限 | |
/// </summary> | |
public T Power | |
{ | |
get => _power; | |
private set | |
{ | |
#region 检查枚举值的合法性 | |
Array values = Enum.GetValues(value.GetType()); | |
ulong tmpV = 0; | |
//检查泛型的每一个值的合法性 | |
for (int n = 0; n < values.Length; n++) | |
{ | |
tmpV = TtoULong((T)values.GetValue(n)); | |
//检查是否为2的正整数次方或1 | |
if (!IsPowerOfTwo(tmpV) && tmpV != 1) { throw new ArgumentException(TValueErr); } | |
//检查当前值是否重复 | |
for (int k = 0; k < n; k++) | |
{ | |
if (tmpV == TtoULong((T)values.GetValue(k))) | |
{ | |
throw new ArgumentException(TValueErr); | |
} | |
} | |
} | |
#endregion 检查枚举值的合法性 | |
_power = value; | |
} | |
} | |
//检查是否为2的正整数次方 | |
private static bool IsPowerOfTwo(ulong value) | |
{ | |
if (value > 1) | |
{ | |
/* | |
* 针对每一个 ulong 的正整数,如果是2的正整数次方,则转换为二进制表示时一定有且仅有一个“1”存在。 | |
* 基于以上原理,则可以按位来比较。 | |
* 从低位到高位查询,遇到“1”后停止比较,所得到的数应当与原数相等, | |
* 否则该数的二进制形式不止一个“1”位,即该数一定不是2的正整数次方。 | |
*/ | |
ulong mask = 1ul; | |
for (int count = 0; count < 64; count++) | |
{ | |
//检查当前标识位是否为“0” | |
if ((mask & value) == 0) | |
{ | |
//当前位为“0”,则左移标识位,再次检查 | |
mask <<= 1; | |
continue; | |
} | |
/* | |
* 程序执行到此,表明当前标识位为“1”,停止检查。 | |
* 而此时的 mask 值正好应当与 value 相等,否则 value 就不是2的正整数次方。 | |
*/ | |
return mask == value; | |
} | |
} | |
return false; | |
} | |
/// <summary> | |
/// 获取当前对象权限的二进制字符串形式 | |
/// </summary> | |
public string PowerString | |
{ | |
get | |
{ | |
ulong v = TtoULong(Power); | |
return UlongToStr(v); | |
} | |
} | |
/// <summary> | |
/// 获取当前对象权限的长整形形式 | |
/// </summary> | |
public ulong PowerUInt64 => TtoULong(Power); | |
#endregion Properties | |
#region ctor | |
/// <summary> | |
/// 构造一个角色权限对象,无任何权限。 | |
/// </summary> | |
public EnumPower() | |
{ | |
if (Enum.GetUnderlyingType(typeof(T)) != typeof(ulong)) { throw new ArgumentException(TBaseErr); } | |
T tmp = (T)Enum.Parse(typeof(T), 0.ToString()); | |
Power = tmp; | |
} | |
/// <summary> | |
/// 构造一个角色权限对象,权限由权限枚举指定。 | |
/// </summary> | |
/// <param name="ownedPower">该角色拥有的权限。如:“<typeparamref name="T"/>.查看用户 | <typeparamref name="T"/>.增改用户”</param> | |
public EnumPower(T ownedPower) | |
{ | |
if (!(ownedPower.GetType().IsEnum)) | |
{ | |
throw new ArgumentException(TTypeErr); | |
} | |
if (Enum.GetUnderlyingType(typeof(T)) != typeof(ulong)) { throw new ArgumentException(TBaseErr); } | |
Power = ownedPower; | |
} | |
/// <summary> | |
/// 构造一个角色权限对象,权限由权限标识指定(标识将被转换为二进制形式)。 | |
/// </summary> | |
/// <param name="ownedPower">该角色拥有的权限(标识,将被转换为二进制形式)</param> | |
public EnumPower(ulong ownedPower) | |
{ | |
if (ownedPower < 0) { throw new ArgumentOutOfRangeException(MinPowerErr); } | |
if (Enum.GetUnderlyingType(typeof(T)) != typeof(ulong)) { throw new ArgumentException(TBaseErr); } | |
T tmp = (T)Enum.Parse(typeof(T), ownedPower.ToString()); | |
Power = tmp; | |
} | |
/// <summary> | |
/// 构造一个角色权限对象,权限由二进制字符串指定。 | |
/// </summary> | |
/// <param name="ownedPower">该角色拥有的权限(二进制如:“10000100000”)</param> | |
public EnumPower(string ownedPower) | |
{ | |
ulong p = 0UL; | |
if (!string.IsNullOrEmpty(ownedPower) && ownedPower.Trim().Length > 0) | |
{ | |
if (ownedPower.Trim().Replace("0", "").Replace("1", "").Length != 0) | |
{ | |
throw new ArgumentOutOfRangeException(PowerStringErr); | |
} | |
p = Convert.ToUInt64(ownedPower, 2); | |
} | |
T tmp = (T)Enum.Parse(typeof(T), p.ToString()); | |
Power = tmp; | |
} | |
#endregion ctor | |
#region public | |
/// <summary> | |
/// 检查当前对象是否满足指定权限要求 | |
/// </summary> | |
/// <param name="powerNeeded">需要满足的权限</param> | |
/// <returns></returns> | |
public bool Check(T powerNeeded) | |
{ | |
return Check(Power, powerNeeded); | |
} | |
/// <summary> | |
/// 检查当前对象是否满足指定权限要求. | |
/// </summary> | |
/// <param name="power">权限集合.</param> | |
/// <param name="powerNeeded">需要满足的权限</param> | |
/// <remarks> | |
/// True:满足权限.反之不满足. | |
/// </remarks> | |
/// <returns></returns> | |
public static bool Check(T power, T powerNeeded) | |
{ | |
//if (!Enum.IsDefined(typeof(T), powerNeeded)) | |
// throw new ArgumentOutOfRangeException("powerNeeded", "枚举值 “" + powerNeeded.ToString() + "” 未定义!"); | |
if (power.ToString() == "0") { return false; } | |
if (powerNeeded.ToString() == "0") { return false; } | |
ulong uPower = TtoULong(power); | |
ulong uPowerNeeded = TtoULong(powerNeeded); | |
//用户拥有的权限: 10110 | |
//操作需要的权限: 10010 | |
//& 操作 ------------- | |
// 10010 | |
// = 操作需要的权限 | |
return ((uPower & uPowerNeeded) == uPowerNeeded); | |
} | |
/// <summary> | |
/// 给现有角色对象赋予一个权限 | |
/// </summary> | |
/// <param name="power">要赋予的权限</param> | |
public void Append(T power) | |
{ | |
ulong n = TtoULong(_power); | |
ulong myPower = TtoULong(power); | |
_power = (T)Enum.Parse(typeof(T), (n | myPower).ToString()); | |
} | |
/// <summary> | |
/// 从现有角色对象剥夺一个权限 | |
/// </summary> | |
/// <param name="power"></param> | |
public void Remove(T power) | |
{ | |
ulong n = TtoULong(_power); | |
ulong myPower = TtoULong(power); | |
_power = (T)Enum.Parse(typeof(T), (n ^ (n & myPower)).ToString()); | |
} | |
/// <summary> | |
/// 查看当前对象的权限详情 | |
/// </summary> | |
/// <returns></returns> | |
public override string ToString() | |
{ | |
StringBuilder sb = new StringBuilder(); | |
sb.AppendLine("角色权限"); | |
sb.AppendLine("-------------------------------------"); | |
sb.AppendLine("权限标识(ulong):" + TtoULong(Power)); | |
sb.AppendLine("二进制字符串:" + PowerString); | |
sb.AppendLine("权限详情:(" + Power + ")"); | |
Array powers = Enum.GetValues(typeof(T)); | |
IEnumerator ie = powers.GetEnumerator(); | |
T tmpP; | |
while (ie.MoveNext()) | |
{ | |
tmpP = (T)Enum.Parse(typeof(T), ie.Current.ToString()); | |
if (Check(tmpP)) | |
{ | |
sb.AppendLine("\t允许:" + tmpP.ToString()); | |
} | |
else | |
{ | |
sb.AppendLine("\t禁止:" + tmpP.ToString()); | |
} | |
} | |
return sb.ToString(); | |
} | |
#endregion public | |
#region private | |
/// <summary> | |
/// 将64位无符号整数(ulong)转换为二进制字符串 | |
/// </summary> | |
/// <param name="value"></param> | |
/// <returns></returns> | |
private string UlongToStr(ulong value) | |
{ | |
int size = 64; | |
ulong mask = 1ul << size - 1; | |
StringBuilder sb = new StringBuilder(); | |
string result = string.Empty; | |
for (int count = 0; count < size; count++) | |
{ | |
result = ((mask & value) > 0) ? "1" : "0"; | |
sb.Append(result); | |
// Shift mask one location over to the right | |
mask >>= 1; | |
} | |
return sb.ToString().TrimStart('0'); | |
} | |
/// <summary> | |
/// 将权限对象转化为长整形形式 | |
/// </summary> | |
/// <param name="obj"></param> | |
/// <returns></returns> | |
private static ulong TtoULong(T obj) | |
{ | |
return (ulong)Enum.Parse(typeof(T), obj.ToString()); | |
} | |
#endregion private | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment