-
特性应用——C#演示API接口权限标记
第一部分:C#基础入门
实例10:特性应用——API接口权限标记
实例介绍
在后端API开发中,权限验证是核心安全需求:不同用户(如管理员、普通用户)能访问的接口不同。传统的权限验证需要在每个接口里写重复的检查逻辑,而特性(Attribute)可以将权限规则“标记”在接口方法上,再通过反射在运行时统一验证,实现代码解耦。
特性是.NET中给代码元素(类、方法、属性等)添加元数据的机制——元数据是描述代码的数据,程序可以通过反射读取这些元数据并做出相应处理。本实例通过自定义权限特性,标记API接口的权限要求,再用反射实现
vb.net教程C#教程python教程SQL教程access 2010教程
统一权限验证,带你掌握特性的定义、使用和运行时处理全流程。
需求分析
1.定义权限特性:PermissionAttribute,用于标记API方法需要的权限(如Admin、User);
2.模拟API控制器:UserController,包含多个接口方法,用特性标记不同权限要求;
3.权限验证工具:PermissionValidator,通过反射读取方法上的特性,验证用户是否有权限访问;
4.测试场景:
1.管理员用户访问所有接口;
2.普通用户访问标记User权限的接口;
3.无权限用户访问受限接口被拒绝;
5.异常处理:处理方法未标记特性的情况(默认允许/拒绝)。
代码实现
csharp
using System;
using System.Reflection;
namespace AttributePermissionDemo
{
// ==================== 1. 自定义权限特性 ====================
/// <summary>
/// API接口权限标记特性
/// </summary>
[AttributeUsage(AttributeTargets.Method, // 特性可应用于方法
AllowMultiple = false, // 是否允许一个方法标记多个该特性(false=不允许)
Inherited = false)] // 子类是否继承父类方法上的特性(false=不继承)
public class PermissionAttribute : Attribute
{
/// <summary>
/// 所需权限名称
/// </summary>
public string RequiredPermission { get; }
/// <summary>
/// 构造函数:传入所需权限
/// </summary>
/// <param name="requiredPermission">权限名称(如Admin、User)</param>
public PermissionAttribute(string requiredPermission)
{
RequiredPermission = requiredPermission;
}
}
// ==================== 2. 模拟API控制器 ====================
/// <summary>
/// 用户管理API控制器
/// </summary>
public class UserController
{
/// <summary>
/// 获取所有用户(需要Admin权限)
/// </summary>
[Permission("Admin")]
public void GetAllUsers()
{
Console.WriteLine("✅ 成功获取所有用户数据");
}
/// <summary>
/// 获取当前用户信息(需要User权限)
/// </summary>
[Permission("User")]
public void GetCurrentUser()
{
Console.WriteLine("✅ 成功获取当前用户信息");
}
/// <summary>
/// 公开接口(无需权限)
/// </summary>
public void PublicInfo()
{
Console.WriteLine("✅ 公开信息:欢迎访问系统");
}
}
// ==================== 3. 权限验证工具类 ====================
/// <summary>
/// 权限验证工具
/// </summary>
public static class PermissionValidator
{
/// <summary>
/// 验证用户是否有权限访问指定方法
/// </summary>
/// <param name="controller">API控制器实例</param>
/// <param name="methodName">方法名称</param>
/// <param name="userPermissions">用户拥有的权限列表</param>
/// <returns>是否有权限</returns>
public static bool Validate(object controller, string methodName, string[] userPermissions)
{
// 1. 获取方法的Type对象
Type controllerType = controller.GetType();
MethodInfo methodInfo = controllerType.GetMethod(methodName);
if (methodInfo == null)
{
Console.WriteLine($"❌ 方法{methodName}不存在");
return false;
}
// 2. 读取方法上的PermissionAttribute特性
PermissionAttribute permissionAttr = methodInfo.GetCustomAttribute<PermissionAttribute>();
if (permissionAttr == null)
{
// 无特性标记:默认允许访问(可根据业务调整为拒绝)
return true;
}
// 3. 检查用户是否拥有所需权限
string requiredPermission = permissionAttr.RequiredPermission;
bool hasPermission = Array.Exists(userPermissions, p => p.Equals(requiredPermission, StringComparison.OrdinalIgnoreCase));
if (!hasPermission)
{
Console.WriteLine($"❌ 无权限访问:需要{requiredPermission}权限");
}
return hasPermission;
}
}
// ==================== 4. 测试程序 ====================
class Program
{
static void Main(string[] args)
{
UserController api = new UserController();
// 模拟不同权限的用户
string[] adminPermissions = new[] { "Admin", "User" };
string[] userPermissions = new[] { "User" };
string[] guestPermissions = new string[0];
Console.WriteLine("=== 管理员用户访问 ===");
TestAccess(api, "GetAllUsers", adminPermissions);
TestAccess(api, "GetCurrentUser", adminPermissions);
TestAccess(api, "PublicInfo", adminPermissions);
Console.WriteLine("\n=== 普通用户访问 ===");
TestAccess(api, "GetAllUsers", userPermissions);
TestAccess(api, "GetCurrentUser", userPermissions);
TestAccess(api, "PublicInfo", userPermissions);
Console.WriteLine("\n=== 游客访问 ===");
TestAccess(api, "GetAllUsers", guestPermissions);
TestAccess(api, "GetCurrentUser", guestPermissions);
TestAccess(api, "PublicInfo", guestPermissions);
}
/// <summary>
/// 测试用户访问指定方法
/// </summary>
static void TestAccess(UserController api, string methodName, string[] permissions)
{
Console.Write($"访问{methodName}:");
if (PermissionValidator.Validate(api, methodName, permissions))
{
// 反射调用方法(实际项目中可能通过HTTP请求触发)
MethodInfo method = typeof(UserController).GetMethod(methodName);
method?.Invoke(api, null);
}
}
}
}
逐行讲解
1. 自定义权限特性(PermissionAttribute)
csharp
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class PermissionAttribute : Attribute
{
public string RequiredPermission { get; }
public PermissionAttribute(string requiredPermission)
{
RequiredPermission = requiredPermission;
}
}
继承Attribute:所有自定义特性必须直接或间接继承自System.Attribute,这是.NET的约定;
AttributeUsage特性:标记当前特性的使用规则:
AttributeTargets.Method:只能应用于方法(还可指定类、属性等,AttributeTargets.Class | AttributeTargets.Method);
AllowMultiple=false:一个方法只能标记一次该特性(若需多个权限,可设为true并存储权限列表);
Inherited=false:子类方法不会继承父类方法的特性(如父类方法标记了Admin权限,子类重写该方法不会自动继承);
构造函数:特性的构造函数用于接收参数(如权限名称),参数会存储为特性的属性,供运行时读取。
2. API方法标记特性
csharp
[Permission("Admin")]
public void GetAllUsers() { ... }
用[Permission("Admin")]标记方法需要Admin权限,编译器会将该特性的元数据存储到程序集中;
无特性标记的方法(如PublicInfo),验证工具会默认允许访问(可根据业务调整逻辑)。
3. 权限验证工具类(PermissionValidator)
核心逻辑在Validate方法:
获取方法Type:通过controller.GetType()和GetMethod(methodName)获取方法的反射对象MethodInfo;
读取特性:methodInfo.GetCustomAttribute
权限检查:对比用户权限列表和特性中的RequiredPermission,判断是否允许访问。
4. 测试程序
模拟不同权限的用户访问API方法:
管理员:能访问所有方法;
普通用户:无法访问GetAllUsers(需要Admin权限);
游客:只能访问无特性标记的PublicInfo方法。
基础知识拓展
-
特性的本质与分类
本质:特性是存储在程序集元数据中的“标签”,本身不执行任何代码,需要通过反射读取并处理;
分类:
预定义特性:.NET内置的特性,如[Obsolete](标记过时方法)、[Serializable](标记可序列化类)、[DataContract](WCF序列化);
自定义特性:像实例中的PermissionAttribute,根据业务需求自定义元数据标记。 -
特性的常见应用场景
权限验证:如实例中的API权限标记;
序列化控制:[JsonProperty](Newtonsoft.Json)标记JSON字段名,[IgnoreDataMember]标记忽略序列化的属性;
ORM映射:[Table](EF Core)标记数据库表名,[Column]标记列名;
单元测试:[Test](NUnit)标记测试方法,[TestCase]标记测试参数;
AOP拦截:结合AOP框架(如AspectCore、Autofac),用特性标记需要拦截的方法(如日志、事务)。 - 特性vs注释vs配置文件
| 技术 | 作用对象 | 可读性 | 程序可访问性 | 适用场景 |
|---|---|---|---|---|
| 特性 | 代码元素 | 中等(需反射) | 是 | 程序运行时需要读取的元数据 |
| 注释 | 代码(给人看) | 高 | 否 | 代码说明、文档生成 |
| 配置文件 | 应用程序 | 高 | 是 | 动态调整的参数(如数据库连接) |
-
特性的性能考量
反射开销:读取特性需要反射,反射比直接调用慢,但在API权限验证场景中,一次请求只反射一次方法,开销可忽略;
优化方案:缓存反射结果(如将方法→权限的映射缓存到字典中),避免重复反射同一方法。
扩展思考
多权限支持:如何让一个方法需要多个权限(如同时需要Admin和User)?
修改PermissionAttribute的AllowMultiple=true,并在验证时检查用户是否拥有所有所需权限;
示例:[Permission("Admin")][Permission("User")]标记方法,验证时需同时满足两个权限。
全局权限拦截:在ASP.NET Core中,如何用特性实现全局权限拦截?
编写中间件或Action过滤器(IActionFilter),在请求到达控制器前,通过反射读取Action方法上的特性,验证权限;
示例:[PermissionFilter]标记控制器,过滤器在OnActionExecuting中执行权限验证。
特性的高级用法:如何让特性支持参数化配置(如权限过期时间)?
在特性中添加属性(如public DateTime ExpireTime { get; set; }),标记方法时传入参数:[Permission("Admin", ExpireTime = "2025-12-31")];
验证时读取ExpireTime,判断权限是否过期。
总结
特性是.NET中元编程的核心技术,通过给代码添加元数据,实现了“标记-处理”的解耦模式。在API权限场景中,特性让权限规则与业务逻辑分离,统一验证逻辑,减少重复代码。
掌握特性的关键是理解:特性本身不执行代码,只是存储信息,需要反射或框架(如ASP.NET Core过滤器)来处理这些信息。合理使用特性可以让代码更简洁、更易维护,是后端开发必备技能之一。
本站原创,转载请注明出处:










