ASP.NET Core 身份认证 (Identity、Authentication)
Authentication和Authorization
每每说到身份验证、认证的时候,总不免说提及一下这2个词。他们的看起来非常的相似,但实际上他们是不一样的。
Authentication想要说明白的是 你是谁(你的身份是什么)
Authorization想要说明白的是 你能做什么(得到了什么权限)
但是这两个词通常是要同时存在的。要知道有什么权限前提是知道你是谁。
OAuth2认证
这是最近很流行的认证的标准。要完全理解他的话也要说上一大篇,在这里简单点说明:
第三方网站能够得到认证方提供的身份和授予的权限。就是上面提到的Authorization
说个例子
这里似乎说个栗子会比较好,例如搭乘飞机:
假设你购买了一张南方航空的机票。那么你去坐飞机的时候可能会出现以下场景:
1.到南方航空的柜台checkin。得到一张纸质的,上面有你身份证信息,航班信息。
2.到入站口被检票人员查票。检票员会查看你的机票是否正确,机票身份信息是否与你的身份证信心对应。
3.到VIP休息室等待登机。被服务人员告知你并没有权限进入VIP休息室,原因是购买的是普通票,非贵宾票。
4.登机,入座。空乘人员核对你的航班是否对应当前的航班。
好了,上面的几个场景跟认证是相当的相似。第一步checkin,对应的是认证系统,纸质票就是提供的票据。第二步就相当于你自己的网站,得到了南方航空的认证,只要知道是南方航空颁发的票据,你都认为是有效的。这里也有个特别的地方,就是机场不可能只认南方航空,可能东方航空,春秋航空都认,所以这个也是认证的特点,你的网站是可以同时实现多个具有相同规则的认证方提供的票据。第三步相当于是权限的验证,虽然客户手上是有票据,但由于票据上声明(Claim)的权限并不包含VIP休息室使用。第四步相当于允许的权限,有这个票据,可以指定做某些可做的事情。
为什么要用
现在的服务基本上都是集群的,进行的网络通讯也以无状态请求为主。而OAuth2就很好地能实现单点登录。
就是一个地方登录了,只要使用OAuth2的规范,其他所有使用的服务器都能验证这个授权的正确性。
怎么实现
说了这么多,其实更加多的人是想知道怎么实现吧~
这里会说一下最简单的实现方法。我使用的是asp.net core的实现,会和用asp.net实现的方法有一点区别。但是也有很多相似的地方,例如都是利用中间件来实现的,只需要修改很少一部分就能在asp.net上使用。
整个Demo会包含2个项目:
一个用于认证,颁发票据的服务。
一个是受认证方,用于根据票据提供服务的网站
第一步:生成一个空的asp.net core的项目。在已经具备.net core环境下;
为什么要一个空的项目呢?因为这个项目实在简单,不必要生成一个MVC,我们重点是实现认证。
在指定的路径下,使用dotnet new web来创建,下面是创建之后的结构在VScode上查看的。可以看到是相当简单的。
可能有些人会有疑问,为什么项目文件是identity.csproj,不是json的后缀。其实是因为dotnet core 1.1已经升级了,为了使用MSBuild。
第二步,要把使用的包引用进来。打开项目文件identity.csproj。然后修改之后的文件如下:
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup> <ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="1.1.1"/>
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="1.1.1"/>
<PackageReference Include="Microsoft.NETCore.App" Version="1.1.1"/>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.1"/>
<PackageReference Include="Microsoft.AspNetCore.Routing" Version="1.1.1"/>
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.1"/>
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="1.1.1"/>
</ItemGroup> </Project>
最后的几个PackageReference就是引用的包了。这里由于没有使用IIS,所以只要引用Kestrel就可以部署了。加完引用包之后,是需要走一下dotnet restore。
完成之后我们继续第三步,重头戏来了,就是做一些认证的配置。代码如下:
public class Startup
{
public Startup(IHostingEnvironment env)
{
} // This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IUserValidate,UserValidate>();
} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(); if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} string secretKey = "encrypt_the_validate_site_key";
var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
var options = new TokenGenerateOption
{
Path = "/token",
Audience = "http://validateSite.woailibian.com",
Issuer = "http://thisSite.woailibian.com",
SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256),
Expiration = TimeSpan.FromMinutes(),
};
var userValidate = app.ApplicationServices.GetService<IUserValidate>();
// var userValidate = new UserValidate(); var tokenGenerator = new TokenGenerator(options, userValidate);
app.Map(options.Path, tokenGenerator.GenerateToken); app.Run(async (context) =>
{
await context.Response.WriteAsync("This Service only use for authentication! ");
});
} class UserValidate : IUserValidate
{
public UserModel GetUserByContext(string userName, string password)
{
UserModel rct = null;
if (userName == "moto" && password == "P@sw0rd123")
{
rct = new UserModel { UserName = userName, UniqueId = "" };
} return rct;
}
}
}
Startup.cs
这里有必要解释一下,我们从24行开始。SecretKey这里是一条key,应该是认证方需要对使用方公开的信息。这里的字符要进行UTF转换。
Path指的是通过认证方的哪一个地址进行认证,其他地址则会忽略。
Audience是使用方的信息,认证只有有可能是重定向地址。Issuer是认证方自己的信息,可以理解为拍照、商标(例如上面例子说道的南方航空票据上南航的商标)。
Expiration是指token的有效时间
tokenGenerator是我们自己建的生成token的类。
app.Map是asp.net core中间件的特性,只根据指定的地址进行处理,并且具体的执行的方法是tokenGenerator的GenerateToken。
app.Run也是asp.net core的特性,这里意思是任何请求,只要上层没有做处理,都会走到这。
第四步,建立TokenGenerator。
public class TokenGenerator
{
TokenGenerateOption _Option; public IUserValidate UserValidator { get; private set; }
public TokenGenerator(TokenGenerateOption option, IUserValidate validator)
{
_Option = option;
UserValidator = validator;
} async Task BadRequest(HttpContext context, string msg)
{
context.Response.StatusCode = ;
await context.Response.WriteAsync(msg);
} internal void GenerateToken(IApplicationBuilder app)
{
app.Run(async context =>
{
if (!context.Request.Method.Equals("POST") || !context.Request.HasFormContentType)
{
await BadRequest(context,"format not corrent");
return;
} var username = context.Request.Form["username"];
var password = context.Request.Form["password"]; var userModel = UserValidator?.GetUserByContext(username, password);
if (userModel == null)
{
await BadRequest(context, "Invalid username or password.");
return;
} var now = DateTime.UtcNow;
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, userModel.UniqueId),
new Claim(JwtRegisteredClaimNames.UniqueName,userModel.UserName),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, now.ToString(), ClaimValueTypes.Integer64)
}; var jwt = new JwtSecurityToken(_Option.Issuer, _Option.Audience, claims, now, now.Add(_Option.Expiration), _Option.SigningCredentials);
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt); var response = new
{
access_token = encodedJwt,
expires_in = (int)_Option.Expiration.TotalSeconds,
}; // Serialize and return the response
context.Response.ContentType = "application/json";
string responseStr = JsonConvert.SerializeObject(response, new JsonSerializerSettings { Formatting = Formatting.Indented });
await context.Response.WriteAsync(responseStr);
});
} }
TokenGenerator
GenerateToken里面继续使用app.Run这个特性。
之后必须判断是否是POST的方式,并且请求的content-type是否是form(这里是OAuth2的标准)
之后通过UserValidator用于验证用户的账号密码是否正确。通过验证之后的结果,来生成指定的Claim。
通过JwtSecurityToken来生成整个token的实体,并且进行Encode成一个字符串。
最终通过json序列化自己定义的一个匿名类。
看了前面两段代码,是还漏了一个接口,两个实体的。下面是他们的代码
public interface IUserValidate
{
UserModel GetUserByContext(string userName,string password);
}
public class UserModel
{
public string UserName { get; set; } public string UniqueId { get; set; }
}
public class TokenGenerateOption
{
public string Path { get; set; } public string Issuer { get; set; } public string Audience { get; set; } public TimeSpan Expiration { get; set; }
public SigningCredentials SigningCredentials { get; set; }
}
好了,identity(认证方)的项目基本上编码方面已经完成了。下面是现在的结构:
试一试
用dotnet run跑起来。之后用一些工具测试,例如Postman等等。下面是结果,可以看到有access_token和过期时间
去https://jwt.io验证,下面可以看到验证时解释了里面的内容。其实通过这个例子也想说明,其实认证方和接受方其实是没有通讯的,也能进行token的解密。
这可以解决多服务集群的单点登录问题。
写到这里本来是想做个使用方的demo,但是发现篇幅已经很长了。所以留到下一篇文章。
如果有经验的朋友看着这些代码,应该会觉得很丑陋。怎么这么粗糙地实现了,代码耦合度很高,并且无法做成一个类库,然后应用到不同的项目中。
其实这个是我专门做成这样的,为了用最简单粗暴的方法入门。OAuth2还有很多需要讲述的知识点,例如Refresh_token,配置文件公开等等。
这里主要是简述原理,做个演示。之后我会写一个事例,完全通过中间件实现的,这样包装之后就能多次应用了。
题外话:
最近在维护一个旧项目,里面的代码真的太恶心了。每个类都有5000行以上,10行重复的代码起码有20次。平均一个方法400行,里面通篇的region...
变量用拼音就算了,还用拼音缩写,还用中文....
里面能看到很多流行的模式,单例、工厂、仓储、Leader-Following等等。但是怎么说呢,觉得还是不要用好了~
单例里面的构造函数是public的,而且外面还真有地方实例化了。。。
工厂里面有很多的公共变量,而且这些变量在不同情境是要做不同的变化的。。。
仓储里面并没有具体的实现方法,是有几个Find,update,delete的方法。然后重点是。。。sql语句是在仓储之外写的,还有是linq也是。。。
Leader-Following这个写了根本没有,已经算最好的了。
话说虽然编码是为了给计算机识别,但是写代码的那个人也是说的人话,请在代码中写出人话!不然除了电脑,谁能看得懂!
说的人话,不是说要加多少注释,其实好的代码,一个屏幕页面,看到3、5个注释才是好的。因为别人光看代码行就懂了!
ASP.NET Core 身份认证 (Identity、Authentication)的更多相关文章
- 深入解读 ASP.NET Core 身份认证过程
长话短说:上文我们讲了 ASP.NET Core 基于声明的访问控制到底是什么鬼? 今天我们乘胜追击:聊一聊ASP.NET Core 中的身份验证. 身份验证是确定用户身份的过程. 授权是确定用户是否 ...
- asp.net core 身份认证/权限管理系统简介及简单案例
如今的网站大多数都离不开账号注册及用户管理,而这些功能就是通常说的身份验证.这些常见功能微软都为我们做了封装,我们只要利用.net core提供的一些工具就可以很方便的搭建适用于大部分应用的权限管理系 ...
- .NET 黑魔法 - asp.net core 身份认证 - Policy
身份认证几乎是每个项目都要集成的功能,在面向接口(Microservice)的系统中,我们需要有跨平台,多终端支持等特性的认证机制,基于token的认证方式无疑是最好的方案.今天我们就来介绍下在.Ne ...
- ASP.NET Core身份识别
Introduction to Identity 66 of 93 people found this helpful By Pranav Rastogi, Rick Anderson, Tom Dy ...
- ASP.NET Core Token认证
翻译:Token Authentication in ASP.NET Core 令牌认证(Token Authentication)已经成为单页应用(SPA)和移动应用事实上的标准.即使是传统的B/S ...
- 细说ASP.NET Forms身份认证
阅读目录 开始 ASP.NET身份认证基础 ASP.NET身份认证过程 如何实现登录与注销 保护受限制的页面 登录页不能正常显示的问题 认识Forms身份认证 理解Forms身份认证 实现自定义的身份 ...
- 简单的ASP.NET Forms身份认证
读了几篇牛人的此方面的文章,自己也动手做了一下,就想有必要总结一下.当然我的文章质量自然不能与人家相比,只是写给从没有接触过这个知识点的朋友. 网站的身份认证我以前只知道session,偶然发现一些牛 ...
- IE11下ASP.NET Forms身份认证无法保存Cookie的问题
IE11下ASP.NET Forms身份认证无法保存Cookie的问题 折腾了三四天,今天才找到资料,解决了. 以下会转贴,还没来得及深究,先放着,有空再学习下. ASP.NET中使用Forms身份认 ...
- asp.net core 自定义认证方式--请求头认证
asp.net core 自定义认证方式--请求头认证 Intro 最近开始真正的实践了一些网关的东西,最近写几篇文章分享一下我的实践以及遇到的问题. 本文主要介绍网关后面的服务如何进行认证. 解决思 ...
随机推荐
- java 获取访问主机的ip地址
Java的api的说法: getHeader public java.lang.String getHeader(java.lang.String name) Return the first val ...
- cocos2d-x在android真机上设置帧率无效的问题
通过setAnimationInterval设置帧频时,发现在android下没有效果的 在Cocos2dxRenderer .java文件里面找到了onDrawFrame这个函数.里面有句注释 : ...
- Codeforces 577B Modulo Sum:数学 结论【选数之和为m的倍数】
题目链接:http://codeforces.com/problemset/problem/448/C 题意: 给你n个数字,给定m. 问你是否能从中选出若干个数字,使得这些数字之和为m的倍数. 题解 ...
- Delphi中TList类应用
在DELPHI中指针最常见的就是和类TLIST结合起来使用.下面是一个很简单的例子,希望对这个例子的分析能让大家对使用TLIST类有一个简单的认识. 代码的功能是使用指针和Tlist来生成一个牌串,并 ...
- stl_multimap.h
stl_multimap.h // Filename: stl_multimap.h // Comment By: 凝霜 // E-mail: mdl2009@vip.qq.com // Blog: ...
- Ffmpeg转码研究一
Ffmpeg是一款功能强大的视频处理工具,那么转码肯定不是问题的,因为项目的需求,对转码进行了研究.刚开始首先去看了ffmpeg源代码中的一个例子transcode.c,但是发现该例子更应该称之为re ...
- windows 2013 datacenter 安装sql server2008 r2兼容性
add-windowsfeature RSAT-Clustering-AutomationServer
- 一:ORM关系对象映射(Object Relational Mapping,简称ORM)
狼来的日子里! 奋发博取 10)django-ORM(创建,字段类型,字段参数) 一:ORM关系对象映射(Object Relational Mapping,简称ORM) ORM分两种: DB fir ...
- 不同类型input尺寸设置区别
最近发现为不用类型的input设置相同的尺寸,却得到了不一样的尺寸结果.发现不同类型的input的height和width竟然含义不同.在此小整理一下. (1)button类型 规律 button类型 ...
- HDOJ1075字典翻译(map应用)
#include<iostream> #include<cstdio> #include<map> #include<string> #include& ...