【问题标题】:aspnet core jwt token as get paramaspnet 核心 jwt 令牌作为获取参数
【发布时间】:2025-12-02 04:10:01
【问题描述】:

我正在开发一个 aspnet core 2 web api 项目,它的主要消费者是一个 vue web 应用程序。

Api 使用 jwt 令牌作为身份验证方法,一切正常。

现在我已经提交了所有代码来管理数据库的图像存储和检索,但是我在从数据库中获取图像时遇到了问题。

所有路由(登录除外)都在身份验证之后,因此为了检索图像,我在请求标头中传递了令牌(通常)

这会阻止我使用图像源标签来实际显示图像,如

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

相反,我必须编写一些 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)
    })
}

这可行,但不知何故这是一个不必要的复杂化。

我在 AspNet Core Identity 中看到

或者,您可以从其他地方获取令牌,例如不同的标头,甚至是 cookie。在这种情况下,处理程序将使用提供的令牌进行所有进一步处理

(摘自Andre Lock博客this文章) 你也可以看到检查aspnet core security code,上面写着

让应用程序有机会从不同位置查找、调整或拒绝令牌

但我找不到任何关于如何使用此功能并传递自定义令牌的示例。

所以,我的问题是:是否有人知道如何将自定义令牌(可能从 get 参数中读取)传递给身份提供者(甚至可能仅适用于某些已定义的路由)?


感谢serpent5的正确回答。

如果有人有兴趣,从 url 参数读取令牌并将其传递给验证的完整代码如下

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;
            }
        };
    });

【问题讨论】:

  • 想知道在图像src 属性中附加不记名令牌是否是个好主意。不确定,但我认为它可以用来劫持和查看您无权查看的图片
  • @adeel41 从理论上讲,是的:如果我可以获得用户令牌,我可以使用它来冒充他并访问他的数据(图像和其他),但要访问用户令牌,必须要么具有 phisical访问用户的计算机或成为中间人 https 和非常短的令牌寿命(在这种情况下为 1 小时)应该可以降低这两种风险

标签: c# asp.net-core jwt asp.net-core-2.0


【解决方案1】:

这可以使用连接到提供给AddJwtBearerJwtBearerOptions 实例的JwtBearerEvents 来处理。具体来说,可以实现一个OnMessageReceived 事件来提供令牌本身。这是一个例子:

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;
            }
        };
    })

您可以在source code 中看到它是如何使用的:

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

// ...

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

【讨论】:

    【解决方案2】:

    您的应用程序可以提供此功能,例如使用正确的凭据登录。 (前端 -> 登录正确 -> 后端发回 JWT 令牌。)

    然后,您可以将后端提供给您的令牌存储在 cookie/localstorage 中。

    每次您将请求发送回 API 时,只需从 cookie/localstorage 中检索您的令牌并将其添加到请求标头中。

    我将向您展示如何添加将处理令牌生成和验证的中间件的示例。

    appsettings.conf

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

    密钥用于生成唯一的 JWT 令牌,应单独存储在机器上,这仅用于示例目的

    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; }
    }
    

    一个类,它将为我们提供令牌生成的基本信息。 “路径”可以更改为您要检索令牌的任何路径。

    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);
        }
    }
    

    这是中间件部分。您必须向您在TokenProviderOptions 中定义的Path 发送一个标头类型为application/x-www-form-urlencoded 和2 个字段usernamepassword 的POST 请求。

    如果检查通过,您将获得一个 jwt 令牌。

    最后是 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();
        }
    }
    

    我省略了冗余代码。这会添加您的自定义中间件并配置应用以使用 JWT 令牌。

    您所要做的就是更改提到的自定义参数,使用 'token': tokenValue 签署您的请求,然后就可以了!

    我在这里有一个可用的后端模板:https://github.com/BusschaertTanguy/dotnet_core_backend_template 仔细检查一切。

    希望对您有所帮助!

    【讨论】:

      最近更新 更多