12.1 身份认证与授权 (JWT)
JWT 认证流程:用户登录→服务器生成 JWT 令牌→客户端存储令牌→后续请求携带令牌→服务器验证令牌。
实战案例:JWT 认证与授权:
- 安装 NuGet 包:Microsoft.AspNetCore.Authentication.JwtBearer。
- 配置 JWT(Program.cs):
var jwtSettings = builder.Configuration.GetSection("JwtSettings").Get<JwtSettings>();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true, // 验证发行人
ValidIssuer = jwtSettings.Issuer,
ValidateAudience = true, // 验证受众
ValidAudience = jwtSettings.Audience,
ValidateLifetime = true, // 验证过期时间
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey)), // 签名密钥
ClockSkew = TimeSpan.Zero // 禁用时钟偏差(避免令牌提前失效)
};
});
// 注册授权服务
builder.Services.AddAuthorization();
- 添加 JWT 配置(appsettings.json):
"JwtSettings": {
"Issuer": "MyWebApi",
"Audience": "WebApiClients",
"SecretKey": "YourSuperSecretKey1234567890!@#$%^&*()", // 生产环境需用长随机密钥
"ExpiresInMinutes": 60 // 令牌有效期60分钟
}
- 登录接口(生成 JWT):
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
private readonly JwtSettings _jwtSettings;
private readonly IUserService _userService;
public AuthController(IOptions<JwtSettings> jwtSettings, IUserService userService)
{
_jwtSettings = jwtSettings.Value;
_userService = userService;
}
[HttpPost("login")]
public async Task<IActionResult> Login([FromBody] LoginRequest request)
{
// 1. 验证用户(实际项目需加密验证密码)
var user = await _userService.ValidateUserAsync(request.UserName, request.Password);
if (user == null)
{
return Unauthorized(new { Message = "用户名或密码错误" });
}
// 2. 生成JWT令牌
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.SecretKey));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
// 3. 设置令牌声明(包含用户ID、角色等信息)
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.Name),
new Claim(ClaimTypes.Role, user.Role) // 角色声明(用于授权)
};
// 4. 创建令牌
var token = new JwtSecurityToken(
issuer: _jwtSettings.Issuer,
audience: _jwtSettings.Audience,
claims: claims,
expires: DateTime.Now.AddMinutes(_jwtSettings.ExpiresInMinutes),
signingCredentials: credentials);
// 5. 返回令牌
var tokenString = new JwtSecurityTokenHandler().WriteToken(token);
return Ok(new { Token = tokenString, ExpiresIn = _jwtSettings.ExpiresInMinutes * 60 });
}
}
public class LoginRequest
{
[Required]
public string UserName { get; set; }
[Required]
public string Password { get; set; }
}
- 添加授权(控制器 / 方法):csharp
// 全局启用认证(Program.cs)
app.UseAuthentication();
app.UseAuthorization();
// 1. 控制器级授权(所有方法需登录)
[Authorize]
[ApiController]
[Route("api/v1/users")]
public class UsersController : ControllerBase { /* ... */ }
// 2. 方法级授权(仅管理员可访问)
[HttpDelete("{id}")]
[Authorize(Roles = "Admin")] // 仅Role=Admin的用户可删除
public async Task<IActionResult> DeleteUser(int id) { /* ... */ }
- 测试:用 Postman 先调用/api/auth/login获取 Token,再在请求头添加Authorization: Bearer {Token}访问需要授权的接口。
12.2 缓存策略与响应缓存
- 响应缓存:通过[ResponseCache]特性缓存 API 响应,减少数据库查询,提升性能。
- 实战案例:缓存用户列表:
[ApiController]
[Route("api/v1/users")]
public class UsersController : ControllerBase
{
// 缓存60秒,按查询参数page和pageSize区分缓存
[HttpGet]
[ResponseCache(Duration = 60, VaryByQueryKeys = new[] { "page", "pageSize" })]
public async Task<ActionResult<PagedResult<UserDto>>> GetUsers(
[FromQuery] int page = 1,
[FromQuery] int pageSize = 10)
{
// 数据库查询逻辑(缓存期间仅执行一次)
var total = await _dbContext.Users.CountAsync();
var users = await _dbContext.Users
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(u => new UserDto { Id = u.Id, Name = u.Name, Email = u.Email })
.ToListAsync();
return Ok(new PagedResult<UserDto> { Total = total, Page = page, PageSize = pageSize, Data = users });
}
}
// 配置缓存服务(Program.cs)
builder.Services.AddResponseCaching();
app.UseResponseCaching(); // 注册响应缓存中间件
12.3 速率限制与 API 防护
速率限制:防止恶意请求(如 DDOS),限制单位时间内的请求次数,使用AspNetCore.RateLimit包。
实战案例:IP 级速率限制:
- 安装 NuGet 包:AspNetCore.RateLimit。
- 配置速率限制(Program.cs):
// 1. 注册速率限制服务
builder.Services.AddMemoryCache(); // 用内存缓存存储速率限制数据(生产环境可用Redis)
builder.Services.Configure<IpRateLimitOptions>(options =>
{
options.EnableEndpointRateLimiting = true;
options.StackBlockedRequests = false;
options.HttpStatusCode = 429; // 超出限制返回429 Too Many Requests
options.RealIpHeader = "X-Real-IP";
options.GeneralRules = new List<RateLimitRule>
{
// 规则1:所有接口,1分钟内最多10次请求
new RateLimitRule
{
Endpoint = "*",
Period = "1m",
Limit = 10
},
// 规则2:登录接口,1分钟内最多5次请求(防止暴力破解)
new RateLimitRule
{
Endpoint = "POST:/api/auth/login",
Period = "1m",
Limit = 5
}
};
});
builder.Services.AddInMemoryRateLimiting();
builder.Services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
// 2. 注册速率限制中间件(需在认证前)
app.UseIpRateLimiting();
12.4 文档生成与 Swagger/OpenAPI
- Swagger:自动生成 API 文档,支持在线调试,使用Swashbuckle.AspNetCore包。
- 实战案例:配置 Swagger:
- 安装 NuGet 包:Swashbuckle.AspNetCore。
- 配置 Swagger(Program.cs):
builder.Services.AddSwaggerGen(options =>
{
// 1. 设置文档信息
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "用户管理API",
Version = "v1",
Description = "基于ASP.NET Core的用户管理Web API文档"
});
// 2. 启用JWT认证支持(添加Authorize按钮)
var securityScheme = new OpenApiSecurityScheme
{
Name = "Authorization",
Type = SecuritySchemeType.Http,
Scheme = "bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Description = "请输入JWT令牌:Bearer {Token}"
};
options.AddSecurityDefinition("Bearer", securityScheme);
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
Array.Empty<string>()
}
});
// 3. 加载XML注释(显示方法和模型的注释)
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
options.IncludeXmlComments(xmlPath);
});
// 3. 启用Swagger中间件(开发环境)
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "用户管理API v1");
options.RoutePrefix = "swagger"; // 文档路径:/swagger
});
}
- 添加 XML 注释:项目属性→生成→勾选 “生成 XML 文档文件”,然后在控制器和模型中添加注释:csharp
/// <summary>
/// 用户管理控制器
/// </summary>
[ApiController]
[Route("api/v1/[controller]")]
public class UsersController : ControllerBase
{
/// <summary>
/// 查询单个用户
/// </summary>
/// <param>用户ID</param>
/// <returns>用户信息</returns>
[HttpGet("{id}")]
public async Task<ActionResult<UserDto>> GetUserById(int id) { /* ... */ }
}
- 访问文档:运行项目后访问/swagger,可查看文档并在线调试 API(点击 “Authorize” 输入 JWT 令牌)。
12.5 gRPC 服务与高性能通信
- gRPC:基于 HTTP/2 的高性能 RPC 框架,使用 Protocol Buffers(Protobuf)序列化,适合服务间通信。
- 实战案例:创建 gRPC 服务:
- 创建 gRPC 服务项目:Visual Studio 新建 “gRPC 服务” 项目,或用 CLI:dotnet new grpc -n UserGrpcService。
- 定义 Protobuf 协议(Protos/user.proto):
syntax = "proto3";
package user;
// 定义用户服务
service UserService {
// 方法1:获取用户信息
rpc GetUser (GetUserRequest) returns (GetUserResponse);
// 方法2:批量获取用户
rpc GetUserList (GetUserListRequest) returns (GetUserListResponse);
}
// 请求模型:获取单个用户
message GetUserRequest {
int32 user_id = 1; // Protobuf字段编号(不可重复)
}
// 响应模型:单个用户
message GetUserResponse {
int32 id = 1;
string name = 2;
string email = 3;
int32 age = 4;
}
// 请求模型:批量获取用户
message GetUserListRequest {
int32 page = 1;
int32 page_size = 2;
}
// 响应模型:用户列表
message GetUserListResponse {
repeated GetUserResponse users = 1; // repeated表明数组
int32 total = 2;
}
- 实现 gRPC 服务(Services/UserService.cs):
public class UserService : Protos.UserService.UserServiceBase
{
private readonly AppDbContext _dbContext;
public UserService(AppDbContext dbContext)
{
_dbContext = dbContext;
}
// 实现GetUser方法
public override async Task<Protos.GetUserResponse> GetUser(
Protos.GetUserRequest request,
ServerCallContext context)
{
var user = await _dbContext.Users.FindAsync(request.UserId);
if (user == null)
{
throw new RpcException(new Status(StatusCode.NotFound, "用户不存在"));
}
return new Protos.GetUserResponse
{
Id = user.Id,
Name = user.Name,
Email = user.Email,
Age = user.Age
};
}
// 实现GetUserList方法
public override async Task<Protos.GetUserListResponse> GetUserList(
Protos.GetUserListRequest request,
ServerCallContext context)
{
var total = await _dbContext.Users.CountAsync();
var users = await _dbContext.Users
.Skip((request.Page - 1) * request.PageSize)
.Take(request.PageSize)
.ToListAsync();
var response = new Protos.GetUserListResponse { Total = total };
foreach (var user in users)
{
response.Users.Add(new Protos.GetUserResponse
{
Id = user.Id,
Name = user.Name,
Email = user.Email,
Age = user.Age
});
}
return response;
}
}
- 配置 gRPC 服务(Program.cs):
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
var app = builder.Build();
app.MapGrpcService<UserService>();
app.MapGet("/", () => "gRPC服务运行中,请使用gRPC客户端调用");
app.Run();
- 创建 gRPC 客户端(调用服务):
// 客户端项目安装NuGet包:Grpc.Net.Client、Google.Protobuf、Grpc.Tools
public class GrpcClientDemo
{
public static async Task CallUserGrpcService()
{
// 1. 创建gRPC通道(生产环境用HTTPS)
var channel = GrpcChannel.ForAddress("http://localhost:5000");
var client = new Protos.UserService.UserServiceClient(channel);
// 2. 调用GetUser方法
try
{
var getUserResponse = await client.GetUserAsync(new Protos.GetUserRequest { UserId = 1 });
Console.WriteLine($"用户信息:ID={getUserResponse.Id}, 姓名={getUserResponse.Name}");
}
catch (RpcException ex)
{
Console.WriteLine($"调用失败:{ex.Status.Detail}");
}
// 3. 调用GetUserList方法
var getUserListResponse = await client.GetUserListAsync(new Protos.GetUserListRequest
{ Page = 1, PageSize = 10 });
Console.WriteLine($"总用户数:{getUserListResponse.Total}");
foreach (var user in getUserListResponse.Users)
{
Console.WriteLine($"ID={user.Id}, 姓名={user.Name}");
}
}
}
© 版权声明
文章版权归作者所有,未经允许请勿转载。
相关文章
暂无评论...