实现一个登录:Mac+.NET 5+Identity+JWT+VS Code

.NET圈儿的朋友们,大家好!我可太喜欢如今开源的.Net了,写代码很巴适!所以今天分享一下之前学习的一个登录小案例,代码有不足之处欢迎指正!!!

工具:采用VS Code及其插件开发,轻量化的同时减少命令行的敲写,使用VS没有冲突哈

e5d8ded4e389fc331cfcacaad301803d.pnga4485c5650618310a3732e0bc6a144a8.png

一、通过插件创建WebApi项目

abe43cbb1f1427f7accfd42981ebbd90.png

原文是个动图,可点击原文查看

二、利用插件下载项目所需要的Nuget包

685b75dbe7ed106e86cde25e7a2166c5.png

三、代码编写

①新建User实体

/// <summary>
/// 登录用户实体类  继承Identiy框架提供的 IdentityUser类
/// </summary>
public class AppUser:IdentityUser
{// 自己再扩充三个字段public DateTime DateCreated { get; set; }public DateTime DateModified { get; set; }public string FullName { get; set; } 
}

②新建一个上下文类

public class AppDBContext : IdentityDbContext<AppUser, IdentityRole, string>
{public AppDBContext(DbContextOptions options) : base(options){}
}

③在Startup依赖注入上下文类

services.AddDbContext<AppDBContext>(options =>
{options.UseMySql(Configuration.GetConnectionString("DefaultConnection"), MySqlServerVersion.LatestSupportedServerVersion);
});
// AddEntityFrameworkStores 用来创建 用户和密码之间的服务
services.AddIdentity<AppUser, IdentityRole>(opt => { }).AddEntityFrameworkStores<AppDBContext>();

④在终端codefirst生成数据表

dotnet ef migrations add init
dotnet ef  database update
5b911edce0fb340f50e9eedea90a685d.png

⑤配置JWT

ConfigureServices方法里面配置服务

