如何在控制器方法中使 JWT 令牌授权可选

Posted

技术标签:

【中文标题】如何在控制器方法中使 JWT 令牌授权可选【英文标题】:How to make JWT token authorization optional in controller methods 【发布时间】:2018-07-19 01:36:57 【问题描述】:

我在 ASP.NET Core 2 Web 应用程序中使用 JWT 令牌

如果 JWT 令牌失败,我仍然希望调用控制器方法,但 ASP.NET 标识 User 对象将为空。目前,如果身份验证失败,控制器方法将不会被输入,因此我无法在方法内部应用逻辑来处理我想作为访客处理而不是阻止他们的未经身份验证的用户。

我的代码:

Startup.cs

    public void ConfigureServices(IServiceCollection services)
    

        services.AddAuthentication()
            .AddJwtBearer(cfg =>
            
                cfg.TokenValidationParameters = new TokenValidationParameters()
                
                    ValidIssuer = _config["Tokens:Issuer"],
                    ValidAudience = _config["Tokens:Audience"],
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]))
                ;

            );

当用户登录时

   private IActionResult CreateToken(ApplicationUser user, IList<string> roles)
    
        var claims = new List<Claim>
        
          new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()),
          new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
          new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName)
        ;



        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]));

        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken(
          _config["Tokens:Issuer"],
          _config["Tokens:Audience"],
          claims.ToArray(),
         expires: DateTime.Now.AddMinutes(60),
          signingCredentials: creds);

        var results = new
        
            token = new JwtSecurityTokenHandler().WriteToken(token),
            expiration = token.ValidTo,
        ;

        return Created("", results);

我确保在经过身份验证后,各种 API 调用都会传递 JWT 令牌。

在我的控制器中,我使用了 Authorize 属性

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
 public class MyController : Controller

这使我可以使用以下代码捕获登录用户以进行各种 API 调用

 var loggedInUser = User.Identity.Name;

如果我删除了授权属性,那么即使用户通过了身份验证,User.Identity.Name 也将始终为空。

如果令牌无效,如何使用授权属性但仍允许输入控制器方法?我想对要输入的授权用户和非授权用户使用相同的方法。然后,我将使用User.Identity.Name 来确定他们是否未经身份验证,并在他们的方法中包含来宾用户逻辑。

【问题讨论】:

我认为您可以编写自己的 Authorize 属性,例如AuthorizeWithGuests 并将逻辑放在该属性中,您将在其中进行检查并查看用户是否未通过身份验证,而不是将用户视为访客。 我不知道当前 Authorize 属性的内部结构,我认为我必须重写它,因为我无法继承它,因为它会阻止访问 是的,你必须在那里编写自己的代码,然后像这样使用它[AuthorizeWithGuests] 【参考方案1】:

这实际上比您想象的要容易。您可以同时使用 AuthorizeAllowAnonymous,如下所示:

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[AllowAnonymous]
public class MyController : Controller

如果授权成功,您将拥有一个经过身份验证的User (User.Identity.IsAuthenticated = true),否则,您将拥有一个匿名的User

【讨论】:

这也将绕过您认为控制器上的任何安全措施。 @Brad 我想处理控制器内部的安全性,但仍然填充了 Authorize 属性所做的 User.Identity。柯克的解决方案为我解决了这个问题 在这种方法中是否可以确定是否提供了有效但已过期的令牌?我是否必须在一些 Http 标头中四处寻找以提取令牌并自己验证它? @Simon_Weaver 是的,我认为您必须自己拉出标题并以这种方式进行管理。根据您要执行的操作,您可能需要考虑使用JwtBearerEvents 钩子,该钩子在更交叉的层面上工作。【参考方案2】:

通过稍微更新 ASP.NET Core,除了配置您的令牌指南外,您还可以通过添加使您的控制器需要承载令牌身份验证:

Startup.cs

  public void ConfigureServices(IServiceCollection services)
  
      services.AddJwtSecurity(
                signingConfigurations, tokenConfigurations);
  

请记住,如果您想一次阻止整个控制器,只需将其添加到 Authorize 类的顶部即可。

namespace <name_controller>
    
    [Produces("application/json")]
    [Route("api/v1/[controller]")]
    [ApiController]
    [RequireHttps]
    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
    public class clientesController : Controller
    
    

【讨论】:

【参考方案3】:

我不确定何时更改,但我的应用程序在 ASP.NET Core 3.1 上并且没有我的操作/端点指定我需要授权,当有效令牌是时,身份验证中间件仍会在我的 ClaimsPrincipal 中提供我需要的信息没有指定令牌时提供和空白主体。

【讨论】:

此行为在 .NET 5 中继续存在。如果您在 Startup 中添加 Authentication 中间件,即使您没有使用授权属性。 如果令牌无效会抛出错误。 @MÇT

以上是关于如何在控制器方法中使 JWT 令牌授权可选的主要内容,如果未能解决你的问题,请参考以下文章

Swagger + spring boot + jwt + 如何禁用特定 API 的授权按钮

如何使用 JWT 令牌 ASP.NET MVC

C# .NET 5.0 MVC JWT 中间件授权标头

如何使用 JWT 令牌进行授权

如何在 express-JWT 中获取授权令牌?

如何在 jwt 授权的 web api 上执行 XUnit 测试?