在OpenIddict中处理请求时发生未处理的异常

时间:2017-06-20 19:33:15

标签: c# asp.net-core .net-core openiddict

所以,我试图用.net核心1.1实现OpenIddict版本1.0.0-beta2-0580,我收到以下错误:

  

处理请求时发生未处理的异常

这是基于此:https://github.com/openiddict/openiddict-core/tree/dev/samples Connect rest call

db正确注册数据库,加载设置,一切都在这里工作。数据库中的表格:__efmigrationshistoryaspnetroleclaimsaspnetrolesaspnetuserclaimsaspnetuserloginsaspnetuserrolesaspnetusers,{{ 1}},aspnetusertokensbasetransactionopeniddictapplicationsopeniddictauthorizationsopeniddictscopes

然后我有

堆栈跟踪:

openiddicttokens

在创业中我有:

InvalidOperationException: The authentication ticket was rejected because the mandatory subject claim was missing.
AspNet.Security.OpenIdConnect.Server.OpenIdConnectServerHandler+<HandleSignInAsync>d__5.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Authentication.AuthenticationHandler+<SignInAsync>d__66.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Http.Authentication.Internal.DefaultAuthenticationManager+<SignInAsync>d__14.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.SignInResult+<ExecuteResultAsync>d__14.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker+<InvokeResultAsync>d__30.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker+<InvokeNextResultFilterAsync>d__28.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ResultExecutedContext context)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker+<InvokeNextResourceFilter>d__22.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ResourceExecutedContext context)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker+<InvokeAsync>d__20.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Builder.RouterMiddleware+<Invoke>d__4.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.VisualStudio.Web.BrowserLink.BrowserLinkMiddleware+<ExecuteWithFilter>d__7.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware+<Invoke>d__7.MoveNext()

然后在配置中我有这个:

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddMvc();

    services.RegisterDatabase(aspNet: true, useOpenIddict : true);

    // Register the Identity services.
    service.AddIdentity<User, IdentityRole>(config => { config.SignIn.RequireConfirmedEmail = requireConfirmEmail; })
          .AddEntityFrameworkStores<DatabaseContext>()
          .AddDefaultTokenProviders();

    services.AddOpenIddict(options =>
    {
        // Register the Entity Framework stores.
        options.AddEntityFrameworkCoreStores<DatabaseContext>();

        // Register the ASP.NET Core MVC binder used by OpenIddict.
        // Note: if you don't call this method, you won't be able to
        // bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
        options.AddMvcBinders();

        // Enable the token endpoint.
        options.EnableTokenEndpoint("/connect/token");

        // Enable the password flow.
        options.AllowPasswordFlow();

        // During development, you can disable the HTTPS requirement.
        options.DisableHttpsRequirement();

        // Note: to use JWT access tokens instead of the default
        // encrypted format, the following lines are required:
        //
        // options.UseJsonWebTokens();
        // options.AddEphemeralSigningKey();
    });
}

然后auth控制器看起来像这样:

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IServiceProvider service, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    app.UseOpenIddict();

    // Create a new service scope to ensure the database context is correctly disposed when this methods returns.
    using (var scope = service.GetRequiredService<IServiceScopeFactory>().CreateScope())
    {
        var context = scope.ServiceProvider.GetRequiredService<DatabaseContext>();

        await context.Database.MigrateAsync();

        OpenIddictApplicationManager<OpenIddictApplication> manager = scope.ServiceProvider.GetRequiredService<OpenIddictApplicationManager<OpenIddictApplication>>();

        /*if (await manager.FindByClientIdAsync("mvc", cancellationToken) == null)
        {
            var application = new OpenIddictApplication
            {
                ClientId = "mvc",
                DisplayName = "MVC client application",
                LogoutRedirectUri = "http://localhost:53507/",
                RedirectUri = "http://localhost:53507/signin-oidc"
            };

            await manager.CreateAsync(application, "901564A5-E7FE-42CB-B10D-61EF6A8F3654", cancellationToken);
        }*/


        // To test this sample with Postman, use the following settings:
        //
        // * Authorization URL: http://localhost:54540/connect/authorize
        // * Access token URL: http://localhost:54540/connect/token
        // * Client ID: postman
        // * Client secret: [blank] (not used with public clients)
        // * Scope: openid email profile roles
        // * Grant type: authorization code
        // * Request access token locally: yes
        var client = await manager.FindByClientIdAsync("postman", cancellationToken); 

        if (client == null)
        {
            var application = new OpenIddictApplication
            {
                ClientId = "postman",
                DisplayName = "Postman",
            };
            await manager.CreateAsync(application, cancellationToken);
        }
    }


    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

依赖项:

public class AuthorizationController : Controller
{
    private readonly SignInManager<User> _signInManager;
    private readonly UserManager<User> _userManager;

    public AuthorizationController(
        SignInManager<User> signInManager,
        UserManager<User> userManager)
    {
        _signInManager = signInManager;
        _userManager = userManager;
    }

