查看: 17|回复: 0

.NET 集成 Swagger、API 版本控制 、JWT 授权、权限设计

[复制链接]

0

主题

0

回帖

0

积分

积极分子

金币
0
阅读权限
220
精华
0
威望
0
贡献
0
在线时间
0 小时
注册时间
2009-6-29
发表于 2026-4-25 10:43:00 | 显示全部楼层 |阅读模式

一、先安装必需 NuGet 包

 
# API版本控制
Install-Package Microsoft.AspNetCore.Mvc.Versioning
Install-Package Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer

# JWT
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer

# Swagger
Install-Package Swashbuckle.AspNetCore
 

 

二、appsettings.json 配置(JWT + 版本)

 
json
 
{
  "Jwt": {
    "Issuer": "MyServer",        // 签发者
    "Audience": "MyClient",      // 接收者
    "SecretKey": "YourLongSecretKeyHere1234567890", // 密钥越长越安全
    "ExpiresMinutes": 120        // 过期时间
  },
  "ApiVersion": {
    "DefaultVersion": "1.0"
  }
}
 

 

三、Program.cs 完整配置

 
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Reflection;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

// 1. 添加控制器
builder.Services.AddControllers();

// ==============================================
// 2. 配置 API 版本控制
// ==============================================
builder.Services.AddApiVersioning(options =>
{
    options.DefaultApiVersion = new ApiVersion(1, 0);
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.ReportApiVersions = true; // 返回头显示支持的版本
    options.ApiVersionReader = ApiVersionReader.Combine(
        new UrlSegmentApiVersionReader(),        // /api/v1/xx
        new HeaderApiVersionReader("api-version"), // Header: api-version:1.0
        new QueryStringApiVersionReader("v")     // ?v=1.0
    );
});

builder.Services.AddVersionedApiExplorer(options =>
{
    options.GroupNameFormat = "'v'VVV"; // v1, v1.0
    options.SubstituteApiVersionInUrl = true;
});

// ==============================================
// 3. 配置 JWT 授权
// ==============================================
var jwtSection = builder.Configuration.GetSection("Jwt");
var secretKey = Encoding.UTF8.GetBytes(jwtSection["SecretKey"]!);

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = jwtSection["Issuer"],
        ValidAudience = jwtSection["Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(secretKey),
        ClockSkew = TimeSpan.Zero // 不允许时间误差
    };
});

// ==============================================
// 4. 配置 Swagger + 版本 + JWT
// ==============================================
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
    // 加载所有API版本
    var provider = builder.Services.BuildServiceProvider()
        .GetRequiredService<IApiVersionDescriptionProvider>();

    foreach (var description in provider.ApiVersionDescriptions)
    {
        options.SwaggerDoc(description.GroupName, new Microsoft.OpenApi.Models.OpenApiInfo
        {
            Title = $"API {description.GroupName}",
            Version = description.GroupName,
            Description = "带版本 + JWT授权的API文档"
        });
    }

    // Swagger 支持 JWT 认证
    options.AddSecurityDefinition("Bearer", new Microsoft.OpenApi.Models.OpenApiSecurityScheme
    {
        Name = "Authorization",
        Type = Microsoft.OpenApi.SecuritySchemeType.Http,
        Scheme = "Bearer",
        BearerFormat = "JWT",
        In = Microsoft.OpenApi.ParameterLocation.Header,
        Description = "输入格式:Bearer {token}"
    });

    options.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement
    {
        {
            new Microsoft.OpenApi.Models.OpenApiSecurityScheme
            {
                Reference = new Microsoft.OpenApi.Models.OpenApiReference
                {
                    Type = Microsoft.OpenApi.ReferenceType.SecurityScheme,
                    Id = "Bearer"
                }
            },
            Array.Empty<string>()
        }
    });

    // 注释(可选)
    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    options.IncludeXmlComments(xmlPath, true);
});

var app = builder.Build();

// 启用Swagger版本分组
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(options =>
    {
        var provider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>();
        foreach (var description in provider.ApiVersionDescriptions)
        {
            options.SwaggerEndpoint(
                $"/swagger/{description.GroupName}/swagger.json",
                $"API {description.GroupName}"
            );
        }
    });
}

app.UseHttpsRedirection();
app.UseAuthentication(); // 必须在 Authorization 之前
app.UseAuthorization();
app.MapControllers();
app.Run();
 

 

四、JWT 工具类(生成 Token)

 
创建 JwtHelper.cs
 
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace YourNamespace;

public class JwtHelper
{
    private readonly IConfiguration _config;

    public JwtHelper(IConfiguration config)
    {
        _config = config;
    }

