ASP.NET Core 7 MVC:使用JSON Web令牌进行身份验证

huangapple go评论98阅读模式
英文:

ASP.NET Core 7 MVC : authentication using JSON web tokens

问题

我正在开发一个ASP.NET Core 7 MVC项目。我对ASP.NET Core和Web开发都比较新手。我几乎完成了应用程序的功能,但我还没有尝试实现应用程序的安全性。现在我正在尝试保护我的一些URL。我有一些在Node.js(express)中使用Json Web Token的经验。

我进行了一些研究,我成功地使用以下方法创建了Jwt令牌

private string CreateToken(string user)
{
    List<Claim> claims = new List<Claim>
    {
        new Claim(ClaimTypes.Name, user)
    };

    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration.GetSection("AppSettings:JwtSecret").Value!));
    var cred = new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature);
    var token = new JwtSecurityToken(
        claims: claims,
        expires: DateTime.Now.AddMinutes(60),
        signingCredentials: cred
    );

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

    return jwt;
}

然后在验证过程后将其发送回给用户

[HttpPost]
public IActionResult Authenticate(string user, string passwd)
{
    //验证逻辑
    if (someLogic)
    {
        string token = CreateToken(user);
        Response.Cookies.Append("TokenCookie", token);

        // 重定向用户到受保护的页面
        return RedirectToAction("Index", "Home");
    }
    else
    {
        TempData["ErrorLogin"] = "Invalid credentials";
        return RedirectToAction("Index", "Login");
    }         
}

根据我到目前为止所了解的情况,Startup.cs 文件的使用已不再需要,因此在我的 Program.cs 文件中添加了以下配置。

var builder = WebApplication.CreateBuilder(args);

// 向容器添加服务。
builder.Services.AddControllersWithViews();
builder.Services
    .AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            ValidateAudience = false,
            ValidateIssuer = false,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration.GetSection("AppSettings:JwtSecret").Value!))
        };
    });

var app = builder.Build();

// 配置HTTP请求管道。
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    // 默认的HSTS值为30天。您可能希望在生产场景中更改此值,详见 https://aka.ms/aspnetcore-hsts。
    app.UseHsts();
}

app.UseAuthorization();

app.UseHttpsRedirection();
app.UseStaticFiles();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Login}/{action=Index}/{id?}");

app.Run();

接下来的步骤将是在我想要保护的路由中使用[Authorize]属性,但当我访问受保护的URL时,我收到401错误。

我不知道应用程序正在使用的中间件是如何工作的,我也不确定它应该如何从操作中获取JSON。我应该在哪里附加JWT?Cookie是否是一个好方法?

我感到相当迷茫,所以我想听听别人的意见、反馈和建议。从这里我该做什么?我是否在正确的方向上前进?我是否应该回退或前进一些步骤?

英文:

I am developing an ASP.NET Core 7 MVC project. I am pretty new to ASP.NET Core and web development in general. I am almost done with the application functionality but I hadn't tried implementing the security of the application. Now I am trying to protect some of my urls. I have little bit of experience using Json Web Token in node js (express).

I have made some research and I managed to create the Jwt using the following method

private string CreateToken(string user)
{
    List&lt;Claim&gt; claims = new List&lt;Claim&gt;
    {
        new Claim(ClaimTypes.Name, user)
    };

    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration.GetSection(&quot;AppSettings:JwtSecret&quot;).Value!));
    var cred = new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature);
    var token = new JwtSecurityToken(
        claims: claims,
        expires: DateTime.Now.AddMinutes(60),
        signingCredentials: cred
    );

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

    return jwt;
}

and I send It back to the user after the validation process

[HttpPost]
public IActionResult Authenticate(string user,string passwd)
{
    //validation logic
    if (someLogic)
    {
        string token = CreateToken(user);
        Response.Cookies.Append(&quot;TokenCookie&quot;, token);

        // Redirect the user to a protected page
        return RedirectToAction(&quot;Index&quot;, &quot;Home&quot;);
    }
    else
    {
        TempData[&quot;ErrorLogin&quot;] = &quot;Invalid credentials&quot;;
        return RedirectToAction(&quot;Index&quot;, &quot;Login&quot;);
    }         
}

According to what I have read up until this point. The usage of the Startup.cs file is no longer required so in my Program.cs file I added the following configuration.

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services
    .AddAuthentication(options =&gt;
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(options =&gt;
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            ValidateAudience = false,
            ValidateIssuer = false,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration.GetSection(&quot;AppSettings:JwtSecret&quot;).Value!))
        };
    });

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler(&quot;/Home/Error&quot;);
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseAuthorization();

