使用JWT(无Cookie)的ASP.NET CORE令牌认证和授权——第1部分

目录

介绍

JWT(JSON Web令牌)

ASP.NET Core中的JWToken配置

用户模型类

创建令牌

步骤1

步骤2

步骤3

步骤4

令牌存储

中间件

自定义中间件app.Use()

中间件app.UseAuthentication()

自定义中间件代码

登录页面(Index.cshtml)

Home控制器

注销

控制器代码

登录演示项目

登录页面

LoginDemo.sln


介绍

本文演示如何在Asp.Net CORE中使用JWTJSON Web令牌)实现令牌认证和授权。本文中使用的方法不使用任何客户端cookie进行身份验证和授权。这意味着,Token不存储在客户端浏览器中,它完全由服务器端处理。由于本文主要关注实现ASP.NET CORE身份验证和授权,因此我们不会深入了解令牌配置和令牌创建。从实现的角度来看,仅给出了令牌配置和创建的简要说明。有许多文章详细解释了它。本文包括完整的代码和LoginDemo.sln项目。

在讨论主题之前,我们先简要介绍一下身份验证和授权。

身份验证:授予用户进入应用程序的访问权限/权限。这就像是允许一个人进入建筑物的访问/许可。

授权:这是在身份验证之后。仅向用户授予对应用程序的某些页面的权限。这就像是一个有权进入一栋有10层楼的建筑的人,只能进入2楼或4楼。

JWTJSON Web令牌)

正如它所说,JWToken是一个JSON格式的字符串值。为每个有效用户(身份验证)颁发JWToken。用户登录时只创建一次令牌。用户将在所有后续HTTP请求中使用该令牌进行授权,直到用户退出应用程序。

ASP.NET Core中的JWToken配置

我们不会详细介绍JWToken配置。有很多文章进行了详细介绍。使用Microsoft.AspNetCore.Authentication.JwtBearerMicrosoft.IdentityModel.Tokens配置JWT。这是在Startup.cs类的ConfigurationServices()方法中完成的。

您可以在下面的代码中看到,令牌配置中有两个部分,services.AddAuthentication()AddJwtBearer()

services.AddAuthentication():此部分用于配置我们将要使用的身份验证方案或机制。在这里,我们告诉ASP.NET Core使用JWT承载令牌认证。这非常重要,因为这稍后将在Configure()方法中使用它。

AddJwtBearer():在本节中,我们使用密钥,到期日期,消费者等配置令牌。密钥是加密和解密令牌。创建令牌时应使用相同的密钥,我们将在创建令牌主题中看到。

