在普通的MVC项目中 我们普遍的使用Cookie来作为认证授权方式,使用简单。登录成功后将用户信息写入Cookie;但当我们做WebApi的时候显然Cookie这种方式就有点不适用了。

在.NET Core 中 WebApi中目前比较流行的认证授权方式是Jwt  (Json Web Token) 技术。Jwt 是一种无状态的分布式身份验证方式,Jwt 是将用户登录信息加密后存放到返回的Token中 ,相当于用户信息是存储在客户端。Jwt的加密方式有两种 :对称加密与非对称加密,非对称加密即 RSA 加密的方式。

自己手写认证授权代码和Jwt的思路是一样的;不同之处在于:

1、加密方式仅仅是采用的对称加密方式 简单高效。哈哈!(弊端就是没有非对称加密更安全);

2、用户登录信息主要保存在Redis中,即服务端。

自己写的好处:

1、扩展性强,可根据自己的需要进行各种扩展,比如在验证授权时可很方便的添加多设备登录挤下线功能等。

2、可随时调整用户的Token失效时间。

认证及授权流程

1、先请求登录接口,登录成功,为用户产生一个Token,

登录获取Token 图片中ticket字段。

2、 客户端拿到Token在其他请求中将Token信息添加到请求头中传递到服务端。

开发思路

1、添加一个过滤器。在Startup 中ConfigureServices方法里添加一个Filters 即我们自己授权代码类。

public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddMvc(mvc =>
{
//添加自己的授权验证
mvc.Filters.Add(typeof(AuthorizeFilter));
});
}

具体详细代码,请看文章结尾github地址。

  

添加过滤器之后我们的每次请求都会优先执行过滤器的代码。在这里我们就可以判断用户是否已经登录,从而进行拦截没有授权的的请求。

    /// <summary>
/// 安全认证过滤器
/// </summary>
public class AuthorizeFilter : IActionFilter, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
//允许匿名访问
if (context.HttpContext.User.Identity.IsAuthenticated ||
context.Filters.Any(item => item is IAllowAnonymousFilter))
return;
var httpContext = context.HttpContext;
var claimsIdentity = httpContext.User.Identity as ClaimsIdentity;
var request = context.HttpContext.Request;
var authorization = request.Headers["Authorization"].ToString();
if (authorization != null && authorization.Contains("BasicAuth"))
{
//获取请求头中传递的ticket
var current_ticket = authorization.Split(" ")[];
//校验ticket并获取用户信息
var userInfo = TicketEncryption.VerifyTicket(current_ticket, out string dec_client);
if (userInfo != null)
{
//同一个终端多次登录挤下线功能 返回403
if (userInfo.ticket != current_ticket && userInfo.client.ToString() == dec_client)
{
#region 多设备挤下线代码
var response = new HttpResponseMessage();
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
context.Result = new JsonResult("Forbidden:The current authorization has expired");
#endregion return;
}
else
{
return;
}
} }
// 401 未授权
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
context.Result = new JsonResult("Forbidden:Tiket Invalid"); }
}

具体详细代码,请看文章结尾github地址。

2、登录并获取Token

由于添加了IAuthorizationFilter类型的过滤器,所以每个请求都会被拦截。所以登录接口我们需要允许匿名访问。

3、加解密Token

加密:登录成功后就要产生个Token了,产生也简单。将用户的唯一信息比如uid或者guid进行对称式加密。当然如果需要对登录设备做区分或者多设备登录挤下线功能时最好也将登录设备一起加密进去。

我们都知道 在加密中一般情况下只要加密的数据及加密key不变;那么加密后的内容也会一直保持不变。如果我们每次登录产生的Token一直没有任何变化只要这个Token被泄露了那将很危险的。竟然我们希望每次登录产生的Token都有变化。那就要改变加密数据或者加密key了。加密数据是用户唯一信息这个显然不可能产生变化。所以我们能改变的地方只能是加密key了;我们采用固定key+随机key的方式。

因为加密key在我们解密时也是需要一一对应的。所以我们得想办法将我们的随机key告诉我们解密的代码中。办法就是 我们将加密后的内容(一般情况进行base64编码)再加上随机key。(随机key一定是固定长度 不然后面无法解析拆分)

比如加密内容是guid=73e01eab-210d-4d19-a72a-d0d64e053ec0+client=ios  固定key=123654+随机key=FEZaaWbyimaWiJHah

即加密过程:

加密(73e01eab-210d-4d19-a72a-d0d64e053ec0&ios,123654FEZaaWbyimaWiJHah)=M0EzM0ZGRjk2QzgwRDY2RDJDMTdFOEJGRUE0NDI3NEE1RDlFNkU4NDQ0MERFNEIyMkQ5QjM4MjAxODcwj加随机key:FEZaaWbyimaWiJHah

所以我们返回给用户的Token实际上是包含了随机key的。当然这个随机key只有我们自己知道。因为随机key的长度以及位置只有我们自己知道。这种方式即使我们固定key被泄露了 只要别人不知道我们随机key处理方法也无济于事。

解密:知道加密过程后就好解密了。拿到用户提交的Token后首先按照随机key的固定位置进行截取。将加密内容和随机key拆开。然后将固定key和随机key组合一起解密加密的内容,取得用户guid和登录的客户端类型。

完整加解密代码

代码中的ticket代表本文中的Token。代码中使用的是DES加解密

 public class TicketEncryption
{
//加密key 实际中请用配置文件配置
private static readonly string key = "yvDlky7GXGtlPCGr";
/// <summary>
/// 获取一个新的ticket
/// </summary>
/// <param name="guid">用户的guid</param>
/// <param name="client">客户端</param>
/// <returns></returns>
public static string GenerateTicket(string guid, string client)
{
//随机key
string randomKey = Randoms.GetRandomString();
var keys = key + randomKey;
var desStr = Encryption.DesEncrypt(guid + "&" + client, keys);
var base64Str = Encryption.Base64Encrypt(desStr) + randomKey;
return base64Str;
} /// <summary>
/// 校验ticket
/// </summary>
/// <param name="encryptStr"></param>
/// <returns></returns>
public static UserInfo VerifyTicket(string encryptStr,out string client)
{
try
{
RedisHelper redisHelper = new RedisHelper("127.0.0.1:6379");
//加密原型:guid&client; 如:08e80f78-95ad-427c-b506-a5f1504e29ac&ios
string randomKey = encryptStr.Substring(encryptStr.Length - );
var base64 = encryptStr.Substring(, encryptStr.Length - );
var deBase64 = Encryption.Base64Decrypt(base64);
var keys = key + randomKey;
string ticketInfo = Encryption.DesDecrypt(deBase64, keys);
var guid = ticketInfo.Split("&")[];
client = ticketInfo.Split("&")[];
string redisKey = "ticket_" + guid;
var obj = redisHelper.Get<UserInfo>(redisKey);
return obj;
}
catch (Exception ex)
{
throw ex;
}
}
}

完整demo代码请看github

https://github.com/cfan1236/NetCoreAuthorize

