令牌未通过 OpenIdDict/Connect 进行身份验证

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}\n
Run Code Online (Sandbox Code Playgroud)\n\n

在 Postman 中,我通过传递以下内容生成令牌:

\n\n
KEY     VALUE\ngrant_type  password\nusername    Phil\npassword    P@ssw0rd\nclient_id   incidentservices\nscope       openid profile email roles\n
Run 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}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我使用上面的\xe2\x80\x98access token\xe2\x80\x99和以下标头调用测试 Web API :

\n\n
KEY     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-\n
Run Code Online (Sandbox Code Playgroud)\n\n

并使用断点,我检查User.IdentityIsAuthenticated为 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    }\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是 OpenId Dict/Connect 代码的核心内容:

\n\n
using 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}\n
Run Code Online (Sandbox Code Playgroud)\n\n

AuthorizationController 取自 OpenIddict-core 版本 2.0.1 Mvc.Server 示例。我更改了CreateTicketAsync,因此如下:

\n\n
private 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}\n
Run Code Online (Sandbox Code Playgroud)\n\n

请帮忙,我不知道如何获得工作不记名令牌。

\n