目录

1、Cookie-based认证的实现

2、Jwt Token 的认证与授权

3、Identity Authentication + EF 的认证

Cookie-based认证的实现

  cookie认证方式如下图所示,当我们访问一个网页(Admin/Index)时候,这时候系统会检查你是否有权限,假如没有权限,便会我当前Url重定向到登陆页面(/Account/Login),在登陆成功后,系统会返回一个cookie保存在浏览器,此时再带着这个cookie去重新访问你最开始要访问的页面(Admin/Index)。如下图所示。

我们在.net core 中,也有一套基于cookie-basic的认证方式。

  首先,创建一个Core 2.0的MVC项目,添加两个控制器AdminController代表我们要认证后才能访问的资源,AccountController,模拟登陆。在AccountController中,

  1. [Authorize]
  2. public class AdminController : Controller
  3. {
  4. public IActionResult Index()
  5. {
  6. return View();
  7. }
  8. }

AdminController

  1. public class AccountController : Controller
  2. {
  3. public async Task<IActionResult> MakeLogin()
  4. {
  5. var claims = new List<Claim>
  6. {
  7. new Claim(ClaimTypes.Name,"lmc"),
  8. new Claim(ClaimTypes.Role, "admin")
  9. };
  10. var claimsIdentity = new ClaimsIdentity(
  11. claims,
  12. CookieAuthenticationDefaults.AuthenticationScheme
  13. );
  14. await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
  15. new ClaimsPrincipal(claimsIdentity),
  16. new AuthenticationProperties {
  17. IsPersistent=true, //cookie过期时间设置为持久
  18. ExpiresUtc= DateTime.UtcNow.AddSeconds() //设置过期20秒
  19. });
  20. return Ok();
  21. }
  22. public async Task<IActionResult> Logout()
  23. {
  24. await HttpContext.SignOutAsync(
  25. CookieAuthenticationDefaults.AuthenticationScheme);
  26. return Ok();
  27. }
  28. }

AccountController

然后我们配置Startup,将认证服务加入到DI容器&&引用认证中间件。

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
  4. .AddCookie(config=>
  5. {
  6. config.LoginPath = "/Account/MakeLogin"; //未认证导向登陆的页面,默认为/Account/Login
  7. config.Cookie.Name = "lmccookie"; //设置一个cookieName
  8.  
  9. });
  10. services.AddMvc();
  11. }
  12.  
  13. public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  14. {
  15. if (env.IsDevelopment())
  16. {
  17. app.UseDeveloperExceptionPage();
  18. app.UseBrowserLink();
  19. }
  20. else
  21. {
  22. app.UseExceptionHandler("/Home/Error");
  23. }
  24.  
  25. app.UseStaticFiles();
  26. app.UseAuthentication(); //加入认证中间件
  27. ...//// other code
  28. }

测试下。 直接访问 Admin/Index被重定向到Login并返回我们定义的cookie,我们再带着cookie再次访问Admin/Index

Jwt Token 的认证

  JwtToken 一般用于一些前后端分离的项目或者是移动端的项目。大体流程是用户首先访问目标资源(例如这里的api/values),然后服务器返回一个401或者是403的响应码标识未授权登陆。这时用户应该重新登陆获取token (例如这里的api/token),拿到token以后,请求头里面带着token再去访问目标资源。

  JwtToken 由三部分构成 首先是HEADER,这里面包含了Base64加密过的 加密算法和token的类型;PAYLOAD ,这里包含了一个Base64加密过的 Claims数组;SIGNATURE,包含了使用你的加密算法加密过后的 HEADER  ‘. ’ 和 PAYLOAD 加一个自定义的密钥。

  我们在.net core 中实现下Jwttoken的验证。

