P. *_*uhn 5 c# openid x509certificate jwt asp.net-core
我目前正在努力转换由 Angular 9 前端与 Asp.Net MVC/Web API/Identity 后端通信组成的现有应用程序。我正在将 .Net 转换为 .Net Core 2.2。后端同时使用cookie和JWT进行身份验证和授权。我正在转向 OpenId Dict/Identity 和 OpenId Connect。值得庆幸的是,我创建了一个独立的项目来测试解决方案,因为它已经过试用并且有很多错误。不幸的是,我对 OpenId Dict/Connect 缺乏了解。
\n\n我需要帮助创建有效的不记名令牌。产生经过验证的用户的令牌。
\n\n众所周知的值如下:
\n\n{\n "issuer": "https://localhost:44324/",\n "token_endpoint": "https://localhost:44324/connect/token",\n "end_session_endpoint": "https://localhost:44324/connect/logout",\n "userinfo_endpoint": "https://localhost:44324/api/userinfo",\n "jwks_uri": "https://localhost:44324/.well-known/jwks",\n "grant_types_supported": [\n "password"\n ],\n "scopes_supported": [\n "openid",\n "email",\n "profile",\n "roles"\n ],\n "claims_supported": [\n "aud",\n "exp",\n "iat",\n "iss",\n "jti",\n "sub"\n ],\n "id_token_signing_alg_values_supported": [\n "RS256"\n ],\n "subject_types_supported": [\n "public"\n ],\n "token_endpoint_auth_methods_supported": [\n "client_secret_basic",\n "client_secret_post"\n ],\n "claims_parameter_supported": false,\n "request_parameter_supported": false,\n "request_uri_parameter_supported": false\n}\nRun Code Online (Sandbox Code Playgroud)\n\n在 Postman 中,我通过传递以下内容生成令牌:
\n\nKEY VALUE\ngrant_type password\nusername Phil\npassword P@ssw0rd\nclient_id incidentservices\nscope openid profile email roles\nRun Code Online (Sandbox Code Playgroud)\n\n它生成以下内容:
\n\n{\n "token_type": "Bearer",\n "access_token": "CfDJ8ISU_Npa_VFIjdjKglRsdyVxwkfPoTyt7Bfx9UPtjrl57djzvm9VDvgNwFbJj-O_op4Mjl2AyecK0bBgb1o3B5p2G3TNdpXyPyQocLz-4zz2lROBAv-M8XsDvaJHEXE234YVqcQcju2Rp80MS7cVhBIrQVdaA94CgF_DE662o-i5wMrod2XACzUDdqDMgLJe9whr5RsIdsAmdCMHoK_yZHEKg68SgBJiSSXok8b35FRwDu1KEDY6cPht0FjBO02XFdsSnRJksBunx6eyif4vReh7-Q09BrQ0F1tk2nnZ3RxB2ttDKdpiZmcgQ3UAVWwxo1psAEkV6yRcroG1cKWM530APP2FLqa52wVzYr-AsESGEIiMKNaypK6Z7LWMnjV3Cu0F0lfxFJLPAO1A2uA2wtDphhvwNRBQafzbFYyEnMfFEQ8NkQ9nOSkiUQ2EbPB34qmiU2IEZijFJT1h5ZvbovCGP8qzslv6O2QUfJkDO0RlymqJNQZeoNPI4rwCyIbUZ9kP3rdoiOcrBjbQdKd5BiCAVQ4GHXOwYj2Oq61foPZByK0ek0imna22yybPYdMsUawHv12WY4Y57DIsFx4cLl8n2d0rjiE20a7rhNjjlzmofsN_jfN8e1JBnMBq0YgDkjASr2t-JcT-EQME16dQx2nVNqnZt95EYfcJxuMugR4pDZ18HJNnpRqx-PnhZapUryVqFMfPHuEEVewX0qpYPPlK6z-DMmGLxynukCC6aO1O70D_FgdemlmeWYViIXa11NHQscBobgfHAdYyA8NCKbVhUyK5R_hN7Ult5vMxVLmWAD25q1_DVwR-7gX7fZWwLuTcOJCVCZeZ9pldxrclL6zNJdHuh4B9_pGq_cN7EomF9Pyq0bM74-LPhBaKfYpVBnNIN6133jjXGFa1cOiS0fvJ3n54LEXXjsGM1_jAV7kOfT0ORwJOz_chIsE7fMgUTNZtUztjAlc-rkoQcPADBgTNs_fFhPM5857qMOBbMwL-",\n "expires_in": 3600,\n "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjgyQ0Q2NjdCOUQxRjNENjY0OTVBMENENTBCNTZCNkZCMTk5MjlDNjIiLCJ0eXAiOiJKV1QifQ.eyJzdWIiOiI3MTM0NkQ2Mi05QkE1LTRCNkQtOUVDQS03NTU1NzRENjI4RDgiLCJ0b2tlbl91c2FnZSI6ImlkX3Rva2VuIiwianRpIjoiOTAxNWIzYmEtMjEwYS00MzA5LThhZjgtNmRmZmU2YTJhNThmIiwiYXVkIjoiaW5jaWRlbnRzZXJ2aWNlcyIsImF0X2hhc2giOiJaRUtWZk9FNjF5SFdVdVVQeUQ1MXRnIiwiYXpwIjoiaW5jaWRlbnRzZXJ2aWNlcyIsIm5iZiI6MTU4MTk3MjI3NiwiZXhwIjoxNTgxOTczNDc2LCJpYXQiOjE1ODE5NzIyNzYsImlzcyI6Imh0dHBzOi8vbG9jYWxob3N0OjQ0MzI0LyJ9.sr48DAD2FWfPasKEioAFPZsXLEaRaubft4IIqj8uIG4dUfSFc59Je_q0FbwwA_HmHYVPnxga5yy_aBFTyAdTDyP1-2fTHdtnYau65_M5TbIupMETR6lLOB40Q51P2Nah83uOc6d_DAWiRQyI7q7AG3gRYewSU8QYoVDeAc8HKHIlnsp-HDSKlqcQScl25OqH7EcF-kM9zJChZFHroxc7kduYJyYpWad9081oqnXCyAkJK_R2g_EZKszBdvPTj4G3Wv29221lwLfNn1Dt7GTPkxaCaitWzXd708GR3xLAAwL4t6ENJz_CvtiPjS5lIPTjRlx8UHm9CztWdaFFgU-kQA"\n}\nRun Code Online (Sandbox Code Playgroud)\n\n我使用上面的\xe2\x80\x98access token\xe2\x80\x99和以下标头调用测试 Web API :
\n\nKEY VALUE\nAuthorization Bearer CfDJ8ISU_Npa_VFIjdjKglRsdyVxwkfPoTyt7Bfx9UPtjrl57djzvm9VDvgNwFbJj-O_op4Mjl2AyecK0bBgb1o3B5p2G3TNdpXyPyQocLz-4zz2lROBAv-M8XsDvaJHEXE234YVqcQcju2Rp80MS7cVhBIrQVdaA94CgF_DE662o-i5wMrod2XACzUDdqDMgLJe9whr5RsIdsAmdCMHoK_yZHEKg68SgBJiSSXok8b35FRwDu1KEDY6cPht0FjBO02XFdsSnRJksBunx6eyif4vReh7-Q09BrQ0F1tk2nnZ3RxB2ttDKdpiZmcgQ3UAVWwxo1psAEkV6yRcroG1cKWM530APP2FLqa52wVzYr-AsESGEIiMKNaypK6Z7LWMnjV3Cu0F0lfxFJLPAO1A2uA2wtDphhvwNRBQafzbFYyEnMfFEQ8NkQ9nOSkiUQ2EbPB34qmiU2IEZijFJT1h5ZvbovCGP8qzslv6O2QUfJkDO0RlymqJNQZeoNPI4rwCyIbUZ9kP3rdoiOcrBjbQdKd5BiCAVQ4GHXOwYj2Oq61foPZByK0ek0imna22yybPYdMsUawHv12WY4Y57DIsFx4cLl8n2d0rjiE20a7rhNjjlzmofsN_jfN8e1JBnMBq0YgDkjASr2t-JcT-EQME16dQx2nVNqnZt95EYfcJxuMugR4pDZ18HJNnpRqx-PnhZapUryVqFMfPHuEEVewX0qpYPPlK6z-DMmGLxynukCC6aO1O70D_FgdemlmeWYViIXa11NHQscBobgfHAdYyA8NCKbVhUyK5R_hN7Ult5vMxVLmWAD25q1_DVwR-7gX7fZWwLuTcOJCVCZeZ9pldxrclL6zNJdHuh4B9_pGq_cN7EomF9Pyq0bM74-LPhBaKfYpVBnNIN6133jjXGFa1cOiS0fvJ3n54LEXXjsGM1_jAV7kOfT0ORwJOz_chIsE7fMgUTNZtUztjAlc-rkoQcPADBgTNs_fFhPM5857qMOBbMwL-\nRun Code Online (Sandbox Code Playgroud)\n\n并使用断点,我检查User.Identity和IsAuthenticated为 false 且Name为 null。\n我正在使用我生成的 WebApp2-TestAuth.pfx 证书。\n这是启动方法,但我将其内容放在一个单独的静态类。
\n\n public class Startup\n {\n public IConfiguration Configuration { get; }\n public Startup(IConfiguration configuration)\n {\n Configuration = configuration;\n }\n //\n public void ConfigureServices(IServiceCollection services)\n {\n //\n AuthSettings _authSettings = new AuthSettings();\n _authSettings = Options.Create<AuthSettings>(\n Configuration.GetSection("AuthSettings").Get<AuthSettings>()).Value;\n services.AddSingleton<AuthSettings>(_authSettings);\n services.Configure<CookiePolicyOptions>(options =>\n {\n options.CheckConsentNeeded = context => true;\n options.MinimumSameSitePolicy = SameSiteMode.None;\n });\n services.AddSingleton<IEmailSender, NotificationService>();\n services.AddCors();\n ConfigureDatabase(services);\n services.AddIdentity<ApplicationUser, ApplicationRole>(options =>\n {\n options.Stores.MaxLengthForKeys = 128;\n options.Password.RequireDigit = true;\n options.Password.RequiredLength = 8;\n options.Password.RequireLowercase = true;\n options.Password.RequireUppercase = true;\n options.Password.RequireNonAlphanumeric = true;\n })\n .AddEntityFrameworkStores<ApplicationDbContext>();\n // Register the OpenIddict services.\n OpenIdDictStartup.OpenIdDictStartupServices(services, _authSettings);\n services.AddMvc(options =>\n {\n options.Filters.Add(new RequireHttpsAttribute());\n })\n .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)\n .AddRazorPagesOptions(options =>\n {\n options.AllowAreas = true;\n });\n //\n }\n //\n public virtual void ConfigureDatabase(IServiceCollection services)\n {\n string _connetionString = Configuration.GetConnectionString("DefaultConnection");\n if (string.IsNullOrEmpty(_connetionString))\n {\n throw (new ApplicationException("no connection string found"));\n }\n services.AddDbContext<ApplicationDbContext>(options =>\n {\n options.UseSqlServer(_connetionString);\n options.UseOpenIddict();\n });\n }\n //\n public void Configure(IApplicationBuilder app, IHostingEnvironment env)\n {\n app.UseDeveloperExceptionPage();\n OpenIdDictStartup.OpenIdDictConfigure(app, env);\n app.UseHttpsRedirection();\n app.UseStaticFiles();\n app.UseMvc(routes =>\n {\n routes.MapRoute(\n name: "default",\n template: "{controller=Home}/{action=Index}/{id?}");\n });\n OpenIdDictStartup.InitializeOpenIddictApplicationAsync(\n app.ApplicationServices).GetAwaiter().GetResult();\n //\n }\n }\nRun Code Online (Sandbox Code Playgroud)\n\n这是 OpenId Dict/Connect 代码的核心内容:
\n\nusing System;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Security.Cryptography.X509Certificates;\n//\nusing Microsoft.AspNetCore.Authentication.JwtBearer;\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.IdentityModel.Tokens;\nusing Microsoft.AspNetCore.Authentication.Cookies;\nusing Microsoft.AspNetCore.Http;\nusing Microsoft.AspNetCore.Hosting;\nusing Microsoft.AspNetCore.Authorization;\nusing Microsoft.AspNetCore.Identity;\nusing AspNet.Security.OpenIdConnect.Primitives;\n//\nusing OpenIddict.Abstractions;\nusing OpenIddict.Core;\nusing OpenIddict.EntityFrameworkCore.Models;\n//\nusing WebApp02.Data;\nusing WebApp02.Models;\n//\nnamespace WebApp02.OpenIddict\n{\n public static class OpenIdDictStartup\n {\n //\n public static void OpenIdDictStartupServices(IServiceCollection services, AuthSettings authSettings)\n {\n //\n X509Certificate2 _jwtSigningCert = new X509Certificate2(authSettings.CertLocation, authSettings.CertPassword);\n services.AddOpenIddict()\n .AddCore(options =>\n {\n options.UseEntityFrameworkCore()\n .UseDbContext<ApplicationDbContext>();\n })\n .AddServer(options =>\n {\n options.UseMvc();\n options.EnableTokenEndpoint("/connect/token");\n options.EnableLogoutEndpoint("/connect/logout");\n options.EnableUserinfoEndpoint("/api/userinfo");\n options.AllowPasswordFlow();\n options.AddSigningCertificate(_jwtSigningCert);\n options.DisableHttpsRequirement();\n options.RegisterScopes(OpenIdConnectConstants.Scopes.Email,\n OpenIdConnectConstants.Scopes.Profile,\n OpenIddictConstants.Scopes.Roles);\n });\n DualCookieJWTStartupServicesAuthentication(services, authSettings);\n //\n }\n //\n public static void DualCookieJWTStartupServicesAuthentication(IServiceCollection services, AuthSettings authSettings)\n {\n //\n X509Certificate2 _jwtSigningCert = new X509Certificate2(authSettings.CertLocation, authSettings.CertPassword);\n services.AddAuthentication()\n .AddCookie(options =>\n {\n options.SlidingExpiration = true;\n options.LoginPath = new PathString("/Identity/Account/Login");\n options.Events = new CookieAuthenticationEvents\n {\n OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync\n };\n })\n .AddJwtBearer(options =>\n {\n options.Authority = authSettings.JwtAuthority;\n options.RequireHttpsMetadata = false;\n options.SaveToken = true;\n options.IncludeErrorDetails = true;\n options.TokenValidationParameters = new TokenValidationParameters()\n {\n ValidIssuer = authSettings.JwtIssuer,\n ValidateIssuer = true,\n ValidAudience = authSettings.JwtAudience,\n IssuerSigningKey = new X509SecurityKey(_jwtSigningCert),\n ValidateIssuerSigningKey = true,\n };\n });\n services.AddAuthorization(options =>\n {\n var _policy = new AuthorizationPolicyBuilder()\n .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme, CookieAuthenticationDefaults.AuthenticationScheme)\n .RequireAuthenticatedUser()\n .Build();\n options.AddPolicy("DualCookieJWT", _policy);\n });\n //\n }\n //\n public static void OpenIdDictConfigure(IApplicationBuilder app, IHostingEnvironment env)\n {\n app.UseAuthentication();\n }\n //\n public static async Task InitializeOpenIddictApplicationAsync(IServiceProvider services)\n {\n //\n string _clientId = "incidentservices";\n using (var _scope = services.GetRequiredService<IServiceScopeFactory>().CreateScope())\n {\n try\n {\n var _dbContext = _scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();\n await _dbContext.Database.EnsureCreatedAsync();\n var _openIddictManager = _scope.ServiceProvider.GetRequiredService<OpenIddictApplicationManager<OpenIddictApplication>>();\n if (await _openIddictManager.FindByClientIdAsync(_clientId) == null)\n {\n var _descriptor = new OpenIddictApplicationDescriptor\n {\n ClientId = _clientId,\n Permissions =\n {\n OpenIddictConstants.Permissions.Endpoints.Logout,\n OpenIddictConstants.Permissions.Endpoints.Token,\n OpenIddictConstants.Permissions.GrantTypes.Password,\n OpenIddictConstants.Permissions.Scopes.Email,\n OpenIddictConstants.Permissions.Scopes.Profile,\n OpenIddictConstants.Permissions.Scopes.Roles\n }\n };\n await _openIddictManager.CreateAsync(_descriptor);\n }\n }\n catch (Exception _ex)\n {\n Console.WriteLine(_ex.ToString());\n }\n //\n }\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\nAuthorizationController 取自 OpenIddict-core 版本 2.0.1 Mvc.Server 示例。我更改了CreateTicketAsync,因此如下:
\n\nprivate async Task<AuthenticationTicket> CreateTicketAsync(\n OpenIdConnectRequest request, ApplicationUser user,\n AuthenticationProperties properties = null)\n{\n var _principal = await _signInManager.CreateUserPrincipalAsync(user);\n var _ticket = new AuthenticationTicket(_principal, properties,\n OpenIddictServerDefaults.AuthenticationScheme);\n if (!request.IsAuthorizationCodeGrantType() && !request.IsRefreshTokenGrantType())\n {\n _ticket.SetScopes(request.GetScopes());\n _ticket.SetResources("resource_server");\n }\n var _identity = (ClaimsIdentity)_principal.Identity;\n if (_ticket.HasScope(OpenIdConnectConstants.Scopes.Profile))\n {\n var _subject = new Claim(OpenIdConnectConstants.Claims.Subject,\n _authSettings.OIDCSubject,\n OpenIdConnectConstants.Destinations.AccessToken);\n _identity.AddClaim(_subject);\n //\n var _name = new Claim(OpenIdConnectConstants.Claims.Username, user.UserName);\n _name.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken);\n _identity.AddClaim(_name);\n //\n var _uname = new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName);\n _uname.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken);\n _identity.AddClaim(_uname);\n }\n IList<string> _roles = await _userManager.GetRolesAsync(user);\n foreach (string _role in _roles)\n {\n _identity.AddClaim(new Claim("roles", _role));\n }\n foreach (var _claim in _ticket.Principal.Claims)\n {\n _claim.SetDestinations(GetDestinations(_claim, _ticket));\n }\n return _ticket;\n //\n}\nRun Code Online (Sandbox Code Playgroud)\n\n请帮忙,我不知道如何获得工作不记名令牌。
\n| 归档时间: |
|
| 查看次数: |
2019 次 |
| 最近记录: |