OpenIddict身份验证与授权之: 二. 2(密码模式)资源服务器

密码模式(最简化示例)

为了简化步骤便于理解, 在密码模式下,我只生成两个项目,一个是验证服务器,一个是资源服务器,客户端用postman或者自带的swagger, 详情请看系列文章,此文章为密码模式中的资源服务器篇

一. Nuget

<ItemGroup>
  <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.31" />
  <PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
</ItemGroup>

二. 配置

1.授权

1.1 未加密Jwt

 builder.Services.AddAuthentication(options =>
 {
     options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
     options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
 })
.AddJwtBearer(options =>
{
    options.Authority = "https://localhost:7097/";
    options.Audience = "resource_api_1";
    options.RequireHttpsMetadata = false; // 仅限开发环境
});

1.2 加密Jwt

 builder.Services.AddAuthentication(options =>
 {
     options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
     options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
 })
.AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        // 配置解密密钥
        TokenDecryptionKey = new X509SecurityKey(new X509Certificate2(
             builder.Configuration.GetValue<string>("Certificate:Path"),
             builder.Configuration.GetValue<string>("Certificate:PassWord"))
         ),
        // 配置证书的公钥
        IssuerSigningKey = new X509SecurityKey(new X509Certificate2(
            builder.Configuration.GetValue<string>("Certificate:Path"), 
            builder.Configuration.GetValue<string>("Certificate:PassWord"))
        ),
        // 验证令牌发行者
        ValidateIssuer = true,
        ValidIssuer = "https://localhost:7097/",
        // 验证令牌受众
        ValidateAudience = true,
        ValidAudience = "resource_api_1",
        // 验证令牌签名
        ValidateIssuerSigningKey = true,
        // 验证令牌有效期
        ValidateLifetime = true,
        // 如果需要时钟偏差,可以设置ClockSkew
        // ClockSkew = TimeSpan.FromMinutes(5)
    };
    // 是否要求HTTPS
    options.RequireHttpsMetadata = false; // 仅限开发环境
});

2.身份验证(根据自己的控制器中的角色来添加)

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("Admin", policy =>
    {
        policy.RequireRole("Admin");
    });
    options.AddPolicy("User", policy =>
    {
        policy.RequireRole("User");
    });
});

3.路由相关

app.UseSwagger();
app.UseSwaggerUI();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

三. 控制器

以下是示例,示例四种情况

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    private readonly ILogger<WeatherForecastController> _logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger)
    {
        _logger = logger;
    }

    //无需授权
    [HttpGet("GetWeatherForecastV1")]
    public IEnumerable<WeatherForecast> GetV1()
    {
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
    }

    //通用授权
    [Authorize]
    [HttpGet("GetWeatherForecastV2")]
    public IEnumerable<WeatherForecast> GetV2()
    {
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
    }

    //特定角色授权
    [Authorize("Admin")]
    [HttpGet("GetWeatherForecastV3")]
    public IEnumerable<WeatherForecast> GetV3()
    {
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
    }

    //特定角色授权
    [Authorize("User")]
    [HttpGet("GetWeatherForecastV4")]
    public IEnumerable<WeatherForecast> GetV4()
    {
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
    }

}

appsettings

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }, 
  },  
  "Certificate": {
    "Path": "Certificate/SelfSignedCertificate.pfx",
    "PassWord": "12345678" 
  },
  "AllowedHosts": "*"
}