我们在ValuesController 打上[Authorize] 标签。配置我们的startup,在startup ConfigureServices方法中注入认证服务,Configure方法中添加中间件。 此时我们访问api/values=》返回401 的http状态码。

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. services.Configure<JwtSettings>(Configuration.GetSection("JwtSettings")); //appsettings中读取到jwtsettings节点
  4. var jwtSetting = new JwtSettings();
  5. Configuration.Bind("JwtSettings", jwtSetting);
  6. services.AddAuthentication(options =>
  7. { // 添加认证头
  8. options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
  9. options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
  10. })
  11. .AddJwtBearer(jo => jo.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
  12. {
  13. ValidIssuer = jwtSetting.Issuer, //使用者
  14. ValidAudience = jwtSetting.Audience, //颁发者
  15. IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSetting.SecreKey)) //加密方式
  16. });
  17. services.AddMvc();
  18. }
  19. public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  20. {
  21. if (env.IsDevelopment())
  22. {
  23. app.UseDeveloperExceptionPage();
  24. }
  25. app.UseAuthentication(); //注意加入中间件

Startup

  1. public class JwtSettings
  2. {
  3. public string Issuer { get; set; } //办法token的人
  4. public string Audience { get; set; } //token使用者
  5. public string SecreKey { get; set; } //token加密钥
  6. }

JwtSettings

  1. {
  2. "Logging": {
  3. "IncludeScopes": false,
  4. "Debug": {
  5. "LogLevel": {
  6. "Default": "Warning"
  7. }
  8. },
  9. "Console": {
  10. "LogLevel": {
  11. "Default": "Warning"
  12. }
  13. }
  14. },
  15. "JwtSettings": {
  16. "Audience": "http://localhost:5000",
  17. "Issuer": "http://localhost:5000",
  18. "SecreKey": "HelloKeylmclmclmc"
  19. }
  20. }

appsettings

接下来需要生成token,添加一个AuthorizeController,通过构造函数把jwtsettings 注入进来,添加一个Index的Action 用来生成我们的token。我们需要一个验证下登录用户的用户名密码,所以还需要添加一个ViewModel

  1. public class LoginViewModel
  2. {
  3. [Required]
  4. public string Name { get; set; }
  5. [Required]
  6. public string PassWord { get; set; }
  7. }

LoginViewModel

  1. private JwtSettings _jwtSettings;
  2. public AuthorizeController(IOptions<JwtSettings> options) //构造函数注入,拿到appsettings 里面的jwtsettings
  3. {
  4. _jwtSettings = options.Value;
  5. }
  6. [Route("api/token")]
  7. [HttpPost]
  8. public IActionResult Index(LoginViewModel loginViewModel)
  9. {
  10. if (!ModelState.IsValid)
  11. return BadRequest();
  12. if (!(loginViewModel.Name == "lmc" && loginViewModel.PassWord == ""))
  13. return BadRequest();
  14. var claims = new Claim[] //实例化一个Claim
  15. {
  16. new Claim(ClaimTypes.Name,"lmc"),
  17. new Claim(ClaimTypes.Role, "admin")
  18. };
  19. var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.SecreKey)); //将appsettings里面的SecreKey拿到
  20. var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); //使用HmacSha256 算法加密
  21. //生成token,设置过期时间为30分钟, 需要引用System.IdentityModel.Tokens.Jwt 包
  22. var token = new JwtSecurityToken(_jwtSettings.Issuer, _jwtSettings.Audience, claims, DateTime.Now, DateTime.Now.AddMinutes(), creds);
  23. //将token返回
  24. return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
  25. }

AuthorizeController

一切准备就绪,拿到Token。带着token来访问 /api/values,注意token需要带这常量 bearer 。

带着我们的加密钥来jwt官网验证一下。

基于角色(Role),Claim/Policy 的授权:

在我们返回token的时候,实例化过一个Claim数组,其中,Role是admin。我们修改ValuesController的Authorize特性标签( [Authorize(Roles = "user")])。

当我们带着token再去验证时候。然后我们把Claim数组的Role那一项的Value 改为user 便会正常认证。

基于Claim的验证我们需要做的事在StartUp的ConfigureServices方法中,将授权模块加入到DI容器中。这里我们添加了一个SuperAdminOnlydPolicy

此时,我们需要在AuthorizeController控制器返回Token的时候,需要在Claim数组中添加一个新的Claim,表示我们的Policy。

此时,再修改我们的ValuesController控制器的Authorize特性标签。

PostMan 走一波===》

Identity +EF Authentication 的认证

  首先,使用net core 的脚手架命令创建一个自带Identity 的mvc项目。然后根据appseetings的数据库配初始化数据库(默认数据库实例是(localdb)\\mssqllocaldb,因为我装vs时候没装这个,所以我换成了.) 。

还原完了数据库,就可以把项目跑起来了,可以根据右上角注册,登录下==》  (其中代码可以自行观看)

然后让我们来自己从头开始实现下这个过程:

可以在上文中Cookie认证的项目中完成,也可以新建一个空的MVC core项目。添加一个Account控制器,其中存在三个方法(action),注册、登录,登出。

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading.Tasks;
  5. using Microsoft.AspNetCore.Mvc;
  6. using System.Security.Claims;
  7. using Microsoft.AspNetCore.Authentication.Cookies;
  8. using Microsoft.AspNetCore.Authentication;
  9. using MVCOnCookieBaseStudy.ViewModel;
  10. using Microsoft.AspNetCore.Identity;
  11. using MVCOnCookieBaseStudy.Models;
  12.  
  13. namespace MVCOnCookieBaseStudy.Controllers
  14. {
  15. public class AccountController : Controller
  16. {
  17. private UserManager<ApplicationUser> _userManager; //加入Identity自带的注册使用的Manager
  18. private SignInManager<ApplicationUser> _signInManager; //加入Identity自带的登录使用的Manager
  19.  
  20. public AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
  21. {
  22. _userManager = userManager;
  23. _signInManager = signInManager;
  24. }
  25. /// <summary>
  26. /// 注册页面
  27. /// </summary>
  28. /// <returns></returns>
  29. public IActionResult Register(string returnUrl = "/Home/Index")
  30. {
  31. ViewData["ReturnUrl"] = returnUrl;
  32. return View();
  33. }
  34. [HttpPost]
  35. public async Task<IActionResult> Register(RegisterViewModel registerViewModel, string returnUrl = "/Home/Index")
  36. {
  37. ViewData["ReturnUrl"] = returnUrl;
  38. if (ModelState.IsValid) //model 验证
  39. {
  40. ApplicationUser identityUser = new ApplicationUser
  41. {
  42. Email = registerViewModel.Email,
  43. UserName = registerViewModel.Email,
  44. NormalizedUserName = registerViewModel.Email
  45. };
  46. var result = await _userManager.CreateAsync(identityUser, registerViewModel.Password);
  47. if (result.Succeeded)
  48. {
  49. await _signInManager.SignInAsync(identityUser, new AuthenticationProperties { IsPersistent = true });
  50. return Redirect(returnUrl);
  51. }
  52. else
  53. {
  54. foreach(var err in result.Errors)
  55. {
  56. ModelState.AddModelError("", err.Description);
  57. }
  58. }
  59. }
  60.  
  61. return View();
  62. }
  63. /// <summary>
  64. /// 登录页面
  65. /// </summary>
  66. /// <returns></returns>
  67. public IActionResult Login(string returnUrl = "/Home/Index")
  68. {
  69. ViewData["ReturnUrl"] = returnUrl;
  70. return View();
  71. }
  72. [HttpPost]
  73. public async Task<IActionResult> Login(RegisterViewModel LoginViewModel, string returnUrl = "/Home/Index")
  74. {
  75. ViewData["ReturnUrl"] = returnUrl;
  76. var loginUser = await _userManager.FindByEmailAsync(LoginViewModel.Email);
  77. if (loginUser == null)
  78. {
  79. return View();
  80. }
  81.  
  82. await _signInManager.SignInAsync(loginUser, new AuthenticationProperties { IsPersistent = true });
  83. return Redirect(returnUrl);
  84. }
  85. /// <summary>
  86. /// 原来的Cookie登录
  87. /// </summary>
  88. /// <returns></returns>
  89. public async Task<IActionResult> MakeLogin()
  90. {
  91. var claims = new List<Claim>
  92. {
  93. new Claim(ClaimTypes.Name,"lmc"),
  94. new Claim(ClaimTypes.Role, "admin")
  95. };
  96. var claimsIdentity = new ClaimsIdentity(
  97. claims,
  98. CookieAuthenticationDefaults.AuthenticationScheme
  99. );
  100. await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
  101. new ClaimsPrincipal(claimsIdentity),
  102. new AuthenticationProperties {
  103. IsPersistent=true, //cookie过期时间设置为持久
  104. ExpiresUtc= DateTime.UtcNow.AddSeconds() //设置过期20秒
  105. });
  106. return Ok();
  107. }
  108. /// <summary>
  109. /// 登出
  110. /// </summary>
  111. /// <returns></returns>
  112. public async Task<IActionResult> Logout()
  113. {
  114. await _signInManager.SignOutAsync();
  115. return Redirect("/Home/Index");
  116. }
  117. }
  118. //覆盖默认验证
  119. public class MyCookieTestAuthorize: CookieAuthenticationEvents
  120. {
  121. public override Task ValidatePrincipal(CookieValidatePrincipalContext context)
  122. {
  123. return base.ValidatePrincipal(context);
  124. }
  125. }
  126. }

