根据 scope 策略设置受保护api资源 访问权限
1 添加 apiscope
2 添加 apiresource
添加api名为“client_credentials_apis”的apiresource,并把作用域“client_credentials_apis.IdentityUserController.scope”和“client_credentials_apis.WeatherForecastController.scope”添加到该apiresource中
3 在 ids 添加客户端
添加客户端,添加客户端密钥,设置授权类型(比如 客户端凭证方式),并至少设置一个 自定义的 apiscope(如添加 client_credentials_apis.WeatherForecastController.scope);token类型默认为 jwt。
4 客户端获取 token并使用 该token访问api资源
接下来给客户端添加 client_credentials_apis.IdentityUserController.scope ,重新访问结果如下:
再给客户端添加 client_credentials_apis.WeatherForecastController.scope,重新访问结果如下
客户端添加作用域如下图:
5 相关代码
5.1 api资源端代码如下:
配置如下:
namespace Resources_Https { public class Config { /// <summary> /// identity server 地址 /// </summary> public const string IdentityServerUri = "https://localhost:44310"; /// <summary> /// reference tokens 的 clientId /// </summary> public const string ApiName = "client_credentials_apis"; /// <summary> /// reference tokens 的 clientSecret /// </summary> public const string ApiSecret = "123456"; } }
startup.cs 代码如下:
using IdentityModel.AspNetCore.OAuth2Introspection; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.OpenApi.Models; namespace Resources_Https { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // nuget 安装 Microsoft.AspNetCore.Authentication.JwtBearer // jwt tokens services.AddAuthentication(OAuth2IntrospectionDefaults.AuthenticationScheme) .AddJwtBearer("Bearer", options => { options.Authority = Config.IdentityServerUri; // 设置 https options.RequireHttpsMetadata = true; options.Audience = Config.ApiName; // 支持 jwt 和 reference 两种 token // if token does not contain a dot, it is a reference token options.ForwardDefaultSelector = context => "Introspection"; }); // reference tokens services.AddAuthentication("Introspection") .AddOAuth2Introspection("Introspection", options => { options.Authority = Config.IdentityServerUri; // this maps to the API resource name and secret options.ClientId = Config.ApiName; // api 名 options.ClientSecret = Config.ApiSecret; // 配置的 api 秘钥 }); // 策略授权 services.AddAuthorization(options => { // client allowedscope 包含 client_credentials_apis.WeatherForecastController.scope 才能访问 options.AddPolicy("WeatherForecastController", policy => policy.RequireScope("client_credentials_apis.WeatherForecastController.scope") ); options.AddPolicy("IdentityUserController", policy => policy.RequireScope("client_credentials_apis.IdentityUserController.scope") ); }); services.AddControllers(); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "Resources_Https", Version = "v1" }); }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Resources_Https v1")); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } } }
TestController代码:
[ApiController] [Route("[controller]/[action]")] [Authorize] public class TestController : ControllerBase { public IActionResult Ping() { return new JsonResult(new { code = 0, msg = "TestController pong" }); } }
IdentityUserController 代码:
[ApiController] [Route("[controller]/[action]")] [Authorize(Policy = "IdentityUserController")] public class IdentityUserController : ControllerBase { [HttpGet] public IActionResult Ping() { return new JsonResult(new { code = 0, msg = "IdentityUserController pong" }); } [HttpGet] public IActionResult GetClaims() { var claims = User.Claims.ToList(); var id = User.Identity as ClaimsIdentity; var claim = id.FindFirst(JwtClaimTypes.Subject); return new JsonResult(claims); } }
WeatherForecastController 代码:
[ApiController] [Route("[controller]/[action]")] [Authorize(Policy = "WeatherForecastController")] public class WeatherForecastController : ControllerBase { [HttpGet] public IActionResult Ping() { return new JsonResult(new { code = 0, msg = "WeatherForecastController pong" }); } }
5.2 客户端代码
配置代码:
public class Config { /// <summary> /// identity server 地址 /// </summary> public const string IdentityServerUri = "https://localhost:44310"; /// <summary> /// 受保护的api地址 /// </summary> public const string ResourceUri = "https://localhost:6001"; public const string ClientId = "ClientCredentials_1_ClientId"; public const string ClientSecret = "511536EF-F270-4058-80CA-1C89C192F69A"; public const string GrantType = "client_credentials"; }
获取token和访问资源代码:
/// <summary> /// 使用 HttpClient 通过客户端凭证方式获取token与资源 /// 适用场景:一般用于服务端应用与服务端应用交互 /// 请求token链接格式:{idsServer}/connect/token?client_id={}&client_secret={}&grant_type=client_credentials /// </summary> public class HttpClient_ClientCredentials { public static void Run() { // 先获取 access token var token_res = string.Empty; string postData = $"client_id={Config.ClientId}&client_secret={Config.ClientSecret}&grant_type={Config.GrantType}"; using (var httpClient = new HttpClient()) { using (HttpContent httpContent = new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes(postData)))) { httpContent.Headers.Add("Content-Type", "application/x-www-form-urlencoded"); token_res = httpClient.PostAsync($"{Config.IdentityServerUri}/connect/token", httpContent).Result.Content.ReadAsStringAsync().Result; } } // 使用 token 访问资源 if (!string.IsNullOrEmpty(token_res)){ using(HttpClient getClient = new HttpClient()) { getClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + JObject.Parse(token_res)["access_token"].ToString()); // 有token就能访问 var apiRes1 = getClient.GetStringAsync($"{Config.ResourceUri}/Test/Ping").Result; // 有token就能访问且 client allowedscope 包含 client_credentials_apis.IdentityUserController.scope 才能访问 var apiRes2 = getClient.GetStringAsync($"{Config.ResourceUri}/IdentityUser/Ping").Result; // 有token就能访问且 client allowedscope 包含 client_credentials_apis.WeatherForecastController.scope 才能访问 var res_res3 = getClient.GetStringAsync($"{Config.ResourceUri}/WeatherForecast/Ping").Result; } } } }
参考资源
https://blog.csdn.net/ma_jiang/article/details/107120049