使用Angular和MVC Core进行JWT身份验证/授权

时间:2016-12-01 00:53:10

标签: asp.net angularjs asp.net-core-mvc jwt openiddict

我正在使用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);
    }

我将省略角度的http拦截器,因为路由通过应用程序以及Postman都失败了。在配置MVC以正确处理这些令牌时,我是否还有什么遗漏?

1 个答案:

答案 0 :(得分:-1)

在启用日志记录并查看更详细的错误后,结束解决此问题。

我将JwtBearerAuthentication更新为:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.28.0/js/jquery.tablesorter.min.js"></script>


<table id="myTable" class="tablesorter"> 
    <thead> 
        <tr> 
            <th>Last Name</th> 
            <th>First Name</th> 
            <th>Email</th> 
            <th>Due</th> 
            <th>Web Site</th> 
        </tr> 
    </thead> 
    <tbody> 
        <tr> 
            <td>Smith</td> 
            <td>John</td> 
            <td>jsmith@gmail.com</td> 
            <td>$50.00</td> 
            <td>http://www.jsmith.com</td> 
        </tr> 
        <tr> 
            <td>Bach</td> 
            <td>Frank</td> 
            <td>fbach@yahoo.com</td> 
            <td>$50.00</td> 
            <td>http://www.frank.com</td> 
        </tr> 
        <tr> 
            <td>Doe</td> 
            <td>Jason</td> 
            <td>jdoe@hotmail.com</td> 
            <td>$100.00</td> 
            <td>http://www.jdoe.com</td> 
        </tr> 
        <tr> 
            <td>Conway</td> 
            <td>Tim</td> 
            <td>tconway@earthlink.net</td> 
            <td>$50.00</td> 
            <td>http://www.timconway.com</td> 
        </tr> 
    </tbody> 
</table> 

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