AccountController

  1. @using MVCOnCookieBaseStudy.ViewModel
  2. @model RegisterViewModel
  3. @{
  4. ViewData["Title"] = "Register";
  5. }
  6.  
  7. <h2>@ViewData["Title"]</h2>
  8.  
  9. <div class="row">
  10. <div class="col-md-4">
  11. <form asp-route-returnUrl="@ViewData["ReturnUrl"]" method="post">
  12. <h4>Create a new account.</h4>
  13. <hr />
  14. <div asp-validation-summary="All" class="text-danger"></div>
  15. <div class="form-group">
  16. <label asp-for="Email"></label>
  17. <input asp-for="Email" class="form-control" />
  18. <span asp-validation-for="Email" class="text-danger"></span>
  19. </div>
  20. <div class="form-group">
  21. <label asp-for="Password"></label>
  22. <input asp-for="Password" class="form-control" />
  23. <span asp-validation-for="Password" class="text-danger"></span>
  24. </div>
  25. <div class="form-group">
  26. <label asp-for="ConfirmPassword"></label>
  27. <input asp-for="ConfirmPassword" class="form-control" />
  28. <span asp-validation-for="ConfirmPassword" class="text-danger"></span>
  29. </div>
  30. <button type="submit" class="btn btn-default">Register</button>
  31. </form>
  32. </div>
  33. </div>
  34.  
  35. @section Scripts {
  36. @await Html.PartialAsync("_ValidationScriptsPartial")
  37. }

注册的View

  1. @using MVCOnCookieBaseStudy.ViewModel
  2. @model RegisterViewModel
  3. @{
  4. ViewData["Title"] = "Login";
  5. }
  6.  
  7. <h2>Login</h2>
  8.  
  9. <div class="row">
  10. <div class="col-md-4">
  11. <section>
  12. <form asp-route-returnurl="@ViewData["ReturnUrl"]" method="post">
  13. <h4>Use a local account to log in.</h4>
  14. <hr />
  15. <div asp-validation-summary="All" class="text-danger"></div>
  16. <div class="form-group">
  17. <label asp-for="Email"></label>
  18. <input asp-for="Email" class="form-control" />
  19. <span asp-validation-for="Email" class="text-danger"></span>
  20. </div>
  21. <div class="form-group">
  22. <label asp-for="Password"></label>
  23. <input asp-for="Password" class="form-control" />
  24. <span asp-validation-for="Password" class="text-danger"></span>
  25. </div>
  26. <div class="form-group">
  27. <button type="submit" class="btn btn-default">Log in</button>
  28. </div>
  29. </form>
  30. </section>
  31. </div>
  32. </div>
  33. @section Scripts{
  34. @await Html.PartialAsync("_ValidationScriptsPartial"); @*前端验证,需要引用jquery.validate.min.js*@
  35. }