public void ConfigureServices(IServiceCollection services)
{
    services.AddSession(options => {
        options.IdleTimeout = TimeSpan.FromMinutes(60);
    });
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    
    //Provide a secret key to Encrypt and Decrypt the Token                                                                
    var SecretKey = Encoding.ASCII.GetBytes
         ("YourKey-2374-OFFKDI940NG7:56753253-tyuw-5769-0921-kfirox29zoxv");
    //Configure JWT Token Authentication
    services.AddAuthentication(auth =>
    {
        auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(token =>
    {
        token.RequireHttpsMetadata = false;
        token.SaveToken = true;
        token.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            //Same Secret key will be used while creating the token
            IssuerSigningKey = new SymmetricSecurityKey(SecretKey),
            ValidateIssuer = true,
            //Usually this is your application base URL
            ValidIssuer = "http://localhost:45092/",
            ValidateAudience = true,
            //Here we are creating and using JWT within the same application. 
            //In this case base URL is fine.
            //If the JWT is created using a web service then this would be the consumer URL.
            ValidAudience = "http://localhost:45092/",
            RequireExpirationTime = true,
            ValidateLifetime = true,
            ClockSkew = TimeSpan.Zero
        };
    });
}

用户模型类

我们需要一个模型类供用户登录。使用用户ID,密码和其他凭据为User创建模型类。在Models文件夹下创建一个User.cs类。

public class User
{
    public string USERID { get; set; }
    public string PASSWORD { get; set; }
    public string FIRST_NAME { get; set; }
    public string LAST_NAME { get; set; }
    public string EMAILID { get; set; }
    public string PHONE { get; set; }
    public string ACCESS_LEVEL { get; set; }
    public string READ_ONLY { get; set; }
}

创建令牌

步骤1

让我们创建一个TokenProvider.cs类,它将为用户创建/生成令牌。令牌只创建一次,并在所有后续请求中使用,直到用户注销。在解决方案的根文件夹下,创建一个类TokenProvider.cs

步骤2

在创建令牌之前,我们需要从登录页面获取UserID并检查用户是否存在于我们的数据库中。出于演示目的,用户列表是存储在列表中的硬编码值。在现实世界中,这将来自数据库或某些数据源。让我们在TokenProvider.cs类中添加一个属性(UserList)。此属性是用户数据存储,其具有很少的硬编码值。

//Using hard coded collection list as Data Store for demo purpose. 
//In reality, User data comes from Database or other Data Source.
private List UserList = new List
{
    new User { USERID = "jsmith@email.com", PASSWORD = "test", 
               EMAILID = "jsmith@email.com", FIRST_NAME = "John", 
               LAST_NAME = "Smith", PHONE = "356-735-2748", 
               ACCESS_LEVEL = "Director", READ_ONLY = "true" },
    new User { USERID = "srob@email.com", PASSWORD = "test", 
               FIRST_NAME = "Steve", LAST_NAME = "Rob", 
               EMAILID = "srob@email.com", PHONE = "567-479-8537", 
               ACCESS_LEVEL = "Supervisor", READ_ONLY = "false" },
    new User { USERID = "dwill@email.com", PASSWORD = "test", 
               FIRST_NAME = "DJ", LAST_NAME = "Will", 
               EMAILID = "dwill@email.com", PHONE = "599-306-6010", 
               ACCESS_LEVEL = "Analyst", READ_ONLY = "false" },
    new User { USERID = "JBlack@email.com", PASSWORD = "test", 
               FIRST_NAME = "Joe", LAST_NAME = "Black", 
               EMAILID = "JBlack@email.com", PHONE = "764-460-8610", 
               ACCESS_LEVEL = "Analyst", READ_ONLY = "true" }
};

步骤3

我们需要在令牌(授权)中为应用程序设置用户权限。在令牌中,我们需要告诉用户可以拥有的权限级别。用户权限创建为Claims。在创建令牌时,我们将在Claims Object集合中设置用户权限并将其分配给Token。这些Claims值将用于授予用户在控制器中的权限/授权。在MVC控制器动作方法中,我们将使用“ACCESS_LEVEL”“READ_ONLY”声明来设置用户权限。出于演示目的,用户声明(claims)是硬编码的。在这里,您可以连接到您的数据库并获得用户权限。

让我们添加一个方法(GetUserClaims())来获取用户权限级别并在TokenProvider.cs类中构建Claims对象集合。

//Using hard coded values in claims collection list as Data Store for demo. 
//In reality, User data comes from Database or other Data Source.
private IEnumerable GetUserClaims(User user)
{
    IEnumerable claims = new Claim[]
    {
        new Claim(ClaimTypes.Name, user.FIRST_NAME + " " + user.LAST_NAME),
        new Claim("USERID", user.USERID),
        new Claim("EMAILID", user.EMAILID),
        new Claim("PHONE", user.PHONE),
        new Claim("ACCESS_LEVEL", user.ACCESS_LEVEL.ToUpper()),
        new Claim("READ_ONLY", user.READ_ONLY.ToUpper())
    };
    return claims;
}

步骤4

现在是时候为用户创建令牌了。首先,从登录页面获取用户ID,并检查用户是否在上面声明的UserList集合属性中。如果用户ID在列表中,那么我们有一个注册用户。如果没有,则认证失败。不要发出令牌。

其次,从登录页面获取密码,并检查密码是否与UserList中的密码匹配。如果是,则为用户创建令牌。如果不是,则验证失败并且不创建/发出令牌。

要创建JWToken,我们将使用两个命名空间System.IdentityModel.Tokens.JwtMicrosoft.IdentityModel.Tokens。让我们使用JwtSecurityToken()类创建令牌(这里我没有介绍令牌创建的细节。有很多文章解释了JWT令牌的创建)。创建令牌时,用户声明(claim)值将加载到令牌“claim”属性中。我们调用上面的函数GetUserClaims()来加载用户的声明。令牌在LoginUser()方法中创建,该方法将UserIDPassword作为输入。

让我们创建一个函数LoginUser(),它将UserIDPassword作为TokenProvider.cs中的输入参数。

public string LoginUser(string UserID, string Password)
{
    //Get user details for the user who is trying to login
    var user = UserList.SingleOrDefault(x => x.USERID == UserID);
    
    //Authenticate User, Check if it’s a registered user in Database
    if (user == null)
        return null;
        
    //If its registered user, check user password stored in Database 
    //For demo, password is not hashed. Simple string comparison 
    //In real, password would be hashed and stored in DB. Before comparing, hash the password
    if (Password == user.PASSWORD)
    {
        
        //Authentication successful, Issue Token with user credentials
        //Provide the security key which was given in the JWToken configuration in Startup.cs
        var key = Encoding.ASCII.GetBytes
                  ("YourKey-2374-OFFKDI940NG7:56753253-tyuw-5769-0921-kfirox29zoxv"); 
        //Generate Token for user 
        var JWToken = new JwtSecurityToken(
            issuer: "http://localhost:45092/",
            audience: "http://localhost:45092/",
            claims: GetUserClaims(user),
            notBefore: new DateTimeOffset(DateTime.Now).DateTime,
            expires: new DateTimeOffset(DateTime.Now.AddDays(1)).DateTime,
            //Using HS256 Algorithm to encrypt Token
            signingCredentials: new SigningCredentials(new SymmetricSecurityKey(key), 
                                SecurityAlgorithms.HmacSha256Signature)
        );
        var token = new JwtSecurityTokenHandler().WriteToken(JWToken);
        return token;
    }
    else
    {
        return null;
    }
}

几点要考虑......

在创建令牌时,我们需要提供与在Startup.csJWToken配置 配置的相同安全密钥。

var key = Encoding.ASCII.GetBytes
          ("YourKey-2374-OFFKDI940NG7:56753253-tyuw-5769-0921-kfirox29zoxv");

issueraudience应该与Startup.csConfigureServices()方法中的配置的值相同。

最后,TokenProvider.cs类如下所示:

using LoginDemo.Models;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace LoginDemo
{
    public class TokenProvider
    {
        public string LoginUser(string UserID, string Password)
        {
            //Get user details for the user who is trying to login
            var user = UserList.SingleOrDefault(x => x.USERID == UserID);
            
            //Authenticate User, Check if it’s a registered user in Database 
            if (user == null)
                return null;
                
            //If its registered user, check user password stored in Database
            //For demo, password is not hashed. Its just a string comparision 
            //In reality, password would be hashed and stored in Database. 
            //Before comparing, hash the password again
            if (Password == user.PASSWORD)
            {
                //Authentication successful, Issue Token with user credentials 
                //Provide the security key which is given in 
                //Startup.cs ConfigureServices() method 
                var key = Encoding.ASCII.GetBytes
                ("YourKey-2374-OFFKDI940NG7:56753253-tyuw-5769-0921-kfirox29zoxv"); 
                //Generate Token for user 
                var JWToken = new JwtSecurityToken( 
                    issuer: "http://localhost:45092/",
                    audience: "http://localhost:45092/",
                    claims: GetUserClaims(user),
                    notBefore: new DateTimeOffset(DateTime.Now).DateTime,
                    expires: new DateTimeOffset(DateTime.Now.AddDays(1)).DateTime,
                    //Using HS256 Algorithm to encrypt Token  
                    signingCredentials: new SigningCredentials
                    (new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
                );
                var token = new JwtSecurityTokenHandler().WriteToken(JWToken);
                return token;
            }
            else
            {
                return null;
            }
        }
        
        //Using hard coded collection list as Data Store for demo. 
        //In reality, User details would come from Database 
        private List UserList = new List
        {
            new User { USERID = "jsmith@email.com", 
            PASSWORD = "test", EMAILID = "jsmith@email.com", 
            FIRST_NAME = "John", LAST_NAME = "Smith", 
            PHONE = "356-735-2748", ACCESS_LEVEL = "Director", 
            READ_ONLY = "true" },
            new User { USERID = "srob@email.com", PASSWORD = "test", 
            FIRST_NAME = "Steve", LAST_NAME = "Rob", 
            EMAILID = "srob@email.com", PHONE = "567-479-8537", 
            ACCESS_LEVEL = "Supervisor", READ_ONLY = "false" },
            new User { USERID = "dwill@email.com", PASSWORD = "test", 
            FIRST_NAME = "DJ", LAST_NAME = "Will", 
            EMAILID = "dwill@email.com", PHONE = "599-306-6010", 
            ACCESS_LEVEL = "Analyst", READ_ONLY = "false" },
            new User { USERID = "JBlack@email.com", PASSWORD = "test", 
            FIRST_NAME = "Joe", LAST_NAME = "Black", 
            EMAILID = "JBlack@email.com", PHONE = "764-460-8610", 
            ACCESS_LEVEL = "Analyst", READ_ONLY = "true" }
        };
        
        //Using hard coded collection list as Data Store for demo. 
        //In reality, User data comes from Database or other Data Source 
        private IEnumerable GetUserClaims(User user)
        {
            IEnumerable claims = new Claim[]
                    {
                    new Claim(ClaimTypes.Name, user.FIRST_NAME + " " + user.LAST_NAME),
                    new Claim("USERID", user.USERID),
                    new Claim("EMAILID", user.EMAILID),
                    new Claim("PHONE", user.PHONE),
                    new Claim("ACCESS_LEVEL", user.ACCESS_LEVEL.ToUpper()),
                    new Claim("READ_ONLY", user.READ_ONLY.ToUpper())
                    };
            return claims;
        }
    }
}

令牌存储

现在我们已经对用户进行了身份验证并为该用户发出了令牌,我们需要将此令牌存储在某处,直到用户从应用程序注销。这是必需的,因为成功登录后需要在每个后续HTTP请求中传递令牌。如上所述,我们不会使用任何客户端(浏览器)端cookie来存储令牌。

相反,我们将在服务器端将令牌存储在用户SESSION中。创建SESSION变量并将令牌存储在其中。成功登录后,对于每个后续请求,我们将从会话变量中获取令牌并插入到传入的HTTP请求中。

我们将在下面的HomeController操作方法中这样做。从TokenProvider.cs获取令牌,创建一个Session对象JWToken并存储令牌。

HomeController.cs,有一个LoginUser操作方法。从Index.cshtml,用户将输入用户ID和密码,并将页面提交到HomeController.cs中的LoginUser操作方法。在 LoginUser控制器操作方法中,我们将令牌添加到会话对象名称JWToken中。如下

HttpContext.Session.SetString("JWToken", userToken);

中间件

这是整个实现的关键部分。这部分更多的是一个概念和几行代码。我们将在这里做两件事:

  1. 将令牌插入HTTP请求
  2. 将用户声明加载到HTTP请求中

让我们先了解这个概念。为了保持简单,请耐心等待。

身份验证和授权通过HTTP请求处理,为此:

  • 令牌应该是HTTP请求的一部分,它应该来自HTTP请求头。
  • ClaimsPrincipleClaimsIdentityHttpContext.User.Identity)对象是从当前HTTP上下文创建的。
  • 用户声明(User Claims)HTTP请求头中读取并加载到HTTP声明标识(Claims Identity)对象中。
  • 换句话说,授权是通过传入的HTTP请求完成的,而不是直接从令牌中读取。
  • 通过这样做,HTTP请求本身就是为该用户授权的。

要实现上述目标:

  • 我们需要将令牌(存储在用户会话变量“JWToken”中)插入到每个传入的HTTP请求中。
  • Token读取用户声明值并将其加载到HTTP上下文声明原则(Context Claims Principle)对象中。
  • 如果令牌在会话变量“JWToken”中不可用,则HTTP请求头授权将为空。在这种情况下,不会在HTTP上下文中设置该用户的声明原则(Claims Principle)。这将拒绝用户的许可。

下图给出了我们如何将令牌插入HTTP头并在HTTP上下文中设置声明原则的想法。

自定义中间件app.Use()

拥有此自定义中间件的主要思想是将令牌插入到传入的HTTP请求中。现在,我们已经记录了存储在Session变量“ JWToken” 中的用户令牌,我们需要将该令牌插入到所有后续的传入HTTP请求中。为此,我们将在ASP.NET Core中间件中编写几行代码。这只是HTTP管道。在Startup.cs Configure()方法中添加了自定义中间件。

PS:在用户登录期间仅创建一次令牌。

中间件app.UseAuthentication()

现在我们需要验证令牌并将声明加载到HTTP请求上下文。UseAuthentication()为我们完成这项工作。在HTTP请求到达MVC控制器之前,UseAuthentication()执行以下操作,

  • 使用AddJwtBearer()配置中提供的密钥解密和验证令牌(在Startup.cs中的ConfigureServices()方法下)
  • HTTP请求上下文中设置User对象
  • 最后从Token中读取Claims值并将其加载到HttpContext.User.Identity对象

自定义中间件代码

Startup.cs,将以下代码添加到Configure()方法。在app.UseCookiePolicy()之后添加以下代码。这里的代码执行顺序很重要。

app.UseSession();
//Add JWToken to all incoming HTTP Request Header
app.Use(async (context, next) =>
{
    var JWToken = context.Session.GetString("JWToken");
    if (!string.IsNullOrEmpty(JWToken))
    {
        context.Request.Headers.Add("Authorization", "Bearer " + JWToken);
    }
    await next();
});
//Add JWToken Authentication service
app.UseAuthentication();

我们来看看代码:

  • app.UseSession()是使用Session对象的配置。
  • 要编写自定义中间件,请使用app.Use()
  • 首先,在将它插入HTTP Request之前,我们需要Token。我们已将令牌存储在Session中。从会话变量JWToken” 获取令牌。
var JWToken = context.Session.GetString("JWToken");
  • 下一行检查令牌在Session中是否可用。如果不是,则用户未经过身份验证。因此拒绝用户权限。
  • 如果令牌存在于Session变量JWToken中,那么我们有经过身份验证的用户。
  • 现在我们需要将令牌添加到HTTP请求中(请记住,用户身份是通过HTTP请求创建的)。以下代码将令牌添加到所有传入的HTTP请求。
context.Request.Headers.Add("Authorization", "Bearer " + JWToken);
  • 注意,我们将令牌添加到HTTP请求的Authorization头中。是的,将标记添加到Authorization头是很重要的,标记应该与关键字Bearer连接起来。
  • 下一行代码是app.UseAuthentication()
    1. 这行代码将查找在ConfigureServices()方法中配置的身份验证机制。在我们的ConfigureService()中,我们使用了AddJwtBearer配置,这是Microsoft.AspNetCore.Authentication namespace的一部分。
    2. AddJWtBeared()里面,我们有令牌配置,包括密钥,到期日期等,
    3. HTTP请求到来时,app.UseAuthentication()将在HTTP请求中查找Authorization头。它将读取存储在Authorization头中的值并将其传递给Microsoft.AspNetCore.AuthenticationMicrosoft.AspNetCore.Authentication将根据我们为令牌设置的配置来评估和验证令牌。这包括使用我们在配置中给出的密钥解密令牌,并从令牌读取声明并将声明加载到HttpContext.User.Identity对象。这里HTT上下文本身是经过身份验证和授权的。
    4. 此完整执行仅对一个HTTP请求(该特定传入请求)有效。我们必须为所有后续HTTP请求执行此操作。这就是我们将令牌存储在会话变量中并将令牌分配给所有后续传入HTTP请求的HTTP请求Authorization头的原因。所有传入的HTTP请求和传出的HTTP响应都通过Startup.cs Configure()方法中的HTTP管道传递。

最后,Startup.cs Configure()方法如下所示,

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }
    app.UseStaticFiles();
    app.UseCookiePolicy();

    //Addd User session
    app.UseSession();

    //Add JWToken to all incoming HTTP Request Header
    app.Use(async (context, next) =>
    {
        var JWToken = context.Session.GetString("JWToken");
        if (!string.IsNullOrEmpty(JWToken))
        {
            context.Request.Headers.Add("Authorization", "Bearer " + JWToken);
        }
        await next();
    });
    //Add JWToken Authentication service
    app.UseAuthentication();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

登录页面(Index.cshtml

现在让我们创建一个带有用户ID和密码文本框的简单登录页面(Index.cshtml)。将User.cs模型添加到查看页面。在这里,您可以看到检查用户是否经过身份验证的IF条件User.Identity.IsAuthenticatedUser对象是System.Security.Claims的一部分,由中间件在HTTP上下文中设置。如果用户已通过身份验证,我们会在声明标识名称属性中显示用户名。如果没有,那么我们要求用户登录。

@model LoginDemo.Models.User
@{
    ViewData["Title"] = "Home Page";
}
<div style="padding-top:50px;"></div>
<div style="padding-top:50px;">
    @if (User.Identity.IsAuthenticated)
    {
        <div class="row">
            You are Logged in as <span style="font-size:large;color:forestgreen;">@User.Identity.Name</span>
        </div>
        <div class="row" style="padding-top:50px;">
            @Html.ActionLink("Log Off", "Logoff", "Home", null, new { @class = "btn btn-primary btn-lg rph-login-button" })
        </div>
    }
    else
    {
        <div class="row">
            <div class="col-lg-4 col-md-4 col-sm-4">
                <div>
                    @using (Html.BeginForm("LoginUser", "Home", FormMethod.Post, new { role = "form" }))
                    {
                        <div>
                            @Html.AntiForgeryToken()
                            <div>
                                <label>User ID</label><br />
                            </div>
                            <div>
                                @Html.TextBoxFor(m => m.USERID, new {@class = "form-control txtbox"})
                            </div>
                            <div style="padding-top:20px;"></div>
                            <div>
                                <label>Password</label><br />
                            </div>
                            <div>
                                @Html.PasswordFor(m => m.USERID, new {@class = "form-control txtbox"})
                            </div>
                        </div>
                        <div class="padding-left:35%;width:40%;">
                            <div class="padding-top:20px;">
                                <input class="btn btn-primary btn-lg rph-login-button" type="submit" value="Login"/>
                            </div>
                        </div>
                    }
                </div>
            </div>
            <div class="col-lg-8 col-md-8 col-sm-8">
                <div style="padding-top:50px;">
                    <div><b>Please login with any of the below User ID, Password is span style="font-size:large;color:forestgreen;">test</span> for all Users</b></div>
                    <div style="padding-top:10px;">
                        <ui style="list-style: none;">
                            <li>jsmith@email.com  -  Director, Read Only - true</li>
                            <li>srob@email.com  -  Supervisor, Read Only - false</li>
                            <li>dwill@email.com  -  Analyst, Read Only - false</li>
                            <li>JBlack@email.com  -  Analyst, Read Only - true</li>
                        </ui>
                    </div>
                </div>
            </div>
        </div>
    }
</div>

Home控制器

让我们在HomeController.cs添加两个Action方法。一个用于Index (登录)页面,另一个用于提交登录页面。

public IActionResult Index()
{
    return View();
}

public IActionResult LoginUser(User user)
{
    TokenProvider _tokenProvider = new TokenProvider();
    //Authenticate user
    var userToken = _tokenProvider.LoginUser(user.USERID.Trim(), user.PASSWORD.Trim());
    if (userToken != null)
    {
        //Save token in session object
        HttpContext.Session.SetString("JWToken", userToken);
    }
    return Redirect("~/Home/Index");
}

Action方法LoginUser(User user)从登录页面获取用户标识和密码值。下面的行通过检查数据存储中的用户ID和密码来进行身份验证。

var userToken = _tokenProvider.LoginUser(user.USERID.Trim(), user.PASSWORD.Trim());

下一行检查是否有令牌发出TokenProvider()。如果是,则将令牌保存在用户Session变量JWToken中。

if (userToken != null)
{
    //Save token in session object
    HttpContext.Session.SetString("JWToken", userToken);
}

然后将页面重定向到Index.cshtml

return Redirect("~/Home/Index");

在页面重定向期间,我们已将令牌存储在会话对象中。现在页面重定向通过Startup.csHTTP管道。现在,自定义中间件将停止HTTP请求并将令牌插入HTTP请求头Authorization。有关更多详细信息,请参阅中间件

如果令牌在会话变量“JWToken”中不可用,则HTTP请求头Authorization将为空。在这种情况下,不会为该用户设置HTTP上下文。重定向将要求用户登录。

注销

让我们注销用户。如果没有令牌,则无法为用户设置HTTP上下文。因此,从会话对象中删除令牌。要从会话中删除令牌,请清除该用户的会话并重定向到另一个控制器操作。

添加控制器操作方法Logoff()。清除用户的会话并重定向到Index操作方法。重定向到另一个控制器操作方法很重要。我们明白为什么?是的,在Logoff()操作方法中我们返回一个View()而不是Redirect()。在这种情况下,视图页面将呈现给浏览器,用户仍然可以访问该页面,User.Identity.IsAuthenticated仍然是true。当asp.net执行控制器操作方法时,它处于HTTP RESPONSE的过程中。这意味着它已经通过了HTTP REQUEST。用户声明原则在HTTP请求中设置。通过注销用户,我们还需要清除该用户的声明原则。单独清除会话是不够的。所以我们需要再次通过HTTP管道。重定向到另一个控制器通过HTTP管道,它将在会话变量“JWToken”中查找令牌。但是我们已经清除了会话,令牌不再在会话中了。如果没有令牌,则无法在HTTP上下文中设置声明原则。

public IActionResult Logoff()
{
    HttpContext.Session.Clear();
    return Redirect("~/Home/Index");
}

控制器代码

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using LoginDemo.Models;
using System.Security.Claims;
using Microsoft.AspNetCore.Http;

namespace LoginDemo.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        public IActionResult LoginUser(User user)
        {
            TokenProvider _tokenProvider = new TokenProvider();
            var userToken = _tokenProvider.LoginUser(user.USERID.Trim(), user.PASSWORD.Trim());
            if (userToken != null)
            {
                //Save token in session object
                HttpContext.Session.SetString("JWToken", userToken);
            }
            return Redirect("~/Home/Index");
        }

        public IActionResult Logoff()
        {
            HttpContext.Session.Clear();
            return Redirect("~/Home/Index");
        }
    }
}

登录演示项目

登录页面


  

LoginDemo.sln

 

原文地址:https://www.codeproject.com/Articles/5160941/ASP-NET-CORE-Token-Authentication-and-Authorizatio

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值