简体   繁体   English

aspnet核心jwt令牌作为get param

[英]aspnet core jwt token as get param

I'm working an an aspnet core 2 web api project, which main consumer is a vue web app. 我正在开发一个aspnet core 2 web api项目,主要消费者是一个vue web应用程序。

Api uses jwt tokens as authentication method, and everything works fine. Api使用jwt令牌作为身份验证方法,一切正常。

Now i've put up all code to manage image storage and retrieval to/from database, but i have a problem in getting images out of db. 现在我已经提出了所有代码来管理数据库的图像存储和检索,但我在从db中获取图像时遇到了问题。

All routes (except login) is behind authentication, so to retrieve an image i have pass token within request header (as usually) 所有路由(登录除外)都在身份验证之后,因此要检索图像,我在请求标头中有通过令牌(通常)

This prevents me from using image source tag to actually display the image, as in 这使我无法使用图像源标签实际显示图像,如

<img src="/api/images/27" />

instead, i have to write some javascript code to request the image and place the content in the image tag, something like 相反,我必须编写一些javascript代码来请求图像并将内容放在图像标记中,例如

// extracted from vue code (not my code, i'm the backend guy)
getImage() {
    this.$http.get('api/images/27', {headers: {'Authorization': 'Bearer ' + this.token},responseType: 'blob'})
    .then(response => {
        return response.blob()
    }).then(blob => { 
        this.photoUrl = URL.createObjectURL(blob)
    })
}

This works, but it's somehow an unnecessary complication. 这有效,但它在某种程度上是一种不必要的复杂化。

I see in AspNet Core Identity that 我在AspNet Core Identity中看到了这一点

Alternatively, you could obtain the token from somewhere else, such as a different header, or even a cookie. 或者,您可以从其他位置获取令牌,例如不同的标头,甚至是cookie。 In that case, the handler will use the provided token for all further processing 在这种情况下,处理程序将使用提供的令牌进行所有进一步处理

(extracted from this article from Andre Lock blog) as you can also see checking aspnet core security code , where it says (摘自Andre Lock博客的这篇文章),你也可以看到检查aspnet核心安全代码

Give application opportunity to find from a different location, adjust, or reject token 为应用程序提供从不同位置查找,调整或拒绝令牌的机会

But i can't find any example on how to use this functionality and pass a custom token. 但我找不到任何关于如何使用此功能并传递自定义令牌的示例。

So, my question is: does anyone have any clue on how to pass a custom token (maybe read from a get parameter) to identity provider (maybe even only for some defined routes) ? 所以,我的问题是:有没有人知道如何将自定义令牌(可能从get参数读取)传递给身份提供者(甚至可能只针对某些已定义的路由)?


Thanks to serpent5 for his correct answer. 感谢serpent5的正确答案。

If anyone is interested, complete code for reading token from url param and passing it to validation is the following 如果有人感兴趣,从url param读取令牌并将其传递给验证的完整代码如下

service.AddAuthentication(...)
    .AddJwtBearer(options =>
        // ...
        options.Events = new JwtBearerEvents
        {
            OnMessageReceived = ctx =>
            {
                // replace "token" with whatever your param name is
                if (ctx.Request.Method.Equals("GET") && ctx.Request.Query.ContainsKey("token"))
                    ctx.Token = ctx.Request.Query["token"];
                return Task.CompletedTask;
            }
        };
    });

This can be handled using the JwtBearerEvents that is connected to the JwtBearerOptions instance provided to AddJwtBearer . 这可以使用被处理JwtBearerEvents连接到JwtBearerOptions提供给实例AddJwtBearer Specifically, there's an OnMessageReceived event that can be implemented to provide the token itself. 具体来说,可以实现一个OnMessageReceived事件来提供令牌本身。 Here's an example: 这是一个例子:

services.AddAuthentication(...)
    .AddJwtBearer(jwtBearerOptions =>
    {
        // ...

        jwtBearerOptions.Events = new JwtBearerEvents
        {
            OnMessageReceived = ctx =>
            {
                // Access ctx.Request here for the query-string, route, etc.
                ctx.Token = "";
                return Task.CompletedTask;
            }
        };
    })

You can see how this is used in the source code : 您可以在源代码中看到它的使用方式:

// event can set the token
await Events.MessageReceived(messageReceivedContext);

// ...

// If application retrieved token from somewhere else, use that.
token = messageReceivedContext.Token;

Your application can provide this, by for example login in with the correct credentials. 您的应用程序可以提供此功能,例如使用正确的凭据登录。 ( Frontend -> Login Correct -> Backend sends back the JWT Token.) (前端 - >登录正确 - >后端发回JWT令牌。)

You then can store the token that your backend gave you in a cookie/localstorage. 然后,您可以将后端提供给您的令牌存储在cookie / localstorage中。

Everytime you send a request back to the API, just retrieve your token from the cookie/localstorage and add it to the request header. 每次向API发送请求时,只需从cookie / localstorage中检索令牌并将其添加到请求标头中。

I'll show u an example of how to add middleware that will handle the token generation & validation. 我将向您展示如何添加将处理令牌生成和验证的中间件的示例。

appsettings.conf appsettings.conf

{
  "Secret": {
    "Key": "abcdefghijklmnop123456789"
  }
}

the secret key is used to generate a unique JWT token, should be stored seperatly on the machine, this is just for example purposes 密钥用于生成唯一的JWT令牌,应该单独存储在机器上,这只是为了示例目的

TokenProviderOptions.cs TokenProviderOptions.cs