登录的View

这个过程中需要使用到一个ViewModel,用来传递登录数据

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel.DataAnnotations;
  4. using System.Linq;
  5. using System.Threading.Tasks;
  6.  
  7. namespace MVCOnCookieBaseStudy.ViewModel
  8. {
  9. public class RegisterViewModel
  10. {
  11. [Required]
  12. [EmailAddress]
  13. [Display(Name = "Email")]
  14. public string Email { get; set; }
  15.  
  16. [Required]
  17. [DataType(DataType.Password)]
  18. [StringLength(, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = )]
  19. [Display(Name = "Password")]
  20. public string Password { get; set; }
  21.  
  22. [DataType(DataType.Password)]
  23. [Display(Name = "Confirm password")]
  24. [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
  25. public string ConfirmPassword { get; set; }
  26. }
  27. }

RegisterViewModel

添加集成自Identity的User 和 Role还有数据库链接上下文

  1. public class ApplicationUserRole: IdentityRole
  2. {
  3. }
  4. public class ApplicationUser:IdentityUser
  5. {
  6. }

Role&&User

  1. public class ApplicationDbContext:IdentityDbContext<ApplicationUser,ApplicationUserRole,string>
  2. {
  3. public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
  4. {
  5.  
  6. }
  7. }

数据库链接上下文

接下来配置我们的StartUp,在ConfigureServices方法中 将数据库连接上下文加入DI容器,Identity服务加入DI容器,重新配置下我们的密码规则。(注意在Configure加入认证中间件)

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. //连接数据库服务加入DI容器
  4. services.AddDbContext<ApplicationDbContext>(option =>
  5. {
  6. option.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
  7. });
  8. //将Identity服务加入DI容器
  9. services.AddIdentity<ApplicationUser, ApplicationUserRole>()
  10. .AddEntityFrameworkStores<ApplicationDbContext>()
  11. .AddDefaultTokenProviders();
  12. //设置注册密码的规则
  13. services.Configure<IdentityOptions>(options =>
  14. {
  15. options.Password.RequireLowercase = false;
  16. options.Password.RequireNonAlphanumeric = false;
  17. options.Password.RequireUppercase = false;
  18. });
  19.  
  20. services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
  21. .AddCookie(config=>
  22. {
  23. config.LoginPath = "/Account/Login"; //未认证导向登陆的页面,默认为/Account/Login
  24. config.Cookie.Name = "lmccookie"; //设置一个cookieName
  25.  
  26. });
  27. services.AddMvc();
  28. }

Startup ConfigServices

Jesse博客学习笔记。传送门=》 http://video.jessetalk.cn/