services.AddAuthentication(x =>
{x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>{// jwt的 key 需要设置复杂点 var key = Encoding.ASCII.GetBytes(Configuration["JWTConfig:Key"]);var issure = Configuration["JWTConfig:Issuer"];   // 发行人 var audience = Configuration["JWTConfig:Audience"];  // 受众   options.TokenValidationParameters = new TokenValidationParameters(){ValidateIssuerSigningKey = true,IssuerSigningKey = new SymmetricSecurityKey(key),ValidateIssuer = true, // 设置为True时 ValidIssure 属性设置下 不然jwt验证不会通过ValidateAudience = true, // 同上 ValidAudience 属性设置下  RequireExpirationTime = true, ValidateLifetime=true,   //  token失效缓冲时间 默认是五分钟 失效时间需要加上这五分钟缓冲//  如果 上面 ValidateIssuer  配置为false 则不需要下面两个属性ValidIssuer = issure,ValidAudience = audience,};});
// 多角色时 可以这样配置  [Authorize(Policy ="PolicyGroup")] 动作方法上可以简写
services.AddAuthorization(options =>
{options.AddPolicy("PolicyGroup", policy => policy.RequireRole("Admin", "User"));
});

Configure 方法里面使用服务

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{if (env.IsDevelopment()){app.UseDeveloperExceptionPage();app.UseSwagger();app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "SwaggerDemo v1"));}app.UseHttpsRedirection();app.UseCors("any");app.UseRouting();// 注意顺序app.UseAuthentication();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapControllers();});
}

⑥swagger配置

ConfigureServices方法里面配置服务

services.AddSwaggerGen(c =>
{c.SwaggerDoc("v1", new OpenApiInfo { Title = "SwaggerDemo", Version = "v1", Description = "Demo API for showing Swagger" });// 下面两步配置 实现 swagger 上面 “锁”c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme{In = ParameterLocation.Header,  // 位于HeaderDescription = "请于此处直接填写token 无需 Bearer然后再加空格的形式",Name = "Authorization",Type = SecuritySchemeType.Http,BearerFormat = "JWT",Scheme = "bearer"});c.AddSecurityRequirement(new OpenApiSecurityRequirement{{new OpenApiSecurityScheme{Reference=new OpenApiReference{Type=ReferenceType.SecurityScheme,Id="Bearer"}},Array.Empty<string>()}});// swagger接口注释显示 // 注意 vscode 用户需要在项目的csproj文件里面手动配置生成注释文档的属性  // 具体参见项目文件里的PropertyGroupvar fileName = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";var filePath = Path.Combine(AppContext.BaseDirectory, fileName);c.IncludeXmlComments(filePath);
});

Configure 方法里面使用服务

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{if (env.IsDevelopment()){app.UseDeveloperExceptionPage();// swagger 中间件使用app.UseSwagger();// 此处的 v1 必须与上面c.SwaggerDoc("v1") 里的一致app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "SwaggerDemo v1"));}app.UseHttpsRedirection();app.UseCors("any");app.UseRouting();// 注意顺序app.UseAuthentication();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapControllers();});
}

⑦创建UserController,并通过构造函数注入登录服务

private readonly UserManager<AppUser> _userManger;  // 用户服务
private readonly SignInManager<AppUser> _signInManger;  // 登录服务private readonly RoleManager<IdentityRole> _roleManger; // 角色服务
private readonly JWTConfig _jwtConfig;  // 配置框架将配置文件注入实体类
public UserController(ILogger<UserController> logger, UserManager<AppUser> userManager,SignInManager<AppUser> signInManager, IOptions<JWTConfig> jwtConfig, RoleManager<IdentityRole> roleManger)
{this._logger = logger;this._userManger = userManager;this._signInManger = signInManager;this._jwtConfig = jwtConfig.Value;this._roleManger = roleManger;
}

注册用户

/// <summary>
/// 用户注册
/// AddAndUpdateUserrRegisterModel 是一个Dto 接受对象
/// AllowAnonymous 不需要权限验证
/// 作者 xxxx
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpPost("RegisterUser")]
public async Task<Object> RegisterUser(AddAndUpdateUserrRegisterModel model)
{try{// check  注册的时候是否包含角色if (model.Roles is null || model.Roles.Count <= 0){return await Task.FromResult(new ResponseModel(Enums.ResponseCode.Error, "角色不能为空"));}// 循环判断用户所注册的角色时候存在 创建角色的方法  AddRole()foreach (var item in model.Roles){if (!await _roleManger.RoleExistsAsync(item)){return await Task.FromResult(new ResponseModel(Enums.ResponseCode.Error, "不存在创建的角色"));}}// 生成一个用户类var user = new AppUser(){UserName = model.Email,FullName = model.FullName,Email = model.Email,DateCreated = DateTime.Now,DateModified = DateTime.UtcNow};// 注册用户 var result = await _userManger.CreateAsync(user, model.Password);if (result.Succeeded){// 注册成功后 获取临时刚刚创建的用户  var tempUser = await _userManger.FindByEmailAsync(model.Email);// 循环给创建的用户添加角色foreach (var role in model.Roles){await _userManger.AddToRoleAsync(tempUser, role); // 添加角色}return await Task.FromResult(new ResponseModel(Enums.ResponseCode.Ok, "用户被成功注册!", null));}// 创建用户失败返回return await Task.FromResult(string.Join(",", result.Errors.Select(x => x.Description).ToArray()));}catch (System.Exception ex){// 异常返回return await Task.FromResult(new ResponseModel(Enums.ResponseCode.Error, ex.Message, null));}
}

登录

/// <summary>
/// 生成Token
/// 作者 xxxx
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
private string GenarateToken(AppUser user, List<string> roles)
{var jwtTokenHandle = new JwtSecurityTokenHandler();var key = Encoding.ASCII.GetBytes(_jwtConfig.Key);// 配置 Subjectvar claims = new List<Claim>(){new Claim(JwtRegisteredClaimNames.NameId,user.Id),new Claim(JwtRegisteredClaimNames.Email,user.Email),new Claim(JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString())};foreach (var role in roles){claims.Add(new Claim(ClaimTypes.Role,role));}var tokenDescriptor = new SecurityTokenDescriptor{// 多重角色Subject=new ClaimsIdentity(claims), // 单一角色// Subject = new ClaimsIdentity(new[]// {//     new System.Security.Claims.Claim(JwtRegisteredClaimNames.NameId,user.Id),//     new System.Security.Claims.Claim(JwtRegisteredClaimNames.Email,user.Email),//     new System.Security.Claims.Claim(JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString())//     // new System.Security.Claims.Claim(ClaimTypes.Role,"role")// }),// 过期时间 12小时  Expires = DateTime.UtcNow.AddSeconds(6),SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),Audience = _jwtConfig.Audience,  // 这里不配置 也会返回UnAuthorized Issuer = _jwtConfig.Issuer // 同上};// 创建tokenvar token = jwtTokenHandle.CreateToken(tokenDescriptor);return jwtTokenHandle.WriteToken(token);
}
/// <summary>
/// 用户登录
/// LoginModel 登录Dto接收
/// 作者 xxxx
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpPost]
public async Task<object> Login(LoginModel model)
{try{if (!ModelState.IsValid){return await Task.FromResult(new ResponseModel(Enums.ResponseCode.Error, "参数不合法!", null));}var result = await _signInManger.PasswordSignInAsync(model.UserName, model.Password, false, false);if (result.Succeeded){// 成功的话获取用户var appUser = await _userManger.FindByNameAsync(model.UserName);var roles = (await _userManger.GetRolesAsync(appUser)).ToList();// await _userManger.GetRolesAsync(appUser);var user = new UserDto(appUser.FullName, appUser.Email, appUser.UserName, appUser.DateCreated, roles){// 生成Token Token = GenarateToken(appUser,roles)};return await Task.FromResult(new ResponseModel(Enums.ResponseCode.Ok, "登录成功", user));}else{return await Task.FromResult(new ResponseModel(Enums.ResponseCode.Error, "登录失败", null));}}catch (System.Exception ex){return await Task.FromResult(new ResponseModel(Enums.ResponseCode.Error, ex.Message, null));}
}
001507850b5ac20c4b11009e08e9a104.png7130a96d91b0ca322cad045bfd098199.png

添加角色

a2def8bfa09432be971634fdcf878aaf.png

先将上文用户登录产生的token 设置到swagger里面,然后访问只有 Admin 角色可以访问的接口

/// <summary>
/// 添加角色
/// [Authorize(Roles ="Admin")]  只有Admin角色的用户可以访问
/// 作者 xxx
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[Authorize(Roles ="Admin")]
[HttpPost("AddRole")]
public async Task<object> AddRole(AddRoleModel model)
{try{if (model is null || string.IsNullOrWhiteSpace(model.Role)){return await Task.FromResult(new ResponseModel(Enums.ResponseCode.Error, "角色不能为空"));}// 判断【AspNetRoles】 表里  角色是否存在  if (await _roleManger.RoleExistsAsync(model.Role)){return await Task.FromResult(new ResponseModel(Enums.ResponseCode.Ok, "角色存在了"));}var role = new IdentityRole(){Name = model.Role,};// 创建角色var result = await _roleManger.CreateAsync(role);if (result.Succeeded){return await Task.FromResult(new ResponseModel(Enums.ResponseCode.Ok, "角色创建成功!"));}else{return await Task.FromResult(new ResponseModel(Enums.ResponseCode.Error, "角色创建失败!"));}}catch (System.Exception){return await Task.FromResult(new ResponseModel(Enums.ResponseCode.Error));}}

关于我们token权限在校验时出现失败怎么办?这里Asp.Net Core 5.0 新增一个接口【IAuthorizationMiddlewareResultHandler】可以处理权限验证  看下文代码展示!

/// <summary>
/// 这个是Asp.Net Core 5 新增的授权处理失败  可以直接暴露出请求上下文 省事很多啦!!!
/// 作者 xxx
/// </summary>
public class AuthorizationHandleMiddleWare : IAuthorizationMiddlewareResultHandler
{private readonly AuthorizationMiddlewareResultHandler authorizationHandleMiddleWare =new();public async Task HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult){// 当 token失效或者token不存在的时候 authorizeResult.Challenged 为Trueif(authorizeResult.Challenged) {// todo 拿到上下文user对象后 此处可以check token  区分token是否是过期了var a=context.Request.Headers["Authorization"];context.Response.StatusCode=(int)HttpStatusCode.OK;await context.Response.WriteAsJsonAsync(new ResponseModel(Enums.ResponseCode.UnAuthorized,"您未授权,请检查Token是否有效!"));return ;}// 此时token 校验通过  但是访问的资源的没权限的话 authorizeResult.Forbidden 为trueif(authorizeResult.Forbidden){context.Response.StatusCode=(int)HttpStatusCode.OK;await context.Response.WriteAsJsonAsync(new ResponseModel(Enums.ResponseCode.ForBidden,"您无此权限访问哦!"));return ;}await authorizationHandleMiddleWare.HandleAsync(next,context,policy,authorizeResult);}
}

另外还需要在ConfigureService里面注册下服务

// .net 5 新增的权限验证中间件  在此处依赖注入一下  详见 AuthorizationHandleMiddleWare.cs 文件
services.AddSingleton<IAuthorizationMiddlewareResultHandler,AuthorizationHandleMiddleWare>();

以上就是一个登录的简单demo,详细代码请访问码云

作者:Jarry

原文链接:https://blog.csdn.net/qbc12345678/article/details/120758144

文中源码托管Gitee:https://gitee.com/holyace/together/tree/JarryGu_develop/framework/JwtLoginDemo

作者QQ技术交流群:346250023

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/296883.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

天体运行动图,如此美妙,如此震撼!

全世界只有3.14 % 的人关注了爆炸吧知识三体中心天体也在运动双星系统银河系与仙女系相撞模拟转自&#xff1a;奇趣物理--完--

密封槽设计标准_哈尔滨轴承 | 超全面机械密封选型

有转动的地方&#xff0c;就有HRB轴承(400-086-6629)100多年来&#xff0c;人们采用填料一类的多种密封材料&#xff0c;使液体从液泵壳体沿着主轴的向外泄漏量不断减少。尽管现代工艺流程中采用的液泵&#xff0c;仍然广泛地沿用最古老的密封设计——填料盒&#xff0c;因为其…

重要更新,Office Add-in将全面支持Webview2

在陪娃上课&#xff0c;耳边回响着美妙的音乐&#xff0c;打开电脑码字&#xff0c;给大家分享一个非常重要的消息&#xff1a;Office Add-in将全面支持WebView2了。Office的开发爱好者可能会对此很感兴趣&#xff0c;虽然新一代的Web Add-in随着Office 365的推出&#xff0c;也…

哈佛学者:这3本英文名著,每天小读10分钟,英语level暴涨....

全世界只有3.14 % 的人关注了爆炸吧知识《锵锵》中窦文涛曾说过&#xff1a;“读原著学英语&#xff0c;就像有了可视化进度条&#xff0c;每天的提升肉眼可见。”今天就给大家推荐3本豆瓣高分英文名著&#xff0c;学英语不枯燥&#xff0c;每天小读10分钟&#xff0c;即可学到…

php -- 用文本来存储内容,file_put_contents,serialize,unserialize

根据存储的内容来划分 字符串&#xff1a; file_put_contents &#xff1a;将一个字符串写入文件 语法&#xff1a;int file_put_contents ( string $filename , mixed $data [, int $flags 0 [, resource$context ]] ) 参数 filename&#xff1a;要被写入数据的文件名。 data…

女朋友当众甩了我一巴掌,我扑上去......

1 小朋友&#xff0c;你是否有很多问号&#xff1f;▼2 小鸟&#xff1a;我呸&#xff01;秀什么秀&#xff1f;没眼看&#xff01;▼3 开学后你的上课状态......▼4 不能总让保安叔叔拿枪指我▼5 哥哥&#xff1a;让我看看&#xff0c;我妹妹怎么那么可爱&#xff01;▼6…

为什么我们要做2021.NET中国开发者调研

今年7月的时候&#xff0c;由上海MS Reactor牵头&#xff0c;我们做了一次小型的闭门会议。主要是围绕.NET开源方向进行讨论。 数闪科技的技术总监谷首道给我们带来的消息是他们有意将公司目前正在研发和使用的一整套技术框架进行开源。亲切可爱的辛老师、盛派创始人苏震巍、数…

sql连接远程服务器索引超出了_手机怎么连接服务器远程桌面?RD client远程桌面使用教程...

大家好啊&#xff0c;现在很多游戏、应用为了体验感好、方便使用都实现了多端同步&#xff0c;服务器远程连接也不例外&#xff0c;今天给你们分享手机连接服务器远程桌面的操作方法【 好东西一起分享&#xff0c;好音乐一起听 】不管是安卓还是苹果手机&#xff0c;咱们都需要…

java垃圾回收机制

为什么80%的码农都做不了架构师&#xff1f;>>> Java垃圾回收机制 C程序员觉得内存管理太重要了,所以一定需要自己管理, java程序员觉得内存管理太重要了,所以一定不能自己管理! 一、简述 垃圾回收是一种动态存储管理技术&#xff0c;它自动地释放不再被程序引用的…

神PS!老爸把儿子的画作P成现实,看完我笑哭了

全世界只有3.14 % 的人关注了爆炸吧知识只要你敢画&#xff0c;我就敢P成现实这种脑洞大开的事发生在英国的一对父子身上儿子天马行空地乱画爸爸认认真真地P出来象君几年前在ins发现了这对宝藏父子没想到他们的疯狂依旧在继续着四脚怪物系列看上去好像也没什么毛病你就是我见过…

单体应用 适合采用 dapr 构建吗?

缘起今天在微信群里有同学问 ”纯.net 项目&#xff0c;有必要上dapr吗&#xff1f;” 当时不假思索的说不是微服务没必要&#xff0c;其他群友也说没必要。下午细想了一下&#xff0c;觉得这个和微服务没有关系&#xff0c;如果我的应用是个单体架构&#xff08;将所有功能都部…

一次面试引发的思考(中小型网站优化思考) (转)

前言 故事的起因是这样的&#xff0c;由于本人地处偏僻工作地点在美丽的冰城哈尔滨虽然地方很美丽&#xff0c;但是这里的软件行业实在是算不上“美丽”&#xff0c;这么多年由于个人原因或者公司原因经常换工作&#xff0c;因为这里都是中小型公司&#xff0c;没有什么大公司。…

string 字符串中字符无效_JavaScript中的字符串(string)到底是什么?

通过可见字符建模把JavaScript中的string当作字符序列来看待是最直观的&#xff0c;虽然这样并不准确。以下代码示例中的字符串由5个字母和一个感叹号组成&#xff1a;const message Hello!;如果把string当作是可见的字符序列&#xff0c;那么Hello!中的字符数是6&#xff1a;…

c语言二维数组代码,C语言之二维数组(示例代码)

二维数组及其指针1 ) 在了解二维数组之前&#xff0c;我们先来了解一维数组及其指针我们知道&#xff0c;一维数组中&#xff1a;数组名代表-->数组首元素的首地址(千万不要认为是数组的首地址(&a)&#xff0c;绝对不是)在内存中&#xff0c;该代码的表现形式如下图&…

颜宁分享干货:给实验室博士的一些忠告

全世界只有3.14 % 的人关注了爆炸吧知识本文来源&#xff1a;颜宁微博nyouyou&#xff0c;作者&#xff1a;颜宁前几日&#xff0c;颜宁在微博上与大家分享自己做实验记录的心得。在此贴出&#xff0c;与大家分享~从我进Shi Lab的第一天&#xff0c;就被导师灌输&#xff1a;好…

研发考核难的本质是因为这三个特点

大家好&#xff0c;我是Z哥。我坦白&#xff0c;这篇是早就写好的库存文章&#xff0c;包括上周的那篇也是。原因是最近跳槽了&#xff0c;到新公司忙得飞起&#xff0c;都没时间写文章。还好我之前未雨绸缪准备了几篇提前写好的文章作为余量&#xff5e;我尽量能保持不断更&am…

剑指offer-面试题13.在O(1)时间删除链表节点

题目:给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间删除该节点。 链表节点与函数的定义如下。 通常我们删除某个节点都是从头开始遍历到需要删除节点的前一个节点。 然后使得该节点的next指向删除节点的next即可&#xff0c;这样看来删除一个节点 的复杂度为O(n)…

为什么不能一次走遍哥尼斯堡的7座桥

全世界只有3.14 % 的人关注了爆炸吧知识数学的快乐到底有多简单今天&#xff0c;8岁表妹问了一个问题&#xff1a;看到这种类似11&#xff1f;的问题&#xff0c;超模君几乎不用思考就已经知道答案。但为了体现让表妹系统的理解知识&#xff0c;所以我决定......发生在哥尼斯堡…

官宣,11月8号,.NET6+64位VS璀璨面世!

12号&#xff0c;.NET6 RC2发布&#xff0c;13号&#xff0c;VS2022 RC版发布&#xff0c;11月8号&#xff0c;.NET6VS2022C#10 正式版将同时发布&#xff0c;.NET开发将步入全新篇章&#xff0c;各种新语法、新框架、新技术都如约而至&#xff0c;令人期待&#xff01;然而&am…

Python-技巧

0.推荐的扩展 requests urilib的替代品 BeautifulSoup4 解析HTML代码 rq 任务队列 selenium 自动化测试工具&#xff0c;模拟浏览器 1.sys.modules, 根据已加载的模块创建对象, 其中 page 为类名 if hasattr(sys.modules[__name__], page):setattr(self, page, getattr(sys.mod…