0

我正在开发一个使用 ASP.NET MVC 核心作为后端 API 的 Angular SPA,我在让我的 JWT 身份验证正常工作时遇到了很多麻烦。

我使用 Openiddict 作为我的 JWT 中间件来发行令牌。我可以成功地向我的控制器发送令牌请求,让用户登录,然后将令牌发送回客户端。

但是,当我尝试访问受保护的 API 路由时,我收到了 401 未经授权的消息。我可以看到令牌是在请求的标头中发送的,所以它一定不能在服务器端正确读取,所以我认为这只是一个配置问题。我为此查看的每一个资源都让人觉得没有其他明智的配置可做。

以下是代码的相关部分:

启动配置服务

public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
        services.AddAuthorization();

        services.AddEntityFramework()
            .AddEntityFrameworkSqlServer()
            .AddDbContext<DbContext>();

        services.AddIdentity<User, IdentityRole>(config =>
       {
           config.User.RequireUniqueEmail = true;
           config.Password.RequiredLength = 8;
       })
       .AddEntityFrameworkStores<DbContext>()
       .AddDefaultTokenProviders();

        services.AddOpenIddict<DbContext>()
            .AddMvcBinders()
            .EnableTokenEndpoint("/auth/token")
            .UseJsonWebTokens()
            .AllowPasswordFlow()
            .AddEphemeralSigningKey()
            //.AddSigningCertificate()
            .DisableHttpsRequirement();

        services.AddLogging();
    }

启动配置应用程序

public void Configure(IApplicationBuilder app, RequestJockeyDataSeeder seeder, ILoggerFactory factory)
    {

        app.UseDefaultFiles(new DefaultFilesOptions()
        {
            DefaultFileNames = new[] { "index.html" }
        });
        app.UseStaticFiles();

        app.UseOpenIddict();

        app.UseJwtBearerAuthentication(new JwtBearerOptions()
        {
            AutomaticAuthenticate = true,
            AutomaticChallenge = true,
            Audience = "http://localhost:5000",
            Authority = "http://localhost:5000",
            RequireHttpsMetadata = false,

        });

        app.UseMvc();

    }

控制器- 我从网上的一个例子中得到这个

[HttpPost("~/auth/token"), Produces("application/json")]
    public async Task<IActionResult> Token(OpenIdConnectRequest request)
    {
        if (!request.IsPasswordGrantType())
        {
            // Return bad request if the request is not for password grant type
            return BadRequest(new OpenIdConnectResponse
            {
                Error = OpenIdConnectConstants.Errors.UnsupportedGrantType,
                ErrorDescription = "The specified grant type is not supported."
            });
        }

        var user = await _userManager.FindByNameAsync(request.Username);
        if (user == null)
        {
            // Return bad request if the user doesn't exist
            return BadRequest(new OpenIdConnectResponse
            {
                Error = OpenIdConnectConstants.Errors.InvalidGrant,
                ErrorDescription = "Invalid username or password"
            });
        }

        // Check that the user can sign in and is not locked out.
        // If two-factor authentication is supported, it would also be appropriate to check that 2FA is enabled for the user
        if (!await _signInManager.CanSignInAsync(user) || (_userManager.SupportsUserLockout && await _userManager.IsLockedOutAsync(user)))
        {
            // Return bad request is the user can't sign in
            return BadRequest(new OpenIdConnectResponse
            {
                Error = OpenIdConnectConstants.Errors.InvalidGrant,
                ErrorDescription = "The specified user cannot sign in."
            });
        }

        if (!await _userManager.CheckPasswordAsync(user, request.Password))
        {
            // Return bad request if the password is invalid
            return BadRequest(new OpenIdConnectResponse
            {
                Error = OpenIdConnectConstants.Errors.InvalidGrant,
                ErrorDescription = "Invalid username or password"
            });
        }

        // The user is now validated, so reset lockout counts, if necessary
        if (_userManager.SupportsUserLockout)
        {
            await _userManager.ResetAccessFailedCountAsync(user);
        }

        // Create the principal
        var principal = await _signInManager.CreateUserPrincipalAsync(user);

        // Claims will not be associated with specific destinations by default, so we must indicate whether they should
        // be included or not in access and identity tokens.
        foreach (var claim in principal.Claims)
        {
            // For this sample, just include all claims in all token types.
            // In reality, claims' destinations would probably differ by token type and depending on the scopes requested.
            claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken, OpenIdConnectConstants.Destinations.IdentityToken);
        }

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

        // Include resources and scopes, as appropriate
        var scope = new[]
        {
    OpenIdConnectConstants.Scopes.OpenId,
    OpenIdConnectConstants.Scopes.Email,
    OpenIdConnectConstants.Scopes.Profile,
    OpenIdConnectConstants.Scopes.OfflineAccess,
    OpenIddictConstants.Scopes.Roles
}.Intersect(request.GetScopes());

        ticket.SetResources("http://localhost:5000/");
        ticket.SetScopes(scope);
        ticket.Properties.ExpiresUtc = DateTimeOffset.Now.AddMinutes(15);

        // Sign in the user
        return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
    }

我将省略 angular http 拦截器,因为路由通过应用程序和 Postman 都失败了。就配置 MVC 以正确处理这些令牌而言,我有什么遗漏吗?

4

1 回答 1

-1

在启用日志记录并查看更详细的错误后最终解决了此问题。

我将我的 JwtBearerAuthentication 更新为:

        app.UseJwtBearerAuthentication(new JwtBearerOptions()
        {
            AutomaticAuthenticate = true,
            AutomaticChallenge = true,
            Audience = "http://localhost:5000/",
            Authority = "http://localhost:5000/",
            RequireHttpsMetadata = false,
            TokenValidationParameters = new TokenValidationParameters() {
                ValidateIssuerSigningKey = false
            }
         }

主要变化是ValidateIssuerSigningKey = False

于 2016-12-01T02:44:37.460 回答