分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 教程案例

Asp.Net Core混合使用cookie和JwtBearer认证方案

发布时间:2023-09-06 02:15责任编辑:郭大石关键词:暂无标签

自己有时捣鼓一些小型演示项目,服务端主要是提供Web Api功能。为了便于管理,需要在服务端加一些简单的MVC网页,用管理员身份登录,做一些简单的操作。

因此需要实现一个功能,在一个Asp.Net Core网站里,MVC网页用cookie认证,Web Api用JwtBearer认证。虽然Identity Server 4可以实现多种认证方案,但是我觉得它太重了,想直接在网站内集成2种认证方案。在网上没有找到现成的DEMO,自己折腾了一段时间搞定了,所以记录一下。

创建cookie认证方案的MVC网站

新建Asp.Net Core MVC项目。无身份验证。无https方便调试。

添加登录网页视图模型类LoginViewModel

public class LoginViewModel ???{ ???????public string UserName { get; set; } = ""; ???????[DataType(DataType.Password)] ???????public string Password { get; set; } = ""; ???}

给Home控制器增加登录和注销函数,登录的时候要创建用户身份标识。

 ???????[HttpGet] ???????public IActionResult Login(string returnUrl = "") ???????{ ???????????ViewData["ReturnUrl"] = returnUrl; ???????????return View(); ???????} ???????[HttpPost, ActionName("Login")] ???????public async Task<IActionResult> LoginPost(LoginViewModel model, string returnUrl = "") ???????{ ???????????ViewData["ReturnUrl"] = returnUrl; ???????????if (ModelState.IsValid) ???????????{ ???????????????bool succee = (model.UserName == "admin") && (model.Password == "123"); ???????????????if (succee) ???????????????{ ???????????????????//创建用户身份标识 ???????????????????var claimsIdentity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme); ???????????????????claimsIdentity.AddClaims(new List<Claim>() ???????????????????{ ???????????????????????new Claim(ClaimTypes.Sid, model.UserName), ???????????????????????new Claim(ClaimTypes.Name, model.UserName), ???????????????????????new Claim(ClaimTypes.Role, "admin"), ???????????????????}); ???????????????????await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity)); ???????????????????return Redirect(returnUrl); ???????????????} ???????????????else ???????????????{ ???????????????????ModelState.AddModelError(string.Empty, "帐号或者密码错误。"); ???????????????????return View(model); ???????????????} ???????????} ???????????return View(model); ???????} ???????public async Task<IActionResult> Logout() ???????{ ???????????await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); ???????????return Redirect("/Home/Index"); ???????}

新建一个登录网页Login.cshtml

@model MixAuth.Models.LoginViewModel@{ ???ViewData["Title"] = "登录";}<div class="row"> ???<div class="col-xs-10 col-sm-8 col-md-6"> ???????<form asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post"> ???????????<div asp-validation-summary="All" class="text-danger"></div> ???????????<div class="form-group"> ???????????????<label asp-for="UserName"></label> ???????????????<input asp-for="UserName" class="form-control" placeholder="请输入用户名" /> ???????????????<span asp-validation-for="UserName" class="text-danger"></span> ???????????</div> ???????????<div class="form-group"> ???????????????<label asp-for="Password"></label> ???????????????<input asp-for="Password" class="form-control" placeholder="请输入密码" /> ???????????????<span asp-validation-for="Password" class="text-danger"></span> ???????????</div> ???????????<button type="submit" class="btn btn-primary">登录</button> ???????</form> ???</div></div>@section Scripts { ???@await Html.PartialAsync("_ValidationScriptsPartial")}

  

然后在Startup.cs增加cookie认证方案,并开启认证中间件。

public void ConfigureServices(IServiceCollection services) ???????{ ???????????services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) ???????????????.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => ???????????????{ ???????????????????//认证失败,会自动跳转到这个地址 ???????????????????options.LoginPath = "/Home/Login"; ???????????????}); ???????????services.Configure<CookiePolicyOptions>(options => ???????????{ ???????????????// This lambda determines whether user consent for non-essential cookies is needed for a given request. ???????????????//options.CheckConsentNeeded = context => true; ???????????????options.MinimumSameSitePolicy = SameSiteMode.None; ???????????}); ???????????services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); ???????} ???????// 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(); ???????????//开启认证中间件 ???????????app.UseAuthentication(); ???????????app.UseMvc(routes => ???????????{ ???????????????routes.MapRoute( ???????????????????name: "default", ???????????????????template: "{controller=Home}/{action=Index}/{id?}"); ???????????}); ???????}

给Home控制器的About函数增加认证要求。

 ???????[Authorize] ???????public IActionResult About()

把网站跑起来,点击关于,就会跳转到登录页面,登录通过后,会调回关于页面。

给网页再增加显示用户登录状态的功能。修改\Views\Shared\_Layout.cshtml,增加一个分部视图

<div class="navbar-collapse collapse"> ???????????????<ul class="nav navbar-nav"> ???????????????????<li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li> ???????????????????<li><a asp-area="" asp-controller="Home" asp-action="About">About</a></li> ???????????????????<li><a asp-area="" asp-controller="Home" asp-action="Contact">Contact</a></li> ???????????????</ul> ???????????????@await Html.PartialAsync("_LoginPartial") ???????????</div>

  

_LoginPartial.cshtml分部视图内容

@if (User.Identity.IsAuthenticated){ ???<form asp-controller="Home" asp-action="Logout" method="post" class="navbar-right"> ???????<ul class="nav navbar-nav navbar-right"> ???????????<li> ???????????????<a href="#">@User.Identity.Name</a> ???????????</li> ???????????<li> ???????????????<button type="submit" class="btn btn-link navbar-btn navbar-link"> ???????????????????退出登录 ???????????????</button> ???????????</li> ???????</ul> ???</form>}else{ ???<ul class="nav navbar-nav navbar-right"> ???????<li> ???????????<a asp-controller="Home" asp-action="Login" asp-route-returnUrl="/Home"> ???????????????登录 ???????????</a> ???????</li> ???</ul>}

  

现在可以点击页面导航栏的按钮的登录和注销了。

至此,网页用cookie认证方案搞定。下面要在这个基础上,增加Web Api和JwtBearer认证。

创建JwtBearer认证方案的Web Api控制器

添加一个Web Api控制器,就用默认的value好了。

增加一个JWTTokenOptions类,定义认证的一些属性。

public class JWTTokenOptions ???{ ???????//谁颁发的 ???????public string Issuer { get; set; } = "server"; ???????//颁发给谁 ???????public string Audience { get; set; } = "client"; ???????//令牌密码 ???????public string SecurityKey { get; private set; } = "a secret that needs to be at least 16 characters long"; ???????//修改密码,重新创建数字签名 ???????public void SetSecurityKey(string value) ???????{ ???????????SecurityKey = value; ???????????CreateKey(); ???????} ???????//对称秘钥 ???????public SymmetricSecurityKey Key { get; set; } ???????//数字签名 ???????public SigningCredentials Credentials { get; set; } ???????public JWTTokenOptions() ???????{ ???????????CreateKey(); ???????} ???????private void CreateKey() ???????{ ???????????Key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecurityKey)); ???????????Credentials = new SigningCredentials(Key, SecurityAlgorithms.HmacSha256); ???????} ???}

  

在startup.cs增加JwtBearer认证方案。

public void ConfigureServices(IServiceCollection services) ???????{ ???????????JWTTokenOptions jwtTokenOptions = new JWTTokenOptions(); ???????????services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) ???????????????.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => ???????????????{ ???????????????????//认证失败,会自动跳转到这个地址 ???????????????????options.LoginPath = "/Home/Login"; ???????????????}) ???????????????.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, jwtBearerOptions => ???????????????{ ???????????????????jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters ???????????????????{ ???????????????????????ValidateIssuerSigningKey = true, ???????????????????????IssuerSigningKey = jwtTokenOptions.Key, ???????????????????????ValidateIssuer = true, ???????????????????????ValidIssuer = jwtTokenOptions.Issuer, ???????????????????????ValidateAudience = true, ???????????????????????ValidAudience = jwtTokenOptions.Audience, ???????????????????????ValidateLifetime = true, ???????????????????????ClockSkew = TimeSpan.FromMinutes(5) ???????????????????}; ???????????????}); ???????????services.Configure<CookiePolicyOptions>(options => ???????????{ ???????????????// This lambda determines whether user consent for non-essential cookies is needed for a given request. ???????????????//options.CheckConsentNeeded = context => true; ???????????????options.MinimumSameSitePolicy = SameSiteMode.None; ???????????}); ???????????services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); ???????}

给value控制器增加认证方案,注意指定方案名称为JwtBearerDefaults.AuthenticationScheme。MVC控制器无需指定方案名称,因为默认就是CookieAuthenticationDefaults.AuthenticationScheme。

 ???[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] ???[Route("api/[controller]")] ???[ApiController] ???public class ValueController : ControllerBase

此时通过浏览器访问Web Api控制器,http://localhost:5000/api/Value,会得到401错误,这是对的,我们也不打算通过浏览器的方式访问Web Api,而是通过PC或者手机客户端。为了让认证客户端,需要增加一个获取Token的函数,暂时放在Home控制器,它的属性设置为[AllowAnonymous],允许未认证者访问。

 ???????[AllowAnonymous] ???????[HttpGet] ???????public string GetToken(string userName, string password) ???????{ ???????????bool success = ((userName == "user") && (password == "111")); ???????????if (!success) ???????????????return ""; ???????????JWTTokenOptions jwtTokenOptions = new JWTTokenOptions(); ???????????//创建用户身份标识 ???????????var claims = new Claim[] ????????????{ ???????????????new Claim(ClaimTypes.Sid, userName), ???????????????new Claim(ClaimTypes.Name, userName), ???????????????new Claim(ClaimTypes.Role, "user"), ???????????}; ???????????//创建令牌 ???????????var token = new JwtSecurityToken( ???????????????issuer: jwtTokenOptions.Issuer, ???????????????audience: jwtTokenOptions.Audience, ???????????????claims: claims, ???????????????notBefore: DateTime.Now, ???????????????expires: DateTime.Now.AddDays(1), ???????????????signingCredentials: jwtTokenOptions.Credentials ???????????????); ???????????string jwtToken = new JwtSecurityTokenHandler().WriteToken(token); ???????????return jwtToken; ???????}

编写客户端使用JwtBearer认证

编写一个WPF客户端软件去获取Token,访问Web Api。

private async Task GetTokenAsync() ???????{ ???????????try ???????????{ ???????????????using (WebClient client = new WebClient()) ???????????????{ ???????????????????//地址 ???????????????????string path = $"{webUrl}/Home/GetToken?userName=user&password=111"; ???????????????????token = await client.DownloadStringTaskAsync(path); ???????????????????txbMsg.Text = $"获取到令牌={token}"; ???????????????} ???????????} ???????????catch (Exception ex) ???????????{ ???????????????txbMsg.Text = $"获取令牌出错={ex.Message}"; ???????????} ???????} ???????private async Task GetValueAsync() ???????{ ???????????try ???????????{ ???????????????using (WebClient client = new WebClient()) ???????????????{ ???????????????????//地址 ???????????????????string path = $"{webUrl}/api/Value"; ???????????????????client.Headers.Add(HttpRequestHeader.Authorization, $"Bearer {token}"); ???????????????????string value = await client.DownloadStringTaskAsync(path); ???????????????????txbMsg.Text = $"获取到数据={value}"; ???????????????} ???????????} ???????????catch (Exception ex) ???????????{ ???????????????txbMsg.Text = $"获取数据出错={ex.Message}"; ???????????} ???????}

如果直接获取数据,能够捕捉到401错误。

先获取令牌。

再获取数据,就没问题了。

DEMO代码参见:

https://github.com/woodsun2018/MixAuth

Asp.Net Core混合使用cookie和JwtBearer认证方案

原文地址:https://www.cnblogs.com/sunnytrudeau/p/9693512.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved