Core篇——初探Core的认证,授权机制
目录
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中,
- [Authorize]
- public class AdminController : Controller
- {
- public IActionResult Index()
- {
- return View();
- }
- }
AdminController
- public class AccountController : Controller
- {
- public async Task<IActionResult> MakeLogin()
- {
- var claims = new List<Claim>
- {
- new Claim(ClaimTypes.Name,"lmc"),
- new Claim(ClaimTypes.Role, "admin")
- };
- var claimsIdentity = new ClaimsIdentity(
- claims,
- CookieAuthenticationDefaults.AuthenticationScheme
- );
- await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
- new ClaimsPrincipal(claimsIdentity),
- new AuthenticationProperties {
- IsPersistent=true, //cookie过期时间设置为持久
- ExpiresUtc= DateTime.UtcNow.AddSeconds() //设置过期20秒
- });
- return Ok();
- }
- public async Task<IActionResult> Logout()
- {
- await HttpContext.SignOutAsync(
- CookieAuthenticationDefaults.AuthenticationScheme);
- return Ok();
- }
- }
AccountController
然后我们配置Startup,将认证服务加入到DI容器&&引用认证中间件。
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
- .AddCookie(config=>
- {
- config.LoginPath = "/Account/MakeLogin"; //未认证导向登陆的页面,默认为/Account/Login
- config.Cookie.Name = "lmccookie"; //设置一个cookieName
- });
- services.AddMvc();
- }
- public void Configure(IApplicationBuilder app, IHostingEnvironment env)
- {
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- app.UseBrowserLink();
- }
- else
- {
- app.UseExceptionHandler("/Home/Error");
- }
- app.UseStaticFiles();
- app.UseAuthentication(); //加入认证中间件
- ...//// other code
- }
测试下。 直接访问 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状态码。
- public void ConfigureServices(IServiceCollection services)
- {
- services.Configure<JwtSettings>(Configuration.GetSection("JwtSettings")); //appsettings中读取到jwtsettings节点
- var jwtSetting = new JwtSettings();
- Configuration.Bind("JwtSettings", jwtSetting);
- services.AddAuthentication(options =>
- { // 添加认证头
- options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
- options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
- })
- .AddJwtBearer(jo => jo.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
- {
- ValidIssuer = jwtSetting.Issuer, //使用者
- ValidAudience = jwtSetting.Audience, //颁发者
- IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSetting.SecreKey)) //加密方式
- });
- services.AddMvc();
- }
- public void Configure(IApplicationBuilder app, IHostingEnvironment env)
- {
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
- app.UseAuthentication(); //注意加入中间件
Startup
- public class JwtSettings
- {
- public string Issuer { get; set; } //办法token的人
- public string Audience { get; set; } //token使用者
- public string SecreKey { get; set; } //token加密钥
- }
JwtSettings
- {
- "Logging": {
- "IncludeScopes": false,
- "Debug": {
- "LogLevel": {
- "Default": "Warning"
- }
- },
- "Console": {
- "LogLevel": {
- "Default": "Warning"
- }
- }
- },
- "JwtSettings": {
- "Audience": "http://localhost:5000",
- "Issuer": "http://localhost:5000",
- "SecreKey": "HelloKeylmclmclmc"
- }
- }
appsettings
接下来需要生成token,添加一个AuthorizeController,通过构造函数把jwtsettings 注入进来,添加一个Index的Action 用来生成我们的token。我们需要一个验证下登录用户的用户名密码,所以还需要添加一个ViewModel
- public class LoginViewModel
- {
- [Required]
- public string Name { get; set; }
- [Required]
- public string PassWord { get; set; }
- }
LoginViewModel
- private JwtSettings _jwtSettings;
- public AuthorizeController(IOptions<JwtSettings> options) //构造函数注入,拿到appsettings 里面的jwtsettings
- {
- _jwtSettings = options.Value;
- }
- [Route("api/token")]
- [HttpPost]
- public IActionResult Index(LoginViewModel loginViewModel)
- {
- if (!ModelState.IsValid)
- return BadRequest();
- if (!(loginViewModel.Name == "lmc" && loginViewModel.PassWord == ""))
- return BadRequest();
- var claims = new Claim[] //实例化一个Claim
- {
- new Claim(ClaimTypes.Name,"lmc"),
- new Claim(ClaimTypes.Role, "admin")
- };
- var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.SecreKey)); //将appsettings里面的SecreKey拿到
- var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); //使用HmacSha256 算法加密
- //生成token,设置过期时间为30分钟, 需要引用System.IdentityModel.Tokens.Jwt 包
- var token = new JwtSecurityToken(_jwtSettings.Issuer, _jwtSettings.Audience, claims, DateTime.Now, DateTime.Now.AddMinutes(), creds);
- //将token返回
- return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
- }
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容器中。这里我们添加了一个SuperAdminOnlyd的Policy
此时,我们需要在AuthorizeController控制器返回Token的时候,需要在Claim数组中添加一个新的Claim,表示我们的Policy。
此时,再修改我们的ValuesController控制器的Authorize特性标签。
PostMan 走一波===》
Identity +EF Authentication 的认证
首先,使用net core 的脚手架命令创建一个自带Identity 的mvc项目。然后根据appseetings的数据库配初始化数据库(默认数据库实例是(localdb)\\mssqllocaldb,因为我装vs时候没装这个,所以我换成了.) 。
还原完了数据库,就可以把项目跑起来了,可以根据右上角注册,登录下==》 (其中代码可以自行观看)
然后让我们来自己从头开始实现下这个过程:
可以在上文中Cookie认证的项目中完成,也可以新建一个空的MVC core项目。添加一个Account控制器,其中存在三个方法(action),注册、登录,登出。
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
- using Microsoft.AspNetCore.Mvc;
- using System.Security.Claims;
- using Microsoft.AspNetCore.Authentication.Cookies;
- using Microsoft.AspNetCore.Authentication;
- using MVCOnCookieBaseStudy.ViewModel;
- using Microsoft.AspNetCore.Identity;
- using MVCOnCookieBaseStudy.Models;
- namespace MVCOnCookieBaseStudy.Controllers
- {
- public class AccountController : Controller
- {
- private UserManager<ApplicationUser> _userManager; //加入Identity自带的注册使用的Manager
- private SignInManager<ApplicationUser> _signInManager; //加入Identity自带的登录使用的Manager
- public AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
- {
- _userManager = userManager;
- _signInManager = signInManager;
- }
- /// <summary>
- /// 注册页面
- /// </summary>
- /// <returns></returns>
- public IActionResult Register(string returnUrl = "/Home/Index")
- {
- ViewData["ReturnUrl"] = returnUrl;
- return View();
- }
- [HttpPost]
- public async Task<IActionResult> Register(RegisterViewModel registerViewModel, string returnUrl = "/Home/Index")
- {
- ViewData["ReturnUrl"] = returnUrl;
- if (ModelState.IsValid) //model 验证
- {
- ApplicationUser identityUser = new ApplicationUser
- {
- Email = registerViewModel.Email,
- UserName = registerViewModel.Email,
- NormalizedUserName = registerViewModel.Email
- };
- var result = await _userManager.CreateAsync(identityUser, registerViewModel.Password);
- if (result.Succeeded)
- {
- await _signInManager.SignInAsync(identityUser, new AuthenticationProperties { IsPersistent = true });
- return Redirect(returnUrl);
- }
- else
- {
- foreach(var err in result.Errors)
- {
- ModelState.AddModelError("", err.Description);
- }
- }
- }
- return View();
- }
- /// <summary>
- /// 登录页面
- /// </summary>
- /// <returns></returns>
- public IActionResult Login(string returnUrl = "/Home/Index")
- {
- ViewData["ReturnUrl"] = returnUrl;
- return View();
- }
- [HttpPost]
- public async Task<IActionResult> Login(RegisterViewModel LoginViewModel, string returnUrl = "/Home/Index")
- {
- ViewData["ReturnUrl"] = returnUrl;
- var loginUser = await _userManager.FindByEmailAsync(LoginViewModel.Email);
- if (loginUser == null)
- {
- return View();
- }
- await _signInManager.SignInAsync(loginUser, new AuthenticationProperties { IsPersistent = true });
- return Redirect(returnUrl);
- }
- /// <summary>
- /// 原来的Cookie登录
- /// </summary>
- /// <returns></returns>
- public async Task<IActionResult> MakeLogin()
- {
- var claims = new List<Claim>
- {
- new Claim(ClaimTypes.Name,"lmc"),
- new Claim(ClaimTypes.Role, "admin")
- };
- var claimsIdentity = new ClaimsIdentity(
- claims,
- CookieAuthenticationDefaults.AuthenticationScheme
- );
- await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
- new ClaimsPrincipal(claimsIdentity),
- new AuthenticationProperties {
- IsPersistent=true, //cookie过期时间设置为持久
- ExpiresUtc= DateTime.UtcNow.AddSeconds() //设置过期20秒
- });
- return Ok();
- }
- /// <summary>
- /// 登出
- /// </summary>
- /// <returns></returns>
- public async Task<IActionResult> Logout()
- {
- await _signInManager.SignOutAsync();
- return Redirect("/Home/Index");
- }
- }
- //覆盖默认验证
- public class MyCookieTestAuthorize: CookieAuthenticationEvents
- {
- public override Task ValidatePrincipal(CookieValidatePrincipalContext context)
- {
- return base.ValidatePrincipal(context);
- }
- }
- }
AccountController
- @using MVCOnCookieBaseStudy.ViewModel
- @model RegisterViewModel
- @{
- ViewData["Title"] = "Register";
- }
- <h2>@ViewData["Title"]</h2>
- <div class="row">
- <div class="col-md-4">
- <form asp-route-returnUrl="@ViewData["ReturnUrl"]" method="post">
- <h4>Create a new account.</h4>
- <hr />
- <div asp-validation-summary="All" class="text-danger"></div>
- <div class="form-group">
- <label asp-for="Email"></label>
- <input asp-for="Email" class="form-control" />
- <span asp-validation-for="Email" class="text-danger"></span>
- </div>
- <div class="form-group">
- <label asp-for="Password"></label>
- <input asp-for="Password" class="form-control" />
- <span asp-validation-for="Password" class="text-danger"></span>
- </div>
- <div class="form-group">
- <label asp-for="ConfirmPassword"></label>
- <input asp-for="ConfirmPassword" class="form-control" />
- <span asp-validation-for="ConfirmPassword" class="text-danger"></span>
- </div>
- <button type="submit" class="btn btn-default">Register</button>
- </form>
- </div>
- </div>
- @section Scripts {
- @await Html.PartialAsync("_ValidationScriptsPartial")
- }
注册的View
- @using MVCOnCookieBaseStudy.ViewModel
- @model RegisterViewModel
- @{
- ViewData["Title"] = "Login";
- }
- <h2>Login</h2>
- <div class="row">
- <div class="col-md-4">
- <section>
- <form asp-route-returnurl="@ViewData["ReturnUrl"]" method="post">
- <h4>Use a local account to log in.</h4>
- <hr />
- <div asp-validation-summary="All" class="text-danger"></div>
- <div class="form-group">
- <label asp-for="Email"></label>
- <input asp-for="Email" class="form-control" />
- <span asp-validation-for="Email" class="text-danger"></span>
- </div>
- <div class="form-group">
- <label asp-for="Password"></label>
- <input asp-for="Password" class="form-control" />
- <span asp-validation-for="Password" class="text-danger"></span>
- </div>
- <div class="form-group">
- <button type="submit" class="btn btn-default">Log in</button>
- </div>
- </form>
- </section>
- </div>
- </div>
- @section Scripts{
- @await Html.PartialAsync("_ValidationScriptsPartial"); @*前端验证,需要引用jquery.validate.min.js*@
- }
登录的View
这个过程中需要使用到一个ViewModel,用来传递登录数据
- using System;
- using System.Collections.Generic;
- using System.ComponentModel.DataAnnotations;
- using System.Linq;
- using System.Threading.Tasks;
- namespace MVCOnCookieBaseStudy.ViewModel
- {
- public class RegisterViewModel
- {
- [Required]
- [EmailAddress]
- [Display(Name = "Email")]
- public string Email { get; set; }
- [Required]
- [DataType(DataType.Password)]
- [StringLength(, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = )]
- [Display(Name = "Password")]
- public string Password { get; set; }
- [DataType(DataType.Password)]
- [Display(Name = "Confirm password")]
- [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
- public string ConfirmPassword { get; set; }
- }
- }
RegisterViewModel
添加集成自Identity的User 和 Role还有数据库链接上下文
- public class ApplicationUserRole: IdentityRole
- {
- }
- public class ApplicationUser:IdentityUser
- {
- }
Role&&User
- public class ApplicationDbContext:IdentityDbContext<ApplicationUser,ApplicationUserRole,string>
- {
- public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
- {
- }
- }
数据库链接上下文
接下来配置我们的StartUp,在ConfigureServices方法中 将数据库连接上下文加入DI容器,Identity服务加入DI容器,重新配置下我们的密码规则。(注意在Configure加入认证中间件)
- public void ConfigureServices(IServiceCollection services)
- {
- //连接数据库服务加入DI容器
- services.AddDbContext<ApplicationDbContext>(option =>
- {
- option.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
- });
- //将Identity服务加入DI容器
- services.AddIdentity<ApplicationUser, ApplicationUserRole>()
- .AddEntityFrameworkStores<ApplicationDbContext>()
- .AddDefaultTokenProviders();
- //设置注册密码的规则
- services.Configure<IdentityOptions>(options =>
- {
- options.Password.RequireLowercase = false;
- options.Password.RequireNonAlphanumeric = false;
- options.Password.RequireUppercase = false;
- });
- services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
- .AddCookie(config=>
- {
- config.LoginPath = "/Account/Login"; //未认证导向登陆的页面,默认为/Account/Login
- config.Cookie.Name = "lmccookie"; //设置一个cookieName
- });
- services.AddMvc();
- }
Startup ConfigServices
0KKV1UVBV6]FKN.png)





0KKV1UVBV6]FKN.png)
0KKV1UVBV6]FKN.png)


Core篇——初探Core的认证,授权机制的更多相关文章
- Core篇——初探Core的Http请求管道&&Middleware
目录: 1.Core 处理HTTP请求流程 2.中间件(Middleware)&&处理流程 3.创建自定义中间件&&模拟Core的请求管道 Core 处理HTTP请求流 ...
- Core篇——初探Core配置管理
文章目录 1.命令行配置 2.Json文件配置 3.配置文件文本至C#对象实例的映射 4.配置文件热更新 5.总结 命令行的配置 我们首先来创建一个.net core 的控制台项目,然后引入.net ...
- Core篇——初探IdentityServer4(OpenID Connect模式)
Core篇——初探IdentityServer4(OpenID Connect客户端验证) 目录 1.Oauth2协议授权码模式介绍2.IdentityServer4的OpenID Connect客户 ...
- Core篇——初探IdentityServer4(客户端模式,密码模式)
Core篇——初探IdentityServer4(客户端模式,密码模式) 目录 1.Oatuth2协议的客户端模式介绍2.IdentityServer4客户端模式实现3.Oatuth2协议的密码模式介 ...
- .net core gRPC与IdentityServer4集成认证授权
前言 随着.net core3.0的正式发布,gRPC服务被集成到了VS2019.本文主要演示如何对gRPC的服务进行认证授权. 分析 目前.net core使用最广的认证授权组件是基于OAuth2. ...
- 【ASP.NET Core学习】使用JWT认证授权
概述 认证授权是很多系统的基本功能 , 在以前PC的时代 , 通常是基于cookies-session这样的方式实现认证授权 , 在那个时候通常系统的用户量都不会很大, 所以这种方式也一直很好运行, ...
- 温故知新,.Net Core遇见JWT(JSON Web Token)授权机制方案
什么是JWT JWT (JSON Web Token) 是一个开放标准,它定义了一种以紧凑和自包含的方法,用于在双方之间安全地传输编码为JSON对象的信息. 因此,简单来说,它是JSON格式的加密字符 ...
- Core篇——初探依赖注入
目录 1.DI&&IOC简单介绍 2.UML类图中六种关联关系 3..net core 中DI的使用 4..net core DI初始化源码初窥 DI&&IOC简单介绍 ...
- .net core 学习小结之 JWT 认证授权
新增配置文件 { "Logging": { "IncludeScopes": false, "Debug": { "LogLeve ...
随机推荐
- 「Redis 笔记」数据类型
REmote DIctionary Server(Redis),一个 key-value 存储系统. 数据类型 Redis 支持五种数据类型:string(字符串),hash(哈希),list(列表) ...
- WinAPI使用: 时间,线程,中断
(1):C/C++获取当前系统时间:http://www.cnblogs.com/mfryf/archive/2012/02/13/2349360.html 不过当计算算法耗时的时候,不要忘记seco ...
- C# 1.将整个文件夹复制到目标文件夹中 2.将指定文件复制到指定目标文件夹中
].Items.Clear(); string filePath = Application.StartupPath; string sourcePath = Path.Combine(filePat ...
- Swift语法3.03(类型Types)
类型 在Swift中,有两种类型:命名型类型和复合型类型.命名型类型是在定义时可以给定的特定名字的类型.命名型类型包括类,结构体,枚举和协议.例如,自定义的类MyClass的实例拥有类型MyClass ...
- Python 数据清洗--处理Nan
参考:http://blog.sina.com.cn/s/blog_13050351e0102xfis.html https://www.sogou.com/link?url=DOb0bgH2eKh1 ...
- 常用CSS代码片段常见css bug
1.禁止文字被选中 .unselectable { -moz-user-select: -moz-none; -khtml-user-select: none; -webkit-user-select ...
- Matrix(坑)
https://github.com/florent37/Android-3D-Layout
- Spring MVC @PathVariable with dot (.) is getting truncated.
转自:http://stackoverflow.com/questions/3526523/spring-mvc-pathvariable-getting-truncated (这里只截取了问题,和笔 ...
- JavaScript 数字字符串比较大小
JavaScript中常用数字字符串比较 1.数字与数字之间直接比较 比较方式依据数学运算,没什么好说的. 2.数字与字符串数字之间直接比较 例如 数字5 与字符串数字'123',这种比较先将数字字符 ...
- PHP算法之判断是否是质数
质数的定义 质数又称素数.一个大于1的自然数,除了1和它自身外,不能整除其他自然数的数叫做质数:否则称为合数. 实现思路 循环所有可能的备选数字,然后和中间数以下且大于等于2的整数进行整除比较,如果能 ...