Core篇——初探Core的认证,授权机制的更多相关文章

  1. Core篇——初探Core的Http请求管道&&Middleware

    目录: 1.Core 处理HTTP请求流程 2.中间件(Middleware)&&处理流程 3.创建自定义中间件&&模拟Core的请求管道 Core 处理HTTP请求流 ...

  2. Core篇——初探Core配置管理

    文章目录 1.命令行配置 2.Json文件配置 3.配置文件文本至C#对象实例的映射 4.配置文件热更新 5.总结 命令行的配置 我们首先来创建一个.net core 的控制台项目,然后引入.net ...

  3. Core篇——初探IdentityServer4(OpenID Connect模式)

    Core篇——初探IdentityServer4(OpenID Connect客户端验证) 目录 1.Oauth2协议授权码模式介绍2.IdentityServer4的OpenID Connect客户 ...

  4. Core篇——初探IdentityServer4(客户端模式,密码模式)

    Core篇——初探IdentityServer4(客户端模式,密码模式) 目录 1.Oatuth2协议的客户端模式介绍2.IdentityServer4客户端模式实现3.Oatuth2协议的密码模式介 ...

  5. .net core gRPC与IdentityServer4集成认证授权

    前言 随着.net core3.0的正式发布,gRPC服务被集成到了VS2019.本文主要演示如何对gRPC的服务进行认证授权. 分析 目前.net core使用最广的认证授权组件是基于OAuth2. ...

  6. 【ASP.NET Core学习】使用JWT认证授权

    概述 认证授权是很多系统的基本功能 , 在以前PC的时代 , 通常是基于cookies-session这样的方式实现认证授权 , 在那个时候通常系统的用户量都不会很大, 所以这种方式也一直很好运行, ...

  7. 温故知新,.Net Core遇见JWT(JSON Web Token)授权机制方案

    什么是JWT JWT (JSON Web Token) 是一个开放标准,它定义了一种以紧凑和自包含的方法,用于在双方之间安全地传输编码为JSON对象的信息. 因此,简单来说,它是JSON格式的加密字符 ...

  8. Core篇——初探依赖注入

    目录 1.DI&&IOC简单介绍 2.UML类图中六种关联关系 3..net core 中DI的使用 4..net core DI初始化源码初窥 DI&&IOC简单介绍 ...

  9. .net core 学习小结之 JWT 认证授权

    新增配置文件 { "Logging": { "IncludeScopes": false, "Debug": { "LogLeve ...

随机推荐

  1. 「Redis 笔记」数据类型

    REmote DIctionary Server(Redis),一个 key-value 存储系统. 数据类型 Redis 支持五种数据类型:string(字符串),hash(哈希),list(列表) ...

  2. WinAPI使用: 时间,线程,中断

    (1):C/C++获取当前系统时间:http://www.cnblogs.com/mfryf/archive/2012/02/13/2349360.html 不过当计算算法耗时的时候,不要忘记seco ...

  3. C# 1.将整个文件夹复制到目标文件夹中 2.将指定文件复制到指定目标文件夹中

    ].Items.Clear(); string filePath = Application.StartupPath; string sourcePath = Path.Combine(filePat ...

  4. Swift语法3.03(类型Types)

    类型 在Swift中,有两种类型:命名型类型和复合型类型.命名型类型是在定义时可以给定的特定名字的类型.命名型类型包括类,结构体,枚举和协议.例如,自定义的类MyClass的实例拥有类型MyClass ...

  5. Python 数据清洗--处理Nan

    参考:http://blog.sina.com.cn/s/blog_13050351e0102xfis.html https://www.sogou.com/link?url=DOb0bgH2eKh1 ...

  6. 常用CSS代码片段常见css bug

    1.禁止文字被选中 .unselectable { -moz-user-select: -moz-none; -khtml-user-select: none; -webkit-user-select ...

  7. Matrix(坑)

    https://github.com/florent37/Android-3D-Layout

  8. Spring MVC @PathVariable with dot (.) is getting truncated.

    转自:http://stackoverflow.com/questions/3526523/spring-mvc-pathvariable-getting-truncated (这里只截取了问题,和笔 ...

  9. JavaScript 数字字符串比较大小

    JavaScript中常用数字字符串比较 1.数字与数字之间直接比较 比较方式依据数学运算,没什么好说的. 2.数字与字符串数字之间直接比较 例如 数字5 与字符串数字'123',这种比较先将数字字符 ...

  10. PHP算法之判断是否是质数

    质数的定义 质数又称素数.一个大于1的自然数,除了1和它自身外,不能整除其他自然数的数叫做质数:否则称为合数. 实现思路 循环所有可能的备选数字,然后和中间数以下且大于等于2的整数进行整除比较,如果能 ...