    // 生成JWT Token(可传入用户ID、角色、权限)
    public string GenerateToken(string userId, string userName, List<string> roles, List<string> permissions)
    {
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.NameIdentifier, userId),
            new Claim(ClaimTypes.Name, userName)
        };

        // 添加角色
        foreach (var role in roles)
            claims.Add(new Claim(ClaimTypes.Role, role));

        // 添加权限
        foreach (var p in permissions)
            claims.Add(new Claim("Permission", p));

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:SecretKey"]!));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken(
            issuer: _config["Jwt:Issuer"],
            audience: _config["Jwt:Audience"],
            claims: claims,
            expires: DateTime.Now.AddMinutes(Convert.ToDouble(_config["Jwt:ExpiresMinutes"])),
            signingCredentials: creds
        );

        return new JwtSecurityTokenHandler().WriteToken(token);
    }
}
 
在 Program.cs 注册:
 
builder.Services.AddScoped<JwtHelper>();
 

 

五、API 版本控制使用示例

 
// V1 版本
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
public class UserController : ControllerBase
{
    [HttpGet("info")]
    public IActionResult GetV1()
    {
        return Ok("API V1 用户信息");
    }
}

// V2 版本
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("2.0")]
public class UserController : ControllerBase
{
    [HttpGet("info")]
    public IActionResult GetV2()
    {
        return Ok("API V2 用户信息");
    }
}
 

 

六、登录接口(获取 Token)

 
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[AllowAnonymous] // 无需登录
public class AuthController : ControllerBase
{
    private readonly JwtHelper _jwtHelper;

    public AuthController(JwtHelper jwtHelper)
    {
        _jwtHelper = jwtHelper;
    }

    [HttpPost("login")]
    public IActionResult Login(string username, string password)
    {
        // 这里替换成真实数据库校验
        if (username == "admin" && password == "123456")
        {
            var token = _jwtHelper.GenerateToken(
                userId: "1001",
                userName: "admin",
                roles: new List<string> { "Admin" },
                permissions: new List<string> { "User:List", "User:Add", "User:Delete" }
            );

            return Ok(new { Token = token });
        }
        return Unauthorized("账号密码错误");
    }
}
 

 

七、权限设计

 

1. 权限设计思想

 
  • 基于 RBAC:用户 → 角色 → 权限
  • 权限粒度:功能权限字符串,如 User:ListOrder:Create
  • 校验方式:自定义权限过滤器
 

2. 自定义权限注解

 
[AttributeUsage(AttributeTargets.Method)]
public class PermissionAttribute : Attribute
{
    public string Code { get; }

    public PermissionAttribute(string code)
    {
        Code = code;
    }
}
 

3. 权限验证过滤器

 
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace YourNamespace;

public class PermissionFilter : IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        // 跳过匿名接口
        if (context.ActionDescriptor.EndpointMetadata.Any(m => m is AllowAnonymousAttribute))
            return;

        // 获取当前方法需要的权限
        var permission = context.ActionDescriptor.EndpointMetadata
            .OfType<PermissionAttribute>()
            .FirstOrDefault();

        if (permission == null) return;

        // 获取用户拥有的权限
        var userPermissions = context.HttpContext.User.Claims
            .Where(c => c.Type == "Permission")
            .Select(c => c.Value)
            .ToList();

        // 校验
        if (!userPermissions.Contains(permission.Code))
        {
            context.Result = new ForbidResult();
        }
    }
}
 
在 Program.cs 注册全局过滤器:
 
builder.Services.AddMvc(options =>
{
    options.Filters.Add<PermissionFilter>();
});
 

 

八、权限 + 版本 + 授权的 API 示例

 
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[Authorize] // 需要登录
public class UserController : ControllerBase
{
    // 需要权限:User:List
    [HttpGet]
    [Permission("User:List")]
    public IActionResult GetList()
    {
        return Ok("用户列表");
    }

    // 需要权限:User:Add
    [HttpPost]
    [Permission("User:Add")]
    public IActionResult Add()
    {
        return Ok("添加成功");
    }

    // 需要权限:User:Delete
    [HttpDelete("{id}")]
    [Permission("User:Delete")]
    public IActionResult Delete(int id)
    {
        return Ok("删除成功");
    }
}
 

 

九、Swagger 使用效果

 
运行后打开:https://localhost:5001/swagger
 
  • 自动分版本:v1 /v2
  • 右上角有 Authorize 按钮
  • 输入:Bearer 你的token
  • 即可访问需要登录 / 权限的接口
 

 

十、总结

 
  1. 调用 /api/v1/Auth/login 获取 Token
  2. Swagger 点击 Authorize 输入 Bearer token
  3. 访问不同版本 API
  4. 系统自动校验:登录 → 角色 → 权限
  5. 权限不满足直接返回 403


来源:https://www.cnblogs.com/chuansheng/p/19913597
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

相关侵权、举报、投诉及建议等,请发 E-mail:qiongdian@foxmail.com

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部