【问题标题】:Token can't store in .NET Core令牌无法存储在 .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}

标签: c# asp.net-core jwt token access-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 的实现差异。stackoverflow.com/a/60417327/2293177
  • 我想可能是因为TokenRequirement,我不知道为什么但是HandleRequirementAsync的token字符串总是返回null,非常感谢你的帮助!
  • 有几件事可能会导致这种情况。我首先检查您是否实际上将令牌作为 HTTP 请求的标头传递。其次,如果您按照字母实现我的代码,我使用的标题键可能与您自己的不匹配。标头是键值对,它们通常看起来像 { "apiToken": "1234-abcd" } "apiToken" 是键,"1234-abcd" 是值。如果您尝试通过针对我的示例搜索名称为“authToken”的标头来获取令牌,那么您将得到 null,b/c 令牌在此实例中的名称为“apiToken”。所以我会检查你是否使用了正确的请求标头键
猜你喜欢
  • 2021-11-05
  • 2020-08-09
  • 2020-08-09
  • 2019-05-11
  • 2018-07-18
  • 2019-11-21
  • 2022-07-19
  • 2011-06-14
  • 2019-01-21
相关资源
最近更新 更多