手写DotNet Core 认证授权代码的更多相关文章

  1. SpringSecurity(1)---认证+授权代码实现

    认证+授权代码实现 Spring Security是 一种基于 Spring AOP 和 Servlet 过滤器的安全框架.它提供全面的安全性解决方案,同时在 Web 请求级和方法调用级处理身份确认和 ...

  2. 4.2tensorflow多层感知器MLP识别手写数字最易懂实例代码

    自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取: https://www.cnblogs.com/bclshuai/p/11380657.html 1.1  多层感知器MLP(m ...

  3. 手写笔记变PDF-几行代码变命令行程序为图形化界面

    前言 最近发现了一个非常不错的Python类库----Gooey, https://github.com/chriskiehl/Gooey 在它的帮助下我们可以非常方便的将一个命令行程序升级成一个图形 ...

  4. 手写数字0-9的识别代码(SVM支持向量机)

    帮一个贴吧的朋友改的一段代码,源代码来自<机器学习实战> 原代码的功能是识别0和9两个数字 经过改动之后可以识别0~9,并且将分类器的产生和测试部分分开来写,免得每次测试数据都要重新生成分 ...

  5. .net core 认证与授权(三)

    前言 在写三上是在一的基础上写的,所以有没有看过二是没得关系的,在一中介绍了认证与授权,但是没有去介绍拿到证书后怎样去验证授权. 概念性东西:在这套机制中,把这个权限认证呢,称作为policy.这个p ...

  6. 手写RPC框架指北另送贴心注释代码一套

    Angular8正式发布了,Java13再过几个月也要发布了,技术迭代这么快,框架的复杂度越来越大,但是原理是基本不变的.所以沉下心看清代码本质很重要,这次给大家带来的是手写RPC框架. 完整代码以及 ...

  7. 常见的JS手写函数汇总(代码注释、持续更新)

    最近在复习面试中常见的JS手写函数,顺便进行代码注释和总结,方便自己回顾也加深记,内容也会陆陆续续进行补充和改善. 一.手写深拷贝 <script> const obj1 = { name ...

  8. Spring security OAuth2.0认证授权学习第三天(认证流程)

    本来之前打算把第三天写基于Session认证授权的,但是后来视屏看完后感觉意义不大,而且内容简单,就不单独写成文章了; 简单说一下吧,就是通过Servlet的SessionApi 通过实现拦截器的前置 ...

  9. springcloud-alibaba手写负载均衡的坑,采用restTemplate,不能添加@loadbalanced注解,否则采用了robbin

    采用springcloud-alibaba整合rabbion使用DiscoveryClient调用restful时遇到的一个问题,报错如下: D:\javaDevlepTool\java1.8\jdk ...

随机推荐

  1. UED团队规范设计参考及建议

    公司产品线逐渐增多,变动频繁且并行开发,常常需要设计与开发能够快速的做出响应.同时这类产品中有存在很多类似的页面以及组件,可以通过抽象得到一些稳定且高复用性的内容.通过模块化的解决方案,降低冗余的生产 ...

  2. HTML 标签小细节

    简书地址:https://www.jianshu.com/p/03a23aa28a34 今天重新学习了一下HTML中标签的用法,补充并记录一下自己新学到的知识. a中的href href Contai ...

  3. Django REST framework+Vue 打造生鲜超市(十三)

    目录 生鲜超市(一)    生鲜超市(二)    生鲜超市(三) 生鲜超市(四)    生鲜超市(五)    生鲜超市(六) 生鲜超市(七)    生鲜超市(八)    生鲜超市(九) 生鲜超市(十) ...

  4. Hibernate Session总结

    现在我们可以在 IDEA 下新建一个 Hibernate 项目,接着上次内容这次主要总结一下 Hibernate 的 Session,及其核心方法. Session 概述 Session 接口是 Hi ...

  5. PAT1017:Queueing at Bank

    1017. Queueing at Bank (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue Supp ...

  6. C++相关:动态内存和智能指针

    前言 在C++中,动态内存的管理是通过运算符new和delete来完成的.但使用动态内存很容易出现问题,因为确保在正确的时间释放内存是及其困难的.有时候我们会忘记内存的的释放,这种情况下就会产生内存泄 ...

  7. mac下nginx安装

    一.安装 Nginx 终端执行: brew search nginx brew install nginx 当前版本 1.10.2,通过brew可以把nginx需要的pcre,openssl,zlib ...

  8. Python初级教程

    Python语言的特点 优点: - 简单 - 易学 - 免费,开源 - 高层语言 - 可移植性(可再多平台运行) - 解释性(不需要编译,可直接运行) - 面向对象 - 可扩展性(缺点:运行效率相对较 ...

  9. SQL Server 2008更改数据库保存路径

    本文由荒原之梦原创,原文链接:http://zhaokaifeng.com/?p=641 操作环境: WindowsXP 数据库: Microsoft SQL Server 2008 操作步骤: 选中 ...

  10. java.lang.IllegalArgumentException: Request header is too large

    tomcat运行项目时,有一个请求过去后,后台报这样的错java.lang.IllegalArgumentException: Request header is too large原因:请求头超过了 ...