    [HttpPost("~/connect/token"), Produces("application/json")]
    public async Task<IActionResult> Exchange(OpenIdConnectRequest request)
    {
        Debug.Assert(request.IsTokenRequest(),
            "The OpenIddict binder for ASP.NET Core MVC is not registered. " +
            "Make sure services.AddOpenIddict().AddMvcBinders() is correctly called.");

        if (request.IsPasswordGrantType())
        {
            var user = await _userManager.FindByNameAsync(request.Username);
            if (user == null)
            {
                return BadRequest(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidGrant,
                    ErrorDescription = "The username/password couple is invalid."
                });
            }

            // Ensure the user is allowed to sign in.
            if (!await _signInManager.CanSignInAsync(user))
            {
                return BadRequest(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidGrant,
                    ErrorDescription = "The specified user is not allowed to sign in."
                });
            }

            // Reject the token request if two-factor authentication has been enabled by the user.
            if (_userManager.SupportsUserTwoFactor && await _userManager.GetTwoFactorEnabledAsync(user))
            {
                return BadRequest(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidGrant,
                    ErrorDescription = "The specified user is not allowed to sign in."
                });
            }

            // Ensure the user is not already locked out.
            if (_userManager.SupportsUserLockout && await _userManager.IsLockedOutAsync(user))
            {
                return BadRequest(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidGrant,
                    ErrorDescription = "The username/password couple is invalid."
                });
            }

            // Ensure the password is valid.
            if (!await _userManager.CheckPasswordAsync(user, request.Password))
            {
                if (_userManager.SupportsUserLockout)
                {
                    await _userManager.AccessFailedAsync(user);
                }

                return BadRequest(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidGrant,
                    ErrorDescription = "The username/password couple is invalid."
                });
            }

            if (_userManager.SupportsUserLockout)
            {
                await _userManager.ResetAccessFailedCountAsync(user);
            }

            // Create a new authentication ticket.
            var ticket = await CreateTicketAsync(request, user);

            return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
        }

        return BadRequest(new OpenIdConnectResponse
        {
            Error = OpenIdConnectConstants.Errors.UnsupportedGrantType,
            ErrorDescription = "The specified grant type is not supported."
        });
    }

    private async Task<AuthenticationTicket> CreateTicketAsync(OpenIdConnectRequest request, User user)
    {
        // Create a new ClaimsPrincipal containing the claims that
        // will be used to create an id_token, a token or a code.
        var principal = await _signInManager.CreateUserPrincipalAsync(user);

        // Note: by default, claims are NOT automatically included in the access and identity tokens.
        // To allow OpenIddict to serialize them, you must attach them a destination, that specifies
        // whether they should be included in access tokens, in identity tokens or in both.

        foreach (var claim in principal.Claims)
        {
            // In this sample, every claim is serialized in both the access and the identity tokens.
            // In a real world application, you'd probably want to exclude confidential claims
            // or apply a claims policy based on the scopes requested by the client application.
            claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken,
                                  OpenIdConnectConstants.Destinations.IdentityToken);
        }

        // Create a new authentication ticket holding the user identity.
        var ticket = new AuthenticationTicket(
            principal, new AuthenticationProperties(),
            OpenIdConnectServerDefaults.AuthenticationScheme);

        // Set the list of scopes granted to the client application.
        // Note: the offline_access scope must be granted
        // to allow OpenIddict to return a refresh token.
        ticket.SetScopes(new[]
        {
            OpenIdConnectConstants.Scopes.OpenId,
            OpenIdConnectConstants.Scopes.Email,
            OpenIdConnectConstants.Scopes.Profile,
            OpenIdConnectConstants.Scopes.OfflineAccess,
            OpenIddictConstants.Scopes.Roles
        }.Intersect(request.GetScopes()));

        return ticket;
    }
}

2 个答案:

答案 0 :(得分:4)

您看到的错误是由于您的ClaimsPrincipal没有强制sub声明,这是由异常消息指示的。

要解决此问题,您有两种选择:手动添加sub声明或要求身份使用sub作为名称标识符声明。

sub声明添加到await _signInManager.CreateUserPrincipalAsync(user);返回的主体

// Note: while ASP.NET Core Identity uses the legacy WS-Federation claims (exposed by the ClaimTypes class),
// OpenIddict uses the newer JWT claims defined by the OpenID Connect specification. To ensure the mandatory
// subject claim is correctly populated (and avoid an InvalidOperationException), it's manually added here.
if (string.IsNullOrEmpty(principal.FindFirstValue(OpenIdConnectConstants.Claims.Subject)))
{
    identity.AddClaim(new Claim(OpenIdConnectConstants.Claims.Subject, await _userManager.GetUserIdAsync(user)));
}

...或要求身份使用sub作为名称标识符声明:

services.Configure<IdentityOptions>(options =>
{
    options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
    options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
    options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
});

答案 1 :(得分:-1)

打开文件startup.cs并在ConfigureServices中使用下面的代码

services.AddTransient();

使用型号名称输入<>