使用令牌 WebAPI 对 MVC 应用程序进行身份验证

Posted

技术标签:

【中文标题】使用令牌 WebAPI 对 MVC 应用程序进行身份验证【英文标题】:Authenticating an MVC application with tokens WebAPI 【发布时间】:2019-12-31 21:56:37 【问题描述】:

我编写了一个使用 Jwt 令牌进行身份验证的基本 WebAPI。当我使用 Postman 进行测试 API 调用时,我得到了一个成功发出的令牌。然而,我一直坚持让我的 MVC 应用程序使用令牌进行身份验证。

这里是 API 的控制器 -

[HttpPost]
    [Route("login")]
    public async Task<IActionResult> Login([FromBody] LoginModel model)
    
        var user = await userManager.FindByNameAsync(model.Username);
        if (user != null && await userManager.CheckPasswordAsync(user, model.Password))
        

            var authClaims = new[]
            
                new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
            ;

            var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("YVBy0OLlMQG6VVVp1OH7Xzyr7gHuw1qvUC5dcGt3SBM="));

            var token = new JwtSecurityToken(
                issuer: "https://localhost:44350",
                audience: "https://localhost:44350",
                expires: DateTime.Now.AddHours(3),
                claims: authClaims,
                signingCredentials: new Microsoft.IdentityModel.Tokens.SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)
                );

            return Ok(new
            
                token = new JwtSecurityTokenHandler().WriteToken(token),
                expiration = token.ValidTo
            );
        
        return Unauthorized();
    

这是 WebAPI 的 Startup.cs -

public class Startup

    public Startup(IConfiguration configuration)
    
        Configuration = configuration;
    

    public IConfiguration Configuration  get; 

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

        services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
        services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();

        services.AddAuthentication(options =>
        
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
        )
        .AddJwtBearer(options =>
        
            options.SaveToken = true;
            options.RequireHttpsMetadata = false;
            options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
            
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidAudience = "https://localhost:44350/",
                ValidIssuer = "https://localhost:44350/",
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("AA873344gshtrhjLJKJSLKF8u4o8grwieot4KJHFs9847GGSD"))
            ;
        );
    

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    
        if (env.IsDevelopment())
        
            app.UseDeveloperExceptionPage();
        

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseAuthentication();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        
            endpoints.MapControllers();
        );

        SeedDB.Initialize(app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope().ServiceProvider);
    

我的 MVC 应用程序控制器代码如下。它成功生成了一个令牌,但我不知道如何填充 HttpContext.User.Identity?

[HttpPost]
    public async Task<ActionResult> Index(LoginModel login)
    
        string url = BaseUrl + "api/authenticate/login";

        using (var client = new HttpClient())
        
            client.BaseAddress = new Uri(url);

            var postTask = client.PostAsJsonAsync<LoginModel>("login", login);
            postTask.Wait();

            var result = postTask.Result;
            if (result.IsSuccessStatusCode)
            
                var user = HttpContext.User.Identity as ClaimsIdentity;

                var tokenDetails = JsonConvert.DeserializeObject<Dictionary<string, string>>(result.Content.ReadAsStringAsync().Result);

                var claims = new Claim[]
                
                    new Claim(JwtRegisteredClaimNames.Sub, login.Username, "string"),
                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                    new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.Ticks.ToString(), ClaimValueTypes.Integer64)
                ;

                user.AddClaims(claims);

                return RedirectToAction("Index", "Home", null);
            
        

        ModelState.AddModelError(string.Empty, "Server error");

        return View(login);
    

这是 MVC 应用程序的 Startup.cs

public class Startup

    public Startup(IConfiguration configuration)
    
        Configuration = configuration;
    

    public IConfiguration Configuration  get; 

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

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    
        if (env.IsDevelopment())
        
            app.UseDeveloperExceptionPage();
        
        else
        
            app.UseExceptionHandler("/Home/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        
        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();



        app.UseEndpoints(endpoints =>
        
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "controller=Home/action=Index/id?");
        );
    

【问题讨论】:

【参考方案1】:

您应该在startup.cs中添加认证服务并使用认证中间件:

services.AddAuthentication(option =>

    option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
).AddJwtBearer(option =>
    
        option.RequireHttpsMetadata = false;
        option.SaveToken = true;
        option.TokenValidationParameters = new TokenValidationParameters
        
            RequireExpirationTime = true,
            ValidateLifetime = true,
            ValidIssuer = "Some Issuer",
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidAudience = "Some Audience",
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("Security Key")),
            ValidateIssuerSigningKey = true,
        ;
);

还将app.UseAuthentication(); 添加到startup.cs 中的Configure 方法中。

【讨论】:

这是在WebAPI项目还是MVC项目?它已经在 WebAPI 中,我已将上述内容添加到 MVC(更改了颁发者、受众和安全密钥以匹配 WebAPI 之一)。仍然 HttpContext.User.Identity 为空。我觉得错误出在 MVC 登录控制器中,它没有设置 HttpContext.User.Identity... @dynmatt 这是一个 WebAPI2 项目,但一般没有区别。请提供有关发送 jwt 令牌的更多详细信息,startup.cs 以及您正在检查令牌验证的位置。 我已将 WebAPI 和 MVC 应用程序的 Startup.cs 添加到原始帖子中。这就是我真正拥有的所有代码,登录 API 生成令牌,MVC Actionresult 调用 API 并成功获取令牌。它只是 MVC 应用程序似乎并没有真正登录到身份?

以上是关于使用令牌 WebAPI 对 MVC 应用程序进行身份验证的主要内容,如果未能解决你的问题,请参考以下文章

如何使用基于令牌的 HTTP 基本身份验证保护 MVC 4 .NET SPA 模板中的 WebAPI?

Web API 在 MVC 中存储承载令牌的位置

MVC 5 身份验证

如何对 ASP.NET WebApi 的每个请求应用自定义验证到 JWT 令牌?

在 MVC 5 的 cookie 中存储 JWT 令牌

MVC .NET cookie 身份验证系统通过令牌身份验证访问 Web Api