app.UseHttpsRedirection();
app.UseStaticFiles();

app.MapControllerRoute(
    name: &quot;default&quot;,
    pattern: &quot;{controller=Login}/{action=Index}/{id?}&quot;);

app.Run();

The following step would be using the [Authorize] attribute in one of the routes that I want to protect but I get a 401 when I hit the protected url.

I don't know how the middleware that the application is using works, I I am not sure how it is supposed to get the json from the action. Where do I attach the jwt? Are the cookies a good way?

I am pretty lost so I would like to get someone else's opinion, feedback, any advice. What can I do from here? Am I following the right way? Do I go some steps back or further?

答案1

得分: 0

I think I was facing the wrong direction all this time.

  • 首先,HTTP请求和响应不会保存任何信息以供将来与服务器进行交互。因此,一旦你将JWT发送回客户端,JWT不会自动添加到下一个请求中。
  • 客户端的任务是随请求一起发送JWT。因此,客户端需要一种方式来存储JWT,直到客户端发送请求。这就是使用cookie的时候。

我将JWT存储在客户端的cookie中,如下所示:

string token = CreateToken(user);

Response.Cookies.Append("jwt", token, new CookieOptions
{
    HttpOnly = true,
    Secure = true,
    SameSite = SameSiteMode.None
});

return RedirectToAction("Index", "Home");

这样JWT就会存储在用户的cookie中。

正如@MdFaridUddinKiron所说:

由于你已经将token写入cookie中,所以在读取标头时,你是否检查了标头是否包含你写入的token?此外,你是否能够从cookie中提取token?

我们需要一种方式从cookie中获取它并将其附加到请求标头。为了解决这个问题,我创建了一个中间件类,执行以下操作。

public class JwtCookieToHeaderMiddleware
{
    private readonly RequestDelegate _next;

    public JwtCookieToHeaderMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        // 检查是否存在“Authorization”标头
        if (!context.Request.Headers.ContainsKey("Authorization") && context.Request.Cookies.TryGetValue("jwt", out string jwtToken))
        {
            // 将“Authorization”与存储在cookie中的JWT一起添加
            context.Request.Headers.Add("Authorization", $"Bearer {jwtToken}");
        }

        // 调用管道中的下一个中间件
        await _next(context);
    }
}

Program.cs文件中,我们添加了这个中间件。

app.UseMiddleware<JwtCookieToHeaderMiddleware>();
app.UseAuthentication();
app.UseAuthorization();

这样,每次身份验证中间件运行时,自定义中间件将根据管道顺序首先发生。

英文:

I think I was facing the wrong direction all this time.

  • First of all HTTP requests and responses do not save any information for future interactions with the server. So once you send the jwt back to the client the jwt will not be magically added to the next request.
  • It's the client's job to send the jwt along with the request. Therefore the client needs a way to store the jwt until the client sends the request. Here's when the cookies come in.

I store the jwt on the client's cookies like so

string token = CreateToken(user);

Response.Cookies.Append(&quot;jwt&quot;, token, new CookieOptions
{
    HttpOnly = true,
    Secure = true,
    SameSite = SameSiteMode.None
});

return RedirectToAction(&quot;Index&quot;, &quot;Home&quot;);

This way the jwt will be stored on the user's cookies.

As said by @MdFaridUddinKiron

> As you have written token in cookie, so while reading from header have you checked if your header contains the token you have written? In addition, did you able to extract token from cookie also?

We need a way to take this from the cookies and attach it to the request header. In order to solve this I created a Middleware class that does the following.

public class JwtCookieToHeaderMiddleware
{
    private readonly RequestDelegate _next;

    public JwtCookieToHeaderMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        // Check if the &quot;Authorization&quot; header is present
        if (!context.Request.Headers.ContainsKey(&quot;Authorization&quot;) &amp;&amp; context.Request.Cookies.TryGetValue(&quot;jwt&quot;, out string jwtToken))
        {
            // Add &quot;Authorization&quot; along with the jwt stored on the cookies
            context.Request.Headers.Add(&quot;Authorization&quot;, $&quot;Bearer {jwtToken}&quot;);
        }

        // call next middleware in the pipeline
        await _next(context);
    }
}

In the Program.cs file we add the Middleware.

app.UseMiddleware&lt;JwtCookieToHeaderMiddleware&gt;();
app.UseAuthentication();
app.UseAuthorization();

This way every time the Authentication Middleware runs the Custom Middleware is going to happen first according to the pipeline order.

huangapple
  • 本文由 发表于 2023年8月5日 02:23:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/76838364.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定