public class TokenProviderOptions
{
    public string Path { get; set; } = "/token";
    public string Issuer { get; set; }
    public string Audience { get; set; }
    public TimeSpan Expiration { get; set; } = TimeSpan.FromHours(1);
    public SigningCredentials SigningCredentials { get; set; }
}

A class that will provide us with basic info for the token generation. 一个类,它将为我们提供令牌生成的基本信息。 The "path" can be changed to any path you want to retrieve the token. “路径”可以更改为您要检索令牌的任何路径。

TokenProviderMiddleware.cs TokenProviderMiddleware.cs

public class TokenProviderMiddleware
{
    private readonly RequestDelegate _next;
    private readonly TokenProviderOptions _options;
    private readonly IAccountService _accountService;

    public TokenProviderMiddleware(RequestDelegate next, IOptions<TokenProviderOptions> options, IAccountService accounteService)
    {
        _next = next;
        _options = options.Value;
        _accountService = accounteService;
    }

    public Task Invoke(HttpContext context)
    {
        //Check path request
        if (!context.Request.Path.Equals(_options.Path, StringComparison.Ordinal)) return _next(context);

        //METHOD: POST && Content-Type : x-www-form-urlencode
        if (context.Request.Method.Equals("POST") && context.Request.HasFormContentType)
            return GenerateToken(context);


        context.Response.StatusCode = 400;
        return context.Response.WriteAsync("Bad Request");
    }

    private async Task GenerateToken(HttpContext context)
    {
        var username = context.Request.Form["username"];
        var password = context.Request.Form["password"];

        var identity = await GetIdentity(username, password);

        if (identity == null)
        {
            context.Response.StatusCode = 400;
            await context.Response.WriteAsync("Invalid username or password");
            return;
        }

        var now = DateTime.UtcNow;

        var claims = new Claim[]
        {
            new Claim(JwtRegisteredClaimNames.Sub, username),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            new Claim(JwtRegisteredClaimNames.Iat, now.Second.ToString(), ClaimValueTypes.Integer64)
        };

        var jwt = new JwtSecurityToken(
            issuer: _options.Issuer,
            audience: _options.Audience,
            claims: claims,
            notBefore: now,
            expires: now.Add(_options.Expiration),
            signingCredentials: _options.SigningCredentials);

        var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);

        var response = new
        {
            access_token = encodedJwt,
            expires_in = (int)_options.Expiration.TotalSeconds,
            username = username
        };

        context.Response.ContentType = "application/json";
        await context.Response.WriteAsync(JsonConvert.SerializeObject(response,
            new JsonSerializerSettings { Formatting = Formatting.Indented }));
    }

    private Task<ClaimsIdentity> GetIdentity(string username, string password)
    {
        //THIS STEP COULD BE DIFFERENT, I HAVE AN ACCOUNTSERVICE THAT QUERIES MY DB TO CHECK THE USER CREDENTIALS
        var auth = _accountService.Login(username, password).Result;
        return auth
            ? Task.FromResult(new ClaimsIdentity(new GenericIdentity(username, "Token"), new Claim[] { }))
            : Task.FromResult<ClaimsIdentity>(null);
    }
}

This is the middleware part. 这是中间件部分。 you've got to send a POST request with header type application/x-www-form-urlencoded and 2 fields username and password to the Path you've defined in your TokenProviderOptions . 您必须向标头类型application/x-www-form-urlencoded发送POST请求,并将2个字段的usernamepassword发送到您在TokenProviderOptions定义的Path

If the checks pass, you will get a jwt token back. 如果检查通过,您将获得一个jwt令牌。

And finally here's the Startup.cs 最后这里是Startup.cs

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; set; }

    public void ConfigureServices(IServiceCollection services)
    {
        //Mvc
        services.AddMvc();

        //...

        //Authentication
        services.AddAuthentication()
            .AddJwtBearer(jwt =>
            {
                var signingKey =
                    new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration.GetSection("Secret:Key").Value));

                jwt.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = signingKey,

                    ValidateIssuer = true,
                    ValidIssuer = "2CIssuer",

                    ValidateAudience = true,
                    ValidAudience = "2CAudience",

                    ValidateLifetime = true,

                    ClockSkew = TimeSpan.Zero
                };
            });

        //Authorization
        services.AddAuthorization(auth =>
        {
            auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme).RequireAuthenticatedUser().Build());
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        //...

        //Authentication
        var signingKey =
            new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration.GetSection("Secret:Key").Value));

        var options = new TokenProviderOptions
        {
            Audience = "2CAudience",
            Issuer = "2CIssuer",
            SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256)
        };

        app.UseAuthentication();

        //JWT
        app.UseMiddleware<TokenProviderMiddleware>(Options.Create(options));

        //Mvc
        app.UseMvc();
    }
}

I've left out the redundant code. 我遗漏了冗余代码。 This adds your custom middleware en configures the app to use JWT tokens. 这会添加您的自定义中间件,将应用程序配置为使用JWT令牌。

All you have to do is change the custom params mentioned, sign your request with 'token': tokenValue and you're good! 您所要做的就是更改上面提到的自定义参数,用'token'签署您的请求:tokenValue,你很好!

I've a working backend template over here: https://github.com/BusschaertTanguy/dotnet_core_backend_template to double check everything. 我在这里有一个工作后端模板: https//github.com/BusschaertTanguy/dotnet_core_backend_template来仔细检查所有内容。

Hope it helped! 希望它有所帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM