ASP.NET Core高级之认证与授权(二)--JWT认证前后端完整实现

这篇具有很好参考价值的文章主要介绍了ASP.NET Core高级之认证与授权(二)--JWT认证前后端完整实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

阅读本文你的收获

  1. 了解JWT身份认证的流程
  2. 了解基于JWT身份认证和Session身份认证的区别
  3. 学习如何在ASP.NET Core WebAPI项目中封装JWT认证功能

在上文ASP.NET Core高级之认证与授权(一)–JWT入门-颁发、验证令牌中演示了JWT认证的一个入门案例,本文是一个基于JWT认证的完整的前后端实现代码案例。

一、基于JWT的用户认证

JWT身份认证的流程

在认证的时候,当用户用他们的凭证成功登录以后,一个JSON Web Token将会被返回。此后,token就是用户凭证了,你必须非常小心以防止出现安全问题。一般而言,你保存令牌的时候不应该超过你所需要它的时间。

无论何时用户想要访问受保护的路由或者资源的时候,用户代理(通常是浏览器)都应该带上JWT,典型的,通常放在Authorization header中,用Bearer schema。

ASP.NET Core高级之认证与授权(二)--JWT认证前后端完整实现,# ASP.NET Core,asp.net,后端,.net core上图流程说明:

  1. 用户携带用户名和密码请求认证
  2. 服务器校验用户账号密码,成功则提供一个token给客户端
  3. 客户端存储token,并且在随后的每一次请求中都带着它
  4. 服务器校验token有效则返回数据,无效则返回401状态码;

二、基于JWT身份认证和Session身份认证的区别

基于Session的身份认证的缺点

ASP.NET Core高级之认证与授权(二)--JWT认证前后端完整实现,# ASP.NET Core,asp.net,后端,.net core

  • 会话信息会占用服务器

    每次用户认证通过以后,服务器需要创建一条记录保存用户信息,通常是在内存中,随着认证通过的用户越来越多,服务器的在这里的开销就会越来越大。

  • 难以扩展

    由于Session是在服务器的内存中的,这就带来一些扩展性的问题。

  • 跨域共享难

    当我们想要扩展我们的应用,让我们的数据被多个移动设备使用时,我们必须考虑跨资源共享(如使用Redis)问题。当使用AJAX调用从另一个域名下获取资源时,我们可能会遇到禁止请求的问题。

  • 安全性差

    客户端要存Cookie来保存SessionId,所以 用户很容易受到CSRF攻击。

基于JWT令牌的身份认证的优缺点

ASP.NET Core高级之认证与授权(二)--JWT认证前后端完整实现,# ASP.NET Core,asp.net,后端,.net core
优点:

  • 简单轻巧

    JWT生成的token字符串是轻量级,json风格,比较简单。

  • 减轻服务器压力

    它是无状态的,JWT方式将用户状态分散到了客户端中,服务器或者Session中不会存储任何用户信息。明显减轻服务端的内存压力。

  • 容易做分布式

    没有会话信息意味着应用程序可以根据需要扩展和添加更多的机器,而不必担心用户登录的位置;

缺点:

  • JWT token一旦签发,无法修改
  • 无法更新token有效期,用户登录状态刷新较难实现
  • 无法销毁一个token,服务端不能对用户状态进行绝对控制
  • 不包含权限控制

三、JWT前后端完整实现关键代码

开发环境:

操作系统: Windows 10 专业版
平台版本是:.NET 6
开发框架:ASP.NET Core WebApi、Vue2+ElementUI
开发工具:Visual Studio 2022

1. JWT的选项配置

在appsetting.json文件中,配置JWT选项参数,这样做的好处是,使用者在发布后任然可以修改JWT的参数,如过期时间,密钥等。

  "JWTTokenOption": {
    "Issuer": "WLW",                        //Token发布者
    "Audience": "EveryTestOne",             //Token接受者
    "IssuerSigningKey": "WLW!@#%^99825949", //秘钥可以构建服务器认可的token;签名秘钥长度最少16
    "AccessTokenExpiresMinutes": "30"       //过期时间30分钟
  }

为了在ASP.NET Core WebAPI项目中读出JWT选项参数,首先定义一个用于保存JWT选项的模型类:

/// <summary>
/// 用来保存jwt的配置信息
/// </summary>
public class JwtTokenOption
{
    /// <summary>
    /// Token发布者
    /// </summary>
    public string Issuer { get; set; }
    /// <summary>
    /// oken接受者
    /// </summary>
    public string Audience { get; set; }
    /// <summary>
    /// 秘钥
    /// </summary>
    public string IssuerSigningKey { get; set; }
    /// <summary>
    /// 过期时间
    /// </summary>
    public int AccessTokenExpiresMinutes { get; set; }
}

在Program.cs中通过以下方式,读取JWT配置选项:

//获取jwt配置项
var jwtTokenConfig = builder.Configuration.GetSection("JWTTokenOption").Get<JwtTokenOption>();
builder.Services.AddSingleton(jwtTokenConfig); //注册单例服务,以便后续调用

2. 配置Jwt身份认证方式

在Program.cs中进行服务注册,配置身份验证的模式为JwtBearer。

安装Microsoft.AspNetCore.Authentication.JwtBearer这个nuget包


//配置JwtBearer身份认证服务
builder.Services.AddAuthentication(opts=>
{
    opts.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; //认证模式
    opts.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;    //质询模式
})
.AddJwtBearer(   //对JwtBearer进行配置
    x =>
    {
        x.RequireHttpsMetadata = true; //设置元数据地址或权限是否需要HTTP
        x.SaveToken = true;
        //Token验证参数
        x.TokenValidationParameters = new TokenValidationParameters()
        {
            ValidateIssuer = true,  //是否验证Issuer
            ValidIssuer = jwtTokenConfig.Issuer,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtTokenConfig.IssuerSigningKey)),
            ValidateAudience = true,
            ValidAudience = jwtTokenConfig.Audience,
            ValidateLifetime = true,
            ClockSkew = TimeSpan.FromMinutes(1)  //对token过期时间验证的允许时间
        };
        //如果jwt过期,在返回的header中加入Token-Expired字段为true,前端在获取返回header时判断
        x.Events = new JwtBearerEvents()
        {
            OnAuthenticationFailed = context =>
            {
                if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                {
                    context.Response.Headers.Add("Token-Expired", "true");
                }
                return Task.CompletedTask;
            }
        };
    }
);

3. 编写生成JWT令牌的帮助类

首先,定义相关的一些模型类,如下:

/// <summary>
/// 存放Token 跟过期时间的模型类
/// </summary>
public class TnToken
{
    /// <summary>
    /// token字符串
    /// </summary>
    public string TokenStr { get; set; }
    /// <summary>
    /// token过期时间
    /// </summary>
    public DateTime Expires { get; set; }
}
/// <summary>
/// 返回信息模型类
/// </summary>
public class ResponseModel
{
    /// <summary>
    /// 返回码
    /// </summary>
    public int Code { get; set; }
    /// <summary>
    /// 消息
    /// </summary>
    public string Msg { get; set; }
    /// <summary>
    /// 数据
    /// </summary>
    public object Data { get; set; }
    /// <summary>
    /// Token信息
    /// </summary>
    public TnToken TokenInfo { get; set; }
}
/// <summary>
/// token工具类的接口,方便使用依赖注入,很简单提供两个常用的方法
/// </summary>
public interface ITokenHelper
{
    /// <summary>
    /// 根据一个对象通过反射提供负载,生成token  
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="user"></param>
    /// <returns></returns>
    TnToken CreateToken<T>(T entity) where T : class;

    /// <summary>
    /// 根据键值对提供负载,生成token
    /// </summary>
    /// <param name="keyValuePairs"></param>
    /// <returns></returns>
    TnToken CreateToken(Dictionary<string, string> keyValuePairs);

}

以上接口的实现类如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Newtonsoft.Json;
using System.Security.Cryptography;

/// <summary>
/// Token帮助类
/// </summary>
public class TokenHelper : ITokenHelper
{
    //依赖注入配置项
    //private readonly IOptions<JwtTokenOption> _options;
    private readonly JwtTokenOption _options;
    /// <summary>
    /// 构造方法
    /// </summary>
    /// <param name="options"></param>
    public TokenHelper(JwtTokenOption options)
    {
        _options = options;
    }

    /// <summary>
    /// 根据一个对象通过反射提供负载,生成token  
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="user"></param>
    /// <returns></returns>
    public TnToken CreateToken<T>(T entity) where T : class
    {
        //定义声明的集合
        List<Claim> claims = new List<Claim>();

        //用反射把数据提供给它
        foreach (var item in entity.GetType().GetProperties())
        {
            object obj = item.GetValue(entity);
            string value = "";
            if(obj != null)
            {
                value = obj.ToString();
            }

            claims.Add(new Claim(item.Name, value));
        }

        //根据声明 生成token字符串
        return CreateTokenString(claims);
    }

    /// <summary>
    /// 根据键值对提供负载,生成token
    /// </summary>
    /// <param name="keyValuePairs"></param>
    /// <returns></returns>
    public TnToken CreateToken(Dictionary<string, string> keyValuePairs)
    {
        //定义声明的集合
        List<Claim> claims = new List<Claim>();

        foreach (var item in keyValuePairs)
        {
            claims.Add(new Claim(item.Key, item.Value));
        }

        //根据声明 生成token字符串
        return CreateTokenString(claims);
    }

    /// <summary>
    /// 私有方法,用于生成Token字符串
    /// </summary>
    /// <param name="claims"></param>
    /// <returns></returns>
    private TnToken CreateTokenString(List<Claim> claims)
    {
        //过期时间
        DateTime expires = DateTime.Now.AddMinutes(_options.AccessTokenExpiresMinutes);

        var token = new JwtSecurityToken(
            issuer: _options.Issuer,
            audience: _options.Audience,
            claims: claims,           //携带的荷载
            notBefore: DateTime.Now,  //token生成时间
            expires: expires,         //token过期时间
            signingCredentials: new SigningCredentials(
                new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.IssuerSigningKey)), SecurityAlgorithms.HmacSha256
                )
            );

        return new TnToken
        {
            Expires = expires,
            TokenStr = new JwtSecurityTokenHandler().WriteToken(token)
        };
    }
}

4. 在用户登录方法中,签发Token

//定义实例tokenHelper实例
private readonly ITokenHelper _tokenHelper;
//构造函数注入ITokenHelper实例 略

/// <summary>
/// 登录功能
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
[HttpPost]
public IActionResult Login(UserLoginDto user) //(1) 后端登录:账号密码的验证
{
    //对输入参数user进行验证,略
 	
    //定义一个响应信息的对象
    ResponseModel res = new ResponseModel();
    
    //检查用户是否存在(MD5对密码进行加密)
    var userInfo = _userService.CheckUserAndPwd(user.LoginName, user.Pwd);
    if (userInfo != null)
    {
        Dictionary<string, string> keyValuePairs = new Dictionary<string, string>
            {
                { "LoginName", user.LoginName },
                { "SuperAdmin", "true"} //假定用户属于SuperAdmin角色
            };
        res.Code = 200;
        res.Msg = "登录成功";
        
        //(2) 后端:帮助类来生成JWT字符串,JWT字符串返回给浏览器
        res.TokenInfo = _tokenHelper.CreateToken(keyValuePairs);
        return Ok(res);
    }
    else
    {
        res.Code = 401;
        res.Msg = "用户名或密码不正确";
        return Unauthorized(res);  //401的错误码
    }
}

5. 给API接口加身份授权锁

在Api控制器或者方法上,加[Authorize]特性,需要引用命名空间:

using Microsoft.AspNetCore.Authorization;

  • [Authorize]加在控制器上,则该控制器下所有API方法需要身份授权后才能访问;
  • [Authorize]加在方法上,则仅该方法需要身份授权后才能访问。

另外,有一个[AllowAnonymous]特性,加在Api控制器或者方法上,允许匿名访问该Api或者控制器下所有Api方法。

Api资源被锁保护起来之后,如果没有登录直接访问,则会报401的错误。在Swagger中测试截图如下:
ASP.NET Core高级之认证与授权(二)--JWT认证前后端完整实现,# ASP.NET Core,asp.net,后端,.net core

6. Swagger中进行Token的测试

(1)在Program.cs的配置Swagger服务:

builder.Services.AddSwaggerGen(c =>{
	var basePath = AppContext.BaseDirectory;  //获取应用程序的所在目录
    //或者用下面的方式也能获取
    var basePath2 =Path.GetDirectoryName(typeof(Program).Assembly.Location);
    
	var xmlPath = System.IO.Path.Combine(basePath, "XfTech.Demo.xml"); //拼接XML文件所在路径
    
	//让Swagger显示方法、类的XML注释信息
	c.IncludeXmlComments(xmlPath, true);
	//设置Swagger文档参数
	c.SwaggerDoc("v1", new OpenApiInfo
	{
		Title = "XfTech.Demo",
		Version = "v1",
		Description = "Asp.Net Core6 WebApi开发实战",  //描述信息
		Contact = new OpenApiContact()                //开发者信息
		{
			Name = "物联网大联盟",               //开发者姓名
			Email = "99825949@qq.com",    //email地址
			Url = new Uri("https://blog.csdn.net/ousetuhou?type=blog") //作者的主页网站
		}
	});
	//开启Authorize权限按钮
	c.AddSecurityDefinition("JWTBearer", new OpenApiSecurityScheme()
	{
		Description = "这是方式一(直接在输入框中输入认证信息,不需要在开头添加Bearer) ",
		Name = "Authorization",        //jwt默认的参数名称
		In = ParameterLocation.Header,  //jwt默认存放Authorization信息的位置(请求头中)
		Type = SecuritySchemeType.Http,
		Scheme = "Bearer"
	});
	//定义JwtBearer认证方式二
	//options.AddSecurityDefinition("JwtBearer", new OpenApiSecurityScheme()
	//{
	//    Description = "这是方式二(JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格))",
	//    Name = "Authorization",//jwt默认的参数名称
	//    In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
	//    Type = SecuritySchemeType.ApiKey
	//});
	//声明一个Scheme,注意下面的Id要和上面AddSecurityDefinition中的参数name一致
	var scheme = new OpenApiSecurityScheme
	{
		Reference = new OpenApiReference()
		{
			Id = "JWTBearer",  //这个名字与上面的一样
			Type = ReferenceType.SecurityScheme
		}
	};
	//注册全局认证(所有的接口都可以使用认证)
	c.AddSecurityRequirement(new OpenApiSecurityRequirement
	{
		{ scheme, Array.Empty<string>() }
	});
});
#endregion

(2) 测试登录接口,输入正确的账号和密码,接口返回JWT令牌
ASP.NET Core高级之认证与授权(二)--JWT认证前后端完整实现,# ASP.NET Core,asp.net,后端,.net core(3)点击“Authorize”按钮,在对话框中输入上一步获取到的令牌
ASP.NET Core高级之认证与授权(二)--JWT认证前后端完整实现,# ASP.NET Core,asp.net,后端,.net coreASP.NET Core高级之认证与授权(二)--JWT认证前后端完整实现,# ASP.NET Core,asp.net,后端,.net core(4)输入令牌后关闭对话框,右边的小锁为锁住的状态。接下来调用业务模块的Api会自动讲JWT令牌携带上。
ASP.NET Core高级之认证与授权(二)--JWT认证前后端完整实现,# ASP.NET Core,asp.net,后端,.net core

四、Vue前台使用JWT认证进行安全防护

登录页面的布局略。以下只演示如何获取并缓存JWT令牌,以及在每个请求的时候携带上JWT令牌。

1. 登录成功后,用sessionStorage保存JWT令牌

this.$http({
	url:"/api/Account/Login",
	method:"post",
	data:this.loginForm,
}).then((res)=>{
	if(res.data.code>0){
	   this.$message(res.data.msg);
	   sessionStorage.setItem('jwtToken',res.data.tokenInfo.access_token) //保存到浏览器缓存
	   this.$router.push('home') //跳转到首页
	}
})

2. 发送请求前,用axios拦截器添加以下请求头

Authorization: Bearer jwt令牌字符串

// 在main.js中 添加请求拦截器
axios.interceptors.request.use(function (config) {
  // 在发送请求之前做些什么
  var tkn = sessionStorage.getItem("jwtToken");
  if(tkn!="")
    config.headers.Authorization = 'Bearer ' + tkn
  return config;
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error);
});

本次对这个JWT认证进行了一个完整的封装演示。如果本文对你有帮助的话,请点赞+评论+关注,或者转发给需要的朋友。文章来源地址https://www.toymoban.com/news/detail-789289.html

到了这里,关于ASP.NET Core高级之认证与授权(二)--JWT认证前后端完整实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • .Net Core Jwt鉴权授权

    目录 简介 基于.Net Core 验证方式 Jwt获取Token 引入三方包 生成Token UserInfo JwtConfig WebApi测试(获取Token) Program.cs appsetting.json Controller .Net Core 验证(webApi) Progarm.cs Contorller .Net Core 授权 简介 Program.cs JwtAuthorization.cs 注意 Autofac 注册授权服务 Controller 注意 jwt触发委托 Jwt分为三段 通过远

    2024年02月13日
    浏览(33)
  • ASP.NET Core MVC 使用 JWT 的示例

    创建一个 ASP.NET Core MVC 项目。 添加 NuGet 包: Microsoft.AspNetCore.Authentication.JwtBearer:用于支持 JWT 的身份验证。 System.IdentityModel.Tokens.Jwt:用于生成和验证 JWT。 在 Startup.cs 文件中做如下修改: 请注意,在上述代码中,您需要将以下参数替换为实际的值: \\\"your_issuer\\\" :发行者的标

    2024年02月13日
    浏览(45)
  • ASP.NET CORE WEBAPI 登录 JWT 鉴权 ,接口权限验证

    介绍 当今Web开发中,API的使用越来越广泛,而API的安全性也变得越来越重要。其中,JWT(JSON Web Token)鉴权和授权是一种常见的解决方案。 本篇文章将会介绍JWT鉴权和授权的原理、实现方式以及注意事项。 什么是JWT? JWT是一种基于JSON格式的开放标准(RFC7519),用于在网络

    2023年04月21日
    浏览(67)
  • ASP.NET Core 鉴权授权三(添加自定义授权策略)

    此处鉴权给的值是6,授权用的1,尝试访问 基于策略的授权中有一个很重要的概念是Requirements,每一个Requirement都代表一个授权条件。 Requirement需要继承接口IAuthorizationRequirement。 已经内置了一些常用的实现: AssertionRequirement :使用最原始的断言形式来声明授权策略。 DenyAn

    2024年02月03日
    浏览(38)
  • ASP.NET Core 授权二(自定义token)

    首先自定义一个类TokenAuthenticationHandler,然后需要继承IAuthenticationHandler接口 具体代码: 后续需要鉴权的接口,在请求上都需要加上Authorization参数 Claim:相当于一个身份单元,存储着键值信息 ClaimsIdentity:身份证,身份单元的集合(可以理解为身份证上有多个身份单元) Clai

    2024年02月03日
    浏览(41)
  • ASP.NET Core 授权一(简单的Cookie)

    简单的理解:鉴权衡量你能不能进一道门,授权是你进门了可以干什么

    2024年02月04日
    浏览(50)
  • 杨中科 ASP.NET Core前后端分离开发

    前后端分离 1、传统MVC开发模式: 前后端的代码被放到同一个项目中,前端人员负责编写页面的模板,而后端开发人员负责编写控制器和模型的代码并且“套模板”。 缺点: 互相依赖, 耦合性强,责任划分不清 。 2、主流的“前后端分离”: 前端开发人员和后端开发人员分别负

    2024年01月23日
    浏览(52)
  • ASP.NET Core 鉴权授权二(自定义token)

    首先自定义一个类TokenAuthenticationHandler,然后需要继承IAuthenticationHandler接口 具体代码: 后续需要鉴权的接口,在请求上都需要加上Authorization参数 Claim:相当于一个身份单元,存储着键值信息 ClaimsIdentity:身份证,身份单元的集合(可以理解为身份证上有多个身份单元) Clai

    2024年02月03日
    浏览(40)
  • ASP.NET Core 鉴权授权一(简单的Cookie)

    简单的理解:鉴权衡量你能不能进一道门,授权是你进门了可以干什么

    2024年02月03日
    浏览(35)
  • ASP.NET Core 鉴权授权三(自定义授权策略、多授权策略、多鉴权架构)

    此处鉴权给的值是6,授权用的1,尝试访问 基于策略的授权中有一个很重要的概念是Requirements,每一个Requirement都代表一个授权条件。 Requirement需要继承接口IAuthorizationRequirement。 已经内置了一些常用的实现: AssertionRequirement :使用最原始的断言形式来声明授权策略。 DenyAn

    2024年02月03日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包