【发布时间】:2020-12-30 07:48:03
【问题描述】:
我很难找到有关此主题的相关信息:.NetCore Web API 中的 JwtBearerAuthentication。该令牌是由 IdentityServer 发出并通过前端客户端应用程序发送的 OIDC id 令牌。令牌被正确接收和验证,产生一个经过身份验证的 ClaimsPrincipal 有 15 个声明,但是当请求到达应用程序时,该主体不存在于 HttpContext 中。存在一个未经身份验证的匿名用户,而不是验证令牌时存在的用户。
这个 api 是由其作者(不是我)构思的,用于使用 OIDC cookie 身份验证,但我正在尝试将其转换为使用 JWT 不记名身份验证。
据我所知,根据我能找到的所有示例,我所做的一切都是正确的,而且我没有想法。任何帮助表示赞赏。
我已经在下面发布了整个 Startup.cs 文件,抱歉所有代码,但您应该能够看到所有相关内容...
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Autofac;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Autofac.Configuration;
using Autofac.Extensions.DependencyInjection;
using CAS.Authorization.Api.Configuration;
using CAS.Authorization.Api.Defaults.Configuration;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Swashbuckle.AspNetCore.Swagger;
namespace CAS.Authorization.Api
{
[ExcludeFromCodeCoverage]
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 IServiceProvider ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddAuthentication(options =>
{
//options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
//options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
// options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
//.AddCookie()
.AddJwtBearer(options =>
{
options.Authority = "http://localhost:40800";
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
ValidateIssuer = false,
ValidIssuer = "http://localhost:40800",
ValidateAudience = false,
ValidAudience = "http://localhost:22426"
};
options.Events = new JwtBearerEvents()
{
OnTokenValidated = async context =>
{
//This does not work, however the context.Principal IS authenticated
//and DOES have claims (15 of them).
context.HttpContext.User = context.Principal;
}
};
});
//.AddOpenIdConnect(options =>
//{
// options.SignInScheme = "Cookies";
// options.Authority = "http://localhost:40800";
// options.ClientId = "defaultClientId";
// options.SignInScheme = "Cookies";
// options.RequireHttpsMetadata = false;
// options.ResponseType = "code";
// options.Scope.Add("profile");
// options.GetClaimsFromUserInfoEndpoint = true;
// options.SaveTokens = true;
// options.ClaimActions.MapAllExcept("iss", "nbf", "exp", "aud", "nonce", "iat", "c_hash");
//});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddHttpContextAccessor();
services.AddAuthorization(PolicyConfiguration.SetupPolicies);
services.AddSingleton<IAuthorizationHandler, AccessPolicyHandler>();
services.AddTransient<IActionContextAccessor, ActionContextAccessor>();
//Api Versioning
//services.AddApiVersioning(o =>
//{
// o.ReportApiVersions = true;
// o.ApiVersionReader = new UrlSegmentApiVersionReader();
//});
services.Configure<BusSettings>(Configuration.GetSection("BusSettings"));
//Swagger
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "CAS Authorization API", Version = "v1" });
//Locate the XML file being generated by ASP.NET...
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.XML";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
//... and tell Swagger to use those XML comments.
c.IncludeXmlComments(xmlPath);
});
// Add Autofac
// ConfigurationModule accepts JSON configuration for other modules/components to register,
// instead of having references here to those modules, directly.
// If we ever want to remove those direct references, this should still work.
// config.AddJsonFile comes from Microsoft.Extensions.Configuration.Json
var config = new ConfigurationBuilder();
config.AddJsonFile("modules.json");
// Register the ConfigurationModule with Autofac.
var module = new ConfigurationModule(config.Build());
var builder = new ContainerBuilder();
builder.RegisterAssemblyModules();
builder.RegisterModule(module);
builder.Populate(services);
var container = builder.Build();
return new AutofacServiceProvider(container);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseCookiePolicy();
app.UseAuthentication();
app.UseHttpsRedirection();
app.UseMvc();
//Swagger UI
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "CAS Authorization API - v1");
c.RoutePrefix = string.Empty;
});
}
}
}
在下面的 GetClaimsByOrganization 方法中添加了尝试访问用户的示例:
public class UserIdentityService : IUserIdentityService
{
private HttpContext _context;
public UserIdentityService(IHttpContextAccessor contextAccessor)
{
_context = contextAccessor?.HttpContext ?? throw new ArgumentNullException();
}
public string UserName => _context.User.FindFirst(ClaimTypes.Name)?.Value;
public Guid Sub => Guid.Parse(_context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value);
public Claim[] GetPermissionClaims(string permissionName, string resourceName)
{
if (string.IsNullOrWhiteSpace(permissionName)) throw new ArgumentException("Invalid permission name");
if (string.IsNullOrWhiteSpace(resourceName)) throw new ArgumentException("Invalid resource name");
return _context.User?.Claims?.Where(c => c.Type.Contains("ORG:") && c.Value == resourceName.ToUpper() + permissionName.ToUpper()).ToArray();
}
public Claim[] GetClaimsByOrganization(string orgName)
{
if (string.IsNullOrWhiteSpace(orgName)) throw new ArgumentException("Invalid organization name");
return _context.User?.Claims?.Where(c => c.Type == "ORG:" + orgName.ToUpper()).ToArray();
}
}
【问题讨论】:
-
请显示您尝试访问用户对象的位置
-
谢谢,我编辑了帖子以添加尝试访问用户的示例。
-
检查这两个链接 startup.cs 和 controller with authorize attribute ,我认为您不必在 Startup 类中设置 User ,.Net核心应该这样做,但是您的 UserIdentityService 必须从控制器调用使用 Authorize 属性,这可能会有所帮助
-
感谢您查看并分享这些示例。我知道不必在启动中设置用户,但这样做确实允许我打破它并看到 context.Principal 确实具有正确的值。这表明令牌已被接收和验证,并且所有预期的声明都存在。但是稍后访问时,该主体不是 HttpContext 中的主体。 Authorize 属性在所涉及的控制器上,这没有区别。
标签: c# asp.net-core authentication bearer-token jwt-auth