令牌无法存储在 .NET Core 中

Posted

技术标签:

【中文标题】令牌无法存储在 .NET Core 中【英文标题】:Token can't store in .NET Core 【发布时间】:2020-07-12 04:02:29 【问题描述】:

我有一个使用 .NET 核心 3.1 版的项目,我正在使用令牌进行登录。使用 Postman 进行测试时一切正常,它创建了令牌,我可以使用它来访问主页。

问题是,当我开始在客户端进行测试时,它不起作用。我登录后调试看到,生成了token,但是因为[Authorize]属性,我无法访问HomeController

这是我生成令牌的代码:

public async Task<HttpResponse<LoginResult>> GetTokenAsync(LoginRequest loginInfo)

    var audience = await _audiences.FindAsync(a => a.Id == loginInfo.ClientId);
    string message = string.Empty;

    if (audience != null)
    
        bool audienceIsValid = _jwtProvider.ValidateAudience(audience.Issuer
                    , audience.SecretKey
                    , ref message);

        if (audienceIsValid)
            return await GenerateToken(loginInfo);
        else
            message = ErrorMessages.Login_AudienceInvalid;
    
    else
        message = string.Format(ErrorMessages.Login_Not_Permitted, "Your client Id");
    return HttpResponse<LoginResult>.Error(message, HttpStatusCode.BadRequest);

我猜该令牌无法正确存储。

我错过了什么?

更新 这是我的登录代码

[HttpPost]
[Route("login")]
[AllowAnonymous]
public async Task<ActionResult> Login([FromForm]LoginRequest model)

      model.ClientId = 1;
      var response = await _services.GetTokenAsync(model);
      if (response.StatusCode == 200)
      
           return RedirectToAction("Index", "Home");
      
      return RedirectToAction("Login");

这就是我要访问的内容

[HttpGet]
[Route("index")]
[Authorize]
public IActionResult Index()

     return View();

【问题讨论】:

所提供的代码都不能帮助确定令牌正确存储的原因。您是否将其正确传递到后端? 似乎您已经显示了生成令牌的服务器代码。但是,您如何将其存储在客户端?您必须像 Postman 测试那样获取令牌并为每个服务调用提供它 @oleksa 似乎我没有获得令牌并将其发送到任何地方,因此我无法存储也无法使用它。你对此有什么方向吗?我只是 .NET core 的新成员,所以我不知道如何为每个需要它的函数提供令牌。谢谢。 @Mickers 不知道有没有传到后端,也许没有,你有没有指点,我真的不知道怎么做,谢谢. 通常您将令牌存储在前端的会话或本地存储中,然后在每个后续 API 调用中将其传递到标头中,例如 token $token 【参考方案1】:

您需要创建自定义策略以在配置为使用自定义需求处理程序的 Authorize 属性中指定

首先,您通过继承IAuthorizationRequirement 的类列出自定义策略的要求

public class TokenRequirement : IAuthorizationRequirement


如果需要,您可以在此处选择接受参数。但通常您在请求的标头中传递一个令牌,您的自定义策略的需求处理程序无需显式参数即可访问该令牌。

您的自定义策略要使用的需求处理程序看起来像这样

public class TokenHandler : AuthorizationHandler<TokenRequirement>

    //Some kind of token validator logic injected into your handler via DI
    private readonly TokenValidator _tokenValidator;

    //The http context of this request session also injected via DI
    private readonly IHttpContextAccessor _httpCtx;

    //The name of the header your token can be found under on a Http Request
    private const string tokenHeaderKey = "authToken";

    //Constructor using DI to get a instance of a TokenValidator class you would
    //have written yourself, and the httpContext
    public TokenHandler(TokenValidator tokenValidator, IHttpContextAccessor httpCtx)
    
        _tokenValidator = tokenValidator;
        _httpCtx = httpCtx;
    

    //Overriden implementation of base class AuthorizationHandler's HandleRequirementAsync method 
    //This is where you check your token.
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context
        ,TokenRequirement requirement)
    
        if (context.Resource is Endpoint endpoint)
        
            HttpRequest httpReqCtx = _httpCtx.HttpContext.Request;
            string token =
                httpReqCtx.Headers.TryGetValue(tokenHeaderKey, out StringValues tokenVal)
                    ? tokenVal.FirstOrDefault()
                    : null;

            if (string.IsNullOrWhitespace(token))
            
                context.Fail();
            
            else
            
                bool tokenIsValid = await _tokenValidator.ValidToken(token);
                if(tokenIsValid)
                    context.Succeed(requirement);
                else
                    context.Fail();
            
        
        return Task.CompletedTask;
    

您可以像这样在 Startup.cs 中的自定义策略名称上注册自定义需求处理程序

//This is a framework extension method under Microsoft.Extensions.DependencyInjection
services.AddHttpContextAccessor();

//Your custom handler
services.AddSingleton<IAuthorizationHandler, TokenHandler>();

//Your custom policy
services.AddAuthorization(options =>

    options.AddPolicy(
        //Your custom policy's name, can be whatever you want
        "myCustomTokenCheckerPolicy",
        //The requirement your policy is going to check
        //Which will be handled by the req handler added above
        policy => policy.Requirements.Add(new TokenRequirement())
    );
);

属性上的 impl 看起来像这样

[HttpGet]
[Route("index")]
[Authorize(Policy = "myCustomTokenCheckerPolicy")]
public IActionResult Index()

    return View();

【讨论】:

非常感谢您的出色回答。我已尝试按照您的指示进行操作,但 HandleRequirementAsync 函数的 AuthorizationHandlerContext 上下文根本没有任何价值,因此令牌字符串始终返回 null。我不知道为什么。你有什么解决办法吗? 做一些快速的谷歌搜索,看起来 .Net Core 3 的情况略有变化,很抱歉造成混淆。我将更新我的答案以准确反映 .Net Core 3 所需的更改。 刚刚更新,抱歉花了一秒钟。另外,这是我发现的 SO 帖子,详细说明了 .NET Core 3 的实现差异。***.com/a/60417327/2293177 我想可能是因为TokenRequirement,我不知道为什么但是HandleRequirementAsync的token字符串总是返回null,非常感谢你的帮助! 有几件事可能会导致这种情况。我首先检查您是否实际上将令牌作为 HTTP 请求的标头传递。其次,如果您按照字母实现我的代码,我使用的标题键可能与您自己的不匹配。标头是键值对,它们通常看起来像 "apiToken": "1234-abcd" "apiToken" 是键,"1234-abcd" 是值。如果您尝试通过针对我的示例搜索名称为“authToken”的标头来获取令牌,那么您将得到 null,b/c 令牌在此实例中的名称为“apiToken”。所以我会检查你是否使用了正确的请求标头键

以上是关于令牌无法存储在 .NET Core 中的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Asp Net Core Web App(不是 WebAPI)中存储令牌

使用 AspNetUserTokens 表在 ASP.NET Core Web Api 中存储刷新令牌

在 ASP.NET Core 2.1 Web 客户端中存储不记名令牌的位置

在 cookie 中存储 JWT 令牌后,如何在 ASP.NET Core 3.1 中破坏该 cookie 并获取信息

如何撤销存储在 Identity Server 数据库中的 asp net core 中的刷新令牌

具有用户权限的基于 JWT 令牌的授权 Asp.net core 2.0