【问题标题】:.NET Core MVC and Web API two authentication schemes.NET Core MVC 和 Web API 两种身份验证方案
【发布时间】:2021-08-27 04:21:34
【问题描述】:

我有一个 MVC 应用程序和一个公开的 API 端点。我使用 Identity Core 的默认值验证了我的 MVC 应用程序,我使用 User.FindFirstValue(ClaimTypes.NameIdentifier) 来查找某个用户是否已登录,等等。

对于我的 API Endpoint,我使用 JWT 身份验证下面是 JWT 的配置代码:

services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(jwt =>
        {
            var key = Encoding.ASCII.GetBytes(Configuration["Jwt:Secret"]);

        jwt.SaveToken = true;
        jwt.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(key),
            ValidateIssuer = false,
            ValidateAudience = false,
            RequireExpirationTime = false,
            ValidateLifetime = true
        };
    });

这是令牌请求的控制器:

[HttpPost]
[Route("token")]
public async Task<IActionResult> Token([FromBody] UserLoginRequest user)
{
    if (ModelState.IsValid)
    {
        var existingUser = await _userManager.FindByEmailAsync(user.Email);
        if (existingUser == null)
        {
            return BadRequest();
        }

         var isCorrect = await _userManager.CheckPasswordAsync(existingUser, user.Password);
         if (isCorrect)
         {
             var jwtToken = _identityService.GenerateJwtToken(existingUser);

             return Ok(new RegistrationResponse()
                {
                    Result = true,
                    Token = jwtToken
                });
         }
         else
         {
             return BadRequest();
         }

    }

    return BadRequest();
}

在我的 MVC 控制器上,我使用 [Authorize]

在我的 API 端点上,我使用 [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

我的GenerateJWTToken 方法:

public string GenerateJwtToken(IdentityUser user)
    {
        var jwtTokenHandler = new JwtSecurityTokenHandler();

        var key = Encoding.ASCII.GetBytes(_jwtConfig.Secret);

        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new[]
            {
            new Claim("Id", user.Id),
            new Claim(JwtRegisteredClaimNames.Sub, user.Email),
            new Claim(JwtRegisteredClaimNames.Email, user.Email),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
        }),
            Expires = DateTime.UtcNow.AddHours(6),
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha512Signature)
        };

        var token = jwtTokenHandler.CreateToken(tokenDescriptor);

        var jwtToken = jwtTokenHandler.WriteToken(token);

        return jwtToken;
    }
}

但显然,此解决方案无法正常运行,因为一旦我启动 MVC 应用程序并尝试登录,我就会被重定向回 Index 并且我仍然未经授权。反之亦然,使用 API 时,当我调用 Postman 时,我得到了令牌,当我尝试调用我的书签控制器来查询用户的书签时,我得到零,尽管有特定用户的书签。

欢迎任何关于我如何完成这项工作的想法。

【问题讨论】:

  • 在向书签发出请求时,您是否将不记名令牌与请求一起发送?其次,您的 mvc 身份验证失败,因为您已将 JWT 身份验证指定为默认身份验证方案。
  • 是的,我确实将令牌与请求一起发送。我的书签实体有一个 UserId 属性,我用它来检索该特定用户的所有书签。当这是在 MVC 中时,我很容易使用 (User.FindFirstValue(ClaimTypes.NameIdentifier)) 检索用户 ID,但在我的 API 调用中,我得到零个书签。 @杰弗里
  • @Jeffery 还有我可以做些什么来将会话身份验证和 JWT 身份验证设为默认值
  • 对于我完成的所有 JWT 身份验证,我编写了自己的 JWT 令牌生成器,我在其中指定了诸如 NameIdentifier 之类的声明类型以及我可能特别需要的其他声明。
  • 我发布了我的代码作为 cmets 的答案以提供帮助。看看能不能解决你的问题

标签: c# asp.net-mvc authentication .net-core jwt


【解决方案1】:

在我的 JWT 令牌生成器中,我获得了我想要存储为声明的用户的详细信息,例如用户名,可用于识别用户。

public static class JwtTokenExtensions
{
    /// <summary>
    /// Generates a JWT Bearer token containing the users email
    /// </summary>
    /// <param name="user"></param>
    /// <returns></returns>
    public static string GenerateJwtToken(this Identity user)
    {
        // Set our token claims
        Claim[] claims = {
            // Unique ID for this token
            new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString("N")),

            new(JwtRegisteredClaimNames.Email, user.Email),
            // The username using the Identity name so it fills out the HttpContext.User.Identity.Name value
            new(ClaimsIdentity.DefaultNameClaimType, user.UserName),
            // Add user Id so that UserManager.GetUserAsync can find the user based on Id
            new Claim(ClaimTypes.NameIdentifier, user.Id)
        };

        // Create the credentials used to generate the token
        SigningCredentials credentials = 
        new SigningCredentials(SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:SecretKey"])),
            SecurityAlgorithms.HmacSha256);

        // Generate the Jwt Token that lasts for an hour before expiring
        JwtSecurityToken token =
            new JwtSecurityToken
            (Configuration["Jwt:Issuer"],
            Configuration["Jwt:Audience"], 
            claims:claims,
            signingCredentials:credentials,
            expires: DateTime.Now.AddHours(1));

        // Return the generated token.
        return new JwtSecurityTokenHandler().WriteToken(token);
    }
}

在具有 JWT 授权的 api 控制器中,我可以通过 HttpContext 获取用户 var user = await _userManager.GetUserAsync(HttpContext.User);

【讨论】:

  • 这个解决方案是我问题的正确答案。谢谢:) @Jeffery
【解决方案2】:

在您的令牌请求的控制器中,尝试添加 [AllowAnonymous],如下所示:

[AllowAnonymous]
[HttpPost]
[Route("token")]
public async Task<IActionResult> Token([FromBody] UserLoginRequest user)
{
    // snip...
}

【讨论】:

    猜你喜欢
    • 2021-10-18
    • 2020-10-15
    • 2022-07-27
    • 1970-01-01
    • 2018-03-23
    • 1970-01-01
    • 2017-04-27
    • 1970-01-01
    • 2019-05-25
    相关资源
    最近更新 更多