UUID含义是 通用唯一识别码 (Universally Unique Identifier),这 是一个软件建构的标准,也是被开源软件基金会 (Open Software Foundation, OSF) 的组织应用在分布式计算环境 (Distributed Computing Environment, DCE) 领域的一部分。 UUID 的目的,是让分布式系统中的所有元素,都能有唯一的辨识资讯,而不需要透过中央控制端来做辨识资讯的指定。如此一来,每个人都可以建立不与其它人冲突的 UUID。在这样的情况下,就不需考虑数据库建立时的名称重复问题。目前最广泛应用的 UUID,即是微软的 Microsoft’s Globally Unique Identifiers (GUIDs),而其他重要的应用,则有 Linux ext2/ext3 档案系统、LUKS 加密分割区、GNOME、KDE、Mac OS X 等等。 UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。通常平台会提供生成的API。按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字 UUID由以下几部分的组合: (1)当前日期和时间,UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同。 (2)时钟序列。 (3)全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得。 UUID的唯一缺陷在于生成的结果串会比较长。关于UUID这个标准使用最普遍的是微软的GUID(Globals Unique Identifiers)。在ColdFusion中可以用CreateUUID()函数很简单地生成UUID,其格式为:xxxxxxxx-xxxx- xxxx-xxxxxxxxxxxxxxxx(8-4-4-16),其中每个 x 是 0-9 或 a-f 范围内的一个十六进制的数字。 而 标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx (8-4-4-4-12),可以从cflib 下载CreateGUID() UDF进行转换。 第一种,PHP代码如下: function create_guid(){ $microTime = microtime(); list($a_dec, $a_sec) = explode(" ", $microTime); $dec_hex = dechex($a_dec* 1000000); $sec_hex = dechex($a_sec); ensure_length($dec_hex, 5); ensure_length($sec_hex, 6); $guid = ""; $guid .= $dec_hex; $guid .= create_guid_section(3); $guid .= '-'; $guid .= create_guid_section(4); […]
View Details1、求两个日期的差数,例如2007-3-5 ~ 2007-3-6 的日期差数 echo abs(strtotime("2007-3-5") – strtotime("2007-3-6"))/60/60/24; echo "天<br>"; 2、JS 获取当前日期 var myDate = new Date(); myDate.getYear(); //获取当前年份(2位) myDate.getFullYear(); //获取完整的年份(4位,1970-????) myDate.getMonth(); //获取当前月份(0-11,0代表1月) myDate.getDate(); //获取当前日(1-31) myDate.getDay(); //获取当前星期X(0-6,0代表星期天) myDate.getTime(); //获取当前时间(从1970.1.1开始的毫秒数) myDate.getHours(); //获取当前小时数(0-23) myDate.getMinutes(); //获取当前分钟数(0-59) myDate.getSeconds(); //获取当前秒数(0-59) myDate.getMilliseconds(); //获取当前毫秒数(0-999) myDate.toLocaleDateString(); //获取当前日期 var mytime=myDate.toLocaleTimeString(); //获取当前时间 myDate.toLocaleString( ); //获取日期与时间 3、PHP 日期与时间戳相互转化 PHP时间大的来分有两种,一是时间戳类型(1228348800),二是正常日期格式(2008-12-4) 所以存到数据库也有两种形式了(真正不止,我的应用就两种),时间戳类型我是保存为字符串的,这个是比较方便的. 正常日期类型是保存为DATE型的. 这两个要注意一下,我平时用两种,所以,前几天建的表,把时间类型存为DATE的,我还一直用时间戳保存,一直资料写不进表,调试了好久的才知错误之处,是类型不符合,不给写入库. 像上面的时间戳是比较方便,但,显示的时候,不至于把这个1228348800串给客户看吧,GOD NOWS! 所以就用到了这两个的转换,先说一下如何取得当前的时间戳,$date1=time();这样就取得当前的时间戳了,要转回2008-12-4这种格式 呢, 用到date()这个函数了,在PHP中date()函数比较常用,如取得当前日期,可以用$date2=date('Y-m-d');,关于里面参数的 意思,如不懂就查一下php的手册了. 好,言归正传,把1228348800转成2008-12-4格式代码如下: $date3=date('Y-m-d H:i:s',"1228348800"); 这样就OK了,如还想得到小时,分钟秒,只要把’Y-m-d’改一下就可以了,不过要注意一下,PHP时间还像有8个小时的误差.加上就OK了. 时间戳转正常日期有了,反之呢,把正常日期格式转为时间戳呢,请看如下代码: $year=((int)substr("2008-12-04",0,4));//取得年份 $month=((int)substr("2008-12-04",5,2));//取得月份 $day=((int)substr("2008-12-04",8,2));//取得几号 echo mktime(0,0,0,$month,$day,$year); 这样就能把正常日期转成时间戳了,这里如果有时分秒也是同理, 注意:php5.1以上时间戳会与实际时间相差8小时,解决办法如下 1、最简单的方法就是不要用php5.1以上的版本--显然这是不可取的方法!!! 2、修改php.ini。打开php.ini查找date.timezone 去掉前面的分号= 后面加Asia/Shanghai,重启apache服务器即可--缺点就是如果程序 放到别人的服务器上,不能修改php.ini,那就奈何不得了。 3、在程序中添加时间的初始化的语句的 即:“date_default_timezone_set("Asia/Shanghai"); ”这个可以由程序员任意设置,我的推 荐。 时区标识符,大陆内地可用的值是:PRC,Asia/Chongqing ,Asia/Shanghai ,Asia/Urumqi (依次为中国,重庆,上海,乌鲁木齐) ,Etc/GMT-8,Asia/Harbin 港台地区可用:Asia/Macao ,Asia/Hong_Kong ,Asia/Taipei (依次为澳门,香港,台北) […]
View Detailsmysql出现ERROR : (2006, 'MySQL server has gone away') 的问题意思就是指client和MySQL server之间的链接断开了。 造成这样的原因一般是sql操作的时间过长,或者是传送的数据太大(例如使用insert … values的语句过长, 这种情况可以通过修改max_allowed_packed的配置参数来避免,也可以在程序中将数据分批插入)。 产生这个问题的原因有很多,总结下网上的分析: 原因一. MySQL 服务宕了 判断是否属于这个原因的方法很简单,进入mysql控制台,查看mysql的运行时长 mysql> show global status like 'uptime'; +—————+———+ | Variable_name | Value | +—————+———+ | Uptime | 3414707 | +—————+———+ 1 row in set或者查看MySQL的报错日志,看看有没有重启的信息 如果uptime数值很大,表明mysql服务运行了很久了。说明最近服务没有重启过。 如果日志没有相关信息,也表名mysql服务最近没有重启过,可以继续检查下面几项内容。 原因二. mysql连接超时 即某个mysql长连接很久没有新的请求发起,达到了server端的timeout,被server强行关闭。 此后再通过这个connection发起查询的时候,就会报错server has gone away (大部分PHP脚本就是属于此类) mysql> show global variables like '%timeout'; +—————————-+———-+ | Variable_name | Value | +—————————-+———-+ | connect_timeout | 10 | | delayed_insert_timeout | 300 | | innodb_lock_wait_timeout | 50 | | innodb_rollback_on_timeout | OFF | | interactive_timeout | […]
View Detailsnginx使用proxy模块时,默认的读取超时时间是60s。 1. send_timeout syntax: send_timeout the time default: send_timeout 60 context: http, server, location Directive assigns response timeout to client. Timeout is established not on entire transfer of answer, but only between two operations of reading, if after this time client will take nothing, then nginx is shutting down the connection. 2. 负载均衡配置时的2个参数:fail_timeout和max_fails 这2个参数一起配合,来控制nginx怎样认为upstream中的某个server是失效的当在fail_timeout的时间内,某个server连接失败了max_fails次,则nginx会认为该server不工作了。同时,在接下来的 fail_timeout时间内,nginx不再将请求分发给失效的server。 个人认为,nginx不应该把这2个时间用同一个参数fail_timeout来控制,要是能再增加一个fail_time,来控制接下来的多长时间内,不再使用down掉的server就更好了~ 如果不设置这2个参数,fail_timeout默认为10s,max_fails默认为1。就是说,只要某个server失效一次,则在接下来的10s内,就不会分发请求到该server上 3. proxy模块的 proxy_connect_timeout syntax: proxy_connect_timeout timeout_in_seconds context: http, server, location This directive assigns a timeout for the connection to the proxyserver. This is not the time until the server returns the pages, this is the […]
View Details使用html属性标记,代码如下: <div id="myCarousel" class="carousel"> <!-- 要切换的内容--> <div class="carousel-inner"> <div class="active item">…</div> <div class="item">…</div> <div class="item">…</div> </div> <!-- 切换导航 --> <a class="carousel-control left" href="#myCarousel" data-slide="prev">‹</a> <a class="carousel-control right" href="#myCarousel" data-slide="next">›</a> </div> 也可以在使用了.carousel类的元素内部添加切换内容指示工具 <!-- 焦点图指示器--> <ol class="carousel-indicators"> <li data-target="#myCarousel" data-slide-to="0" class="active"></li> <li data-target="#myCarousel" data-slide-to="1"></li> <li data-target="#myCarousel" data-slide-to="2"></li> </ol> 方法 .carousel(options) 初始化轮播组件,接受一个对象做为可选参数并开始循环播放. 例如: $(".carousel").carousel({ interval:2000 }) .carousel('cycle') 从左向右循环播放 .carousel('pause')中止播放 .carousel(number)播放到某个特定的帧(与数组类似,从0开始计数). .carousel('prev') 回到上一帧 .carousel('next') 回到下一帧 轮播类提供了两个事件 slide 该事件在调用slide实例方法时立刻触发 slid 该事件在轮播项切换效果完成之后触发 本实例代码如下: <style>/*metro风格样式*/ div, input, select, textarea, span, img, table, td, th, p, a, button, ul, li { border–radius: 0 0 0 0 !important; } [class^="m-icon-"] { display: inline–block; width: 14px; height: 14px; margin–top: 4px; line–height: 14px; vertical–align: top; background–image: url(http://www.kuiyu.net/content/bootstrap/img/syncfusion-icons.png); background–position: 0 0; background–repeat: no–repeat; } [class^="m-icon-big-"] { display: inline–block; width: 30px; height: 30px; margin: 6px; vertical–align: top; background–image: url(http://www.kuiyu.net/content/bootstrap/img/syncfusion-icons.png); background–position: 0 0px; background–repeat: no–repeat; } .m–icon–white { background–image: url(http://www.kuiyu.net/content/bootstrap/img/syncfusion-icons-twhite.png); } /* Misc */ .btn.icn–only { min–width: 14px; } .btn.bigicn–only { min–width: 34px; } .m–icon–swapright { background–position: –27px –10px; […]
View Details在和银行做数据对接时,涉及到数据传输时的验签及加密。其中数据签名方案中就要求数据项根据属性名按 ASCII码 进行升序排序。C#中的ASCII码排序并不是表面上那么简单,一不小心就入坑了。因为C#的排序默认并不是按照ASCII码进行排序的。举个例子, 我有这样一个字符串数组,然后对其排序。
1 2 3 |
string[] vv = { "1", "2", "A", "a", "B", "b" }; Array.Sort(vv); //结果 1 2 a A b B |
如果是按照ASCII码进行排序的话,顺序应该是: 1, 2, A, B, a, b 而实际排序后的结果则是:1, 2, a, A, b, B . 这也就是说Sort()方法默认情况下并不是按ASCII码进行排序的。之后我也同样测试了C#中的OrderBy()的排序,发现它默认情况下也并不是按照ASCII码进行的排序。
1 2 3 |
string[] vv = { "1", "2", "A", "a", "B", "b" }; vv.OrderBy(x => x); //结果 1 2 a A b B |
那么既然默认排序不是按ASCII码进行的排序,我们要怎么做呢? 看下面代码,只需要在原来排序方法上再加个参数: string.CompareOrdinal。string.CompareOrdinal会把每个字符先转成相应的数值(如 a 转为数值 97),然后再对数值进行比较。
1 |
Array.Sort(vv, string.CompareOrdinal); //ASCII排序 |
注:掉入这个坑是因为起初不知道如何对字符做ASCII码排序,于是百度了一把。得到的结果就是这个 C# 参数按照ASCII码从小到大排序(字典序) 而当我采用这种方式时,银行验签那步始终通不过,调试发现我排序后的结果和银行那边的不同。这篇博文的博主可能自己也没发现这个坑吧。 from:http://www.cnblogs.com/similar/p/6739293.html
View Details前言 上篇博文介绍了怎么样在 asp.net core 中使用中间件,以及如何自定义中间件。项目中刚好也用到了Redis,所以本篇就介绍下怎么样在 asp.net core 中使用 Redis 进行资源缓存和Session缓存。 如果你觉得对你有帮助的话,不妨点个【推荐】。 目录 Redis 介绍 asp.net core Session 介绍 Redis & Session 实例讲解 Session的使用 使用 Protobuf 给 Session添加扩展方法 Redis 介绍 下面是Redis官网的介绍: Redis is an open source (BSD licensed), in-memory data structure store, used as database, cache and message broker. It supports data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs and geospatial indexes with radius queries. Redis has built-in replication, Lua scripting, LRU eviction, transactions and different levels of on-disk persistence, and provides […]
View Details前言 接【中篇】,在有一些场景下,我们需要对 ASP.NET Core 的加密方法进行扩展,来适应我们的需求,这个时候就需要使用到了一些 Core 提供的高级的功能。 本文还列举了在集群场景下,有时候我们需要实现自己的一些方法来对Data Protection进行分布式配置。 加密扩展 IAuthenticatedEncryptor 和 IAuthenticatedEncryptorDescriptor IAuthenticatedEncryptor是 Data Protection 在构建其密码加密系统中的一个基础的接口。 一般情况下一个key 对应一个IAuthenticatedEncryptor,IAuthenticatedEncryptor封装了加密操作中需要使用到的秘钥材料和必要的加密算法信息等。 下面是IAuthenticatedEncryptor接口提供的两个 api方法:
1 2 |
Decrypt(ArraySegment<<span class="hljs-keyword">byte</span>> ciphertext, ArraySegment<<span class="hljs-keyword">byte</span>> additionalAuthenticatedData) : <span class="hljs-keyword">byte</span>[] Encrypt(ArraySegment<<span class="hljs-keyword">byte</span>> plaintext, ArraySegment<<span class="hljs-keyword">byte</span>> additionalAuthenticatedData) : <span class="hljs-keyword">byte</span>[] |
其中接口中的参数additionalAuthenticatedData表示在构建加密的时候提供的一些附属信息。 IAuthenticatedEncryptorDescriptor接口提供了一个创建包含类型信息IAuthenticatedEncryptor实例方法。
1 2 |
CreateEncryptorInstance() : IAuthenticatedEncryptor ExportToXml() : XmlSerializedDescriptorInfo |
密钥管理扩展 在密钥系统管理中,提供了一个基础的接口IKey,它包含以下属性:
1 2 3 4 5 |
Activation creation expiration dates Revocation status Key identifier (<span class="hljs-name">a</span> GUID) |
IKey还提供了一个创建IAuthenticatedEncryptor实例的方法CreateEncryptorInstance。 IKeyManager接口提供了一系列用来操作Key的方法,包括存储,检索操作等。他提供的高级操作有: 创建一个Key 并且持久存储 从存储库中获取所有的 Key 撤销保存到存储中的一个或多个键 XmlKeyManager 通常情况下,开发人员不需要去实现IKeyManager来自定义一个 KeyManager。我们可以使用系统默认提供的XmlKeyManager类。 XMLKeyManager是一个具体实现IKeyManager的类,它提供了一些非常有用的方法。
1 2 3 4 5 6 7 8 9 10 |
<span class="hljs-keyword">public</span> sealed <span class="hljs-keyword">class</span> XmlKeyManager : IKeyManager, IInternalXmlKeyManager { <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">XmlKeyManager</span><span class="hljs-params">(IXmlRepository repository, IAuthenticatedEncryptorConfiguration configuration, IServiceProvider services)</span></span>; <span class="hljs-function"><span class="hljs-keyword">public</span> IKey <span class="hljs-title">CreateNewKey</span><span class="hljs-params">(DateTimeOffset activationDate, DateTimeOffset expirationDate)</span></span>; <span class="hljs-keyword">public</span> IReadOnlyCollection<IKey> GetAllKeys(); <span class="hljs-function"><span class="hljs-keyword">public</span> CancellationToken <span class="hljs-title">GetCacheExpirationToken</span><span class="hljs-params">()</span></span>; <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">RevokeAllKeys</span><span class="hljs-params">(DateTimeOffset revocationDate, <span class="hljs-built_in">string</span> reason = null)</span></span>; <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">RevokeKey</span><span class="hljs-params">(Guid keyId, <span class="hljs-built_in">string</span> reason = null)</span></span>; } |
IAuthenticatedEncryptorConfiguration 主要是规定新 Key 使用的算法。 IXmlRepository 主要控制 Key 在哪里持久化存储。 IXmlRepository IXmlRepository接口主要提供了持久化以及检索XML的方法,它只要提供了两个API: GetAllElements() : IReadOnlyCollection StoreElement(XElement element, string friendlyName) 我们可以通过实现IXmlRepository接口的StoreElement方法来定义data protection xml的存储位置。 GetAllElements来检索所有存在的加密的xml文件。 接口部分写到这里吧,因为这一篇我想把重点放到下面,更多接口的介绍大家还是去官方文档看吧~ 集群场景 上面的API估计看着有点枯燥,那我们就来看看我们需要在集群场景下借助于Data Protection来做点什么吧。 就像我在【上篇】总结中末尾提到的,在做分布式集群的时候,Data Protection的一些机制我们需要知道,因为如果不了解这些可能会给你的部署带来一些麻烦,下面我们就来看看吧。 在做集群的时,我们必须知道并且明白关于 ASP.NET Core Data Protection 的三个东西: 1、程序识别者 “Application discriminator”,它是用来标识应用程序的唯一性。 为什么需要这个东西呢?因为在集群环境中,如果不被具体的硬件机器环境所限制,就要排除运行机器的一些差异,就需要抽象出来一些特定的标识,来标识应用程序本身并且使用该标识来区分不同的应用程序。这个时候,我们可以指定ApplicationDiscriminator。 在services.AddDataProtection(DataProtectionOptions option)的时候,ApplicationDiscriminator可以作为参数传递,来看一下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureServices</span>(<span class="hljs-params">IServiceCollection services</span>) </span>{ services.AddDataProtection(); services.AddDataProtection(DataProtectionOptions option); } <span class="hljs-comment">//===========扩展方法如下:</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">DataProtectionServiceCollectionExtensions</span> { <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> IDataProtectionBuilder <span class="hljs-title">AddDataProtection</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IServiceCollection services</span>)</span>; <span class="hljs-comment">//具有可传递参数的重载,在集群环境中需要使用此项配置</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> IDataProtectionBuilder <span class="hljs-title">AddDataProtection</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IServiceCollection services, Action<DataProtectionOptions> setupAction</span>)</span>; } <span class="hljs-comment">// DataProtectionOptions 属性:</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">DataProtectionOptions</span> { <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> ApplicationDiscriminator { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; } } |
可以看到这个扩展返回的是一个IDataProtectionBuilder,在IDataProtectionBuilder还有一个扩展方法叫 SetApplicationName ,这个扩展方法在内部还是修改的ApplicationDiscriminator的值。也就说以下写法是等价的:
1 2 3 4 |
services.AddDataProtection(x => x.ApplicationDiscriminator = <span class="hljs-string">"my_app_sample_identity"</span>); services.AddDataProtection().SetApplicationName(<span class="hljs-string">"my_app_sample_identity"</span>); |
也就是说集群环境下同一应用程序他们需要设定为相同的值(ApplicationName or ApplicationDiscriminator)。 2、主加密键 […]
View Details前言 上篇主要是对 ASP.NET Core 的 Data Protection 做了一个简单的介绍,本篇主要是介绍一下API及使用方法。 API 接口 ASP.NET Core Data Protectio 主要对普通开发人员提供了两个接口,IDataProtectionProvider 和 IDataProtector。 我们先看一下这两个接口的关系:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<span class="hljs-keyword">namespace</span> <span class="hljs-title">Microsoft.AspNetCore.DataProtection</span> { <span class="hljs-comment">//</span> <span class="hljs-comment">// 摘要:</span> <span class="hljs-comment">// An interface that can provide data protection services.</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IDataProtector</span> : <span class="hljs-title">IDataProtectionProvider</span> { <span class="hljs-keyword">byte</span>[] Protect(<span class="hljs-keyword">byte</span>[] plaintext); <span class="hljs-keyword">byte</span>[] Unprotect(<span class="hljs-keyword">byte</span>[] protectedData); } } |
可以看到,IDataProtector继承自IDataProtectionProvider ,并且提供了两个方法 Protect 和 Unprotect ,从命名来看,一个是加密,一个是解密。而他们的签名都是传入一个byte数组,这也就意味着他们可以加密和解密一切对象。返回的也是byte数组,也就是说在实际的使用过程中,我们应该自己添加或者使用系统的一些扩展方法来具体化我们的需求。 我们再看一下IDataProtectionProvider接口:
1 2 3 4 5 6 7 8 |
<span class="hljs-keyword">namespace</span> <span class="hljs-title">Microsoft.AspNetCore.DataProtection</span> { <span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IDataProtectionProvider</span> { <span class="hljs-function">IDataProtector <span class="hljs-title">CreateProtector</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> purpose</span>)</span>; } } |
IDataProtectionProvider提供了一个方法,通过传入一个 purpose字符串(见后面详细介绍)来生成一个IDataProtector接口对象。 从这个接口的命名来看,它以Provider结尾,也就是说这部分我们可以实现自己的一套加解密的东西。 我们在阅读微软项目的源代码的时候,经常看一些以xxxxProvider结尾的对象,那么它的职责是什么,同时扮演什么样的角色呢? 其实这是微软专门为ASP.NET设计的一个设计模式,叫Provider Model设计模式,也可以说它是由微软发明的,它不属于23种设计模式中的一种,从功能上来看的话,应该是工厂和策略的结合体。自ASP.NET 2.0开始,微软就开始引入这种设计模式,最开始主要是用于实现应用程序的配置的多个实现。比如开发者最熟悉的web.config中, 针对于数据库连接字符串的配置, 还有二进制,再比如XML啊等等很多,现在其他地方这种模式也用的越来越多起来。 再来说一下CreateProtector方法签名中的 purpose 这个字符串,在上一篇博文中为了读者好理解,我把传入的purpose说成可以理解为一个公钥,其实这个说法是不严谨的,可以理解为一个标识,指示当前Protector的用途。 在使用IDataProtector的时候,会发现它还有一些扩展方法位于Microsoft.AspNetCore.DataProtection命名空间下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">DataProtectionCommonExtensions</span> { <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> IDataProtector <span class="hljs-title">CreateProtector</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IDataProtectionProvider provider, IEnumerable<<span class="hljs-keyword">string</span>> purposes</span>)</span>; <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> IDataProtector <span class="hljs-title">CreateProtector</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IDataProtectionProvider provider, <span class="hljs-keyword">string</span> purpose, <span class="hljs-keyword">params</span> <span class="hljs-keyword">string</span>[] subPurposes</span>)</span>; <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> IDataProtector <span class="hljs-title">GetDataProtector</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IServiceProvider services, IEnumerable<<span class="hljs-keyword">string</span>> purposes</span>)</span>; <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> IDataProtector <span class="hljs-title">GetDataProtector</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IServiceProvider services, <span class="hljs-keyword">string</span> purpose, <span class="hljs-keyword">params</span> <span class="hljs-keyword">string</span>[] subPurposes</span>)</span>; <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> <span class="hljs-title">Protect</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IDataProtector protector, <span class="hljs-keyword">string</span> plaintext</span>)</span>; <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> <span class="hljs-title">Unprotect</span>(<span class="hljs-params"><span class="hljs-keyword">this</span> IDataProtector protector, <span class="hljs-keyword">string</span> protectedData</span>)</span>; } |
可以看到,CreateProtector还提供了可以传多个purpose的方法(IEnumerable,params string[]),为什么会有这种需求呢? 其实DataProtector是有层次结构的,再看一下IDataProtector接口,它自身也实现了IDataProtectionProvider接口,就是说IDataProtector自身也可以再创建IDataProtector。 举个例子:我们在做一个消息通讯的系统,在消息通讯的过程中,需要对用户的会话进行加密,我们使用CreateProtector("Security.BearerToken")加密。但是加密的时候并不能保证消息是不受信任的客户端发过来的,所以想到了CreateProtector("username")来进行加密,这个时候假如有一个用户的用户名叫“Security.BearerToken”,那么就和另外一个使用Security.BearerToken作为标示的 Protector 冲突了,所以我们可以使用 CreateProtector([ “Security.BearerToken”, “User: username” ])这种方式。它相当于 provider.CreateProtector(“Security.BearerToken).CreateProtector(“User: username”)。 意思就是先创建一个Protector叫“Security.BearerToken”,然后再在purpose1下创建一个名为“User: username”的Protector。 用户密码哈希 在Microsoft.AspNetCore.Cryptography.KeyDerivation命名空间下提供了一个KeyDerivation.Pbkdf2方法用来对用户密码进行哈希。 具有生命周期限制的加密 有些时候,我们需要一些具有过期或者到期时间的加密字符串,比如一个用户在找回密码的时候,我们向用户的邮箱发送一封带有重置命令的一封邮件,这个重置命令就需要有一个过期时间了,超过这个过期时间后就失效,在以前我们可能需要向数据库存储一个时间来标记发送时间,然后再解密对比和数据库的时间差来验证。 现在我们不需要这么做了,ASP.NET Core 默认提供了一个接口叫 ITimeLimitedDataProtector ,我们先看一下这个接口的定义:
1 2 3 4 5 6 7 |
CreateProtector(<span class="hljs-built_in">string</span> purpose) : ITimeLimitedDataProtector This API <span class="hljs-keyword">is</span> similar <span class="hljs-keyword">to</span> the existing IDataProtectionProvider.CreateProtector <span class="hljs-keyword">in</span> that it can be used <span class="hljs-keyword">to</span> create purpose chains <span class="hljs-keyword">from</span> a root time-limited protector. Protect(<span class="hljs-built_in">byte</span>[] plaintext, DateTimeOffset expiration) : <span class="hljs-built_in">byte</span>[] Protect(<span class="hljs-built_in">byte</span>[] plaintext, TimeSpan lifetime) : <span class="hljs-built_in">byte</span>[] Protect(<span class="hljs-built_in">byte</span>[] plaintext) : <span class="hljs-built_in">byte</span>[] Protect(<span class="hljs-built_in">string</span> plaintext, DateTimeOffset expiration) : <span class="hljs-built_in">string</span> Protect(<span class="hljs-built_in">string</span> plaintext, TimeSpan lifetime) : <span class="hljs-built_in">string</span> Protect(<span class="hljs-built_in">string</span> plaintext) : <span class="hljs-built_in">string</span> |
ITimeLimitedDataProtector提供了数个重载方法用来设定带有生命周期的加密方法,用户可以通过Date TimeOffset,TimeSpan等参数来设置时间。 有对应的加密,就有相对应的解密方法,在这里就不详细介绍了。有兴趣的同学可以去看一下官方文档。 配置数据保护 在我们的 ASP.NET Core 运行的时候,系统会基于当前机器的运行环境默认配置一些关于 Data Protection 的东西,但是有些时候可能需要对这些配置做一些改变,比如在分布式部署的时候,在上一篇博文的末尾也提到过,下面就来看一下具体怎么配置的吧。 上篇文章已经提到过,我们通过以下方式来把 Data Protection 注册到服务中:
1 2 3 4 |
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureServices</span><span class="hljs-params">(IServiceCollection services)</span> </span>{ services.AddDataProtection(); } |
其中AddDataProtection 返回的是一个 IDataProtectionBuilder 接口,这个接口提供了一个扩展方法PersistKeysToFileSystem() 来存储私钥。可以通过它传入一个路径来指定私钥存储的位置:
1 2 3 4 5 6 |
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureServices</span>(<span class="hljs-params">IServiceCollection services</span>) </span>{ services.AddDataProtection() .PersistKeysToFileSystem(<span class="hljs-keyword">new</span> DirectoryInfo(<span class="hljs-string">@"\\server\share\directory\"</span>)); } |
可以传入一个共享文件夹,来存储私钥,这样在不同机器的私钥就可以保存到一个位置了。可以通过此种方式在分布式部署的时候,隔离开了机器的差异化。 如果你觉得不安全,还可以配置一个X.509证书来,进行加密:
1 2 3 4 5 6 |
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureServices</span>(<span class="hljs-params">IServiceCollection services</span>) </span>{ services.AddDataProtection() .PersistKeysToFileSystem(<span class="hljs-keyword">new</span> DirectoryInfo(<span class="hljs-string">@"\\server\share\directory\"</span>)) .ProtectKeysWithCertificate(<span class="hljs-string">"thumbprint"</span>); } |
上篇文章讲过,Data Protection 的默认保存时间是90天,你可以通过以下方式来修改默认的保存时间:
1 2 3 4 5 |
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureServices</span><span class="hljs-params">(IServiceCollection services)</span> </span>{ services.AddDataProtection() .SetDefaultKeyLifetime(TimeSpan.FromDays(<span class="hljs-number">14</span>)); } |
默认情况下,即使使用相同的物理密钥库,Data Protection 也会把不同的应用程序隔离开,因为这样可以防止从一个应用程序获取另外一个应用程序的密钥。所以如果是相同的应用程序,可以设置相同的应用程序名称:
1 2 3 4 5 |
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureServices</span><span class="hljs-params">(IServiceCollection services)</span> </span>{ services.AddDataProtection() .SetApplicationName(<span class="hljs-string">"my application"</span>); } |
有时候需要禁用应用程序生成密钥,或者是说我只有一个程序用来生成或者管理密钥,其他程序只是负责读的话,那么可以这样:
1 2 3 4 5 |
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureServices</span><span class="hljs-params">(IServiceCollection services)</span> </span>{ services.AddDataProtection() .DisableAutomaticKeyGeneration(); } |
修改加密算法 可以使用UseCryptographicAlgorithms方法来修改ASP.NET Core […]
View Details前言 上一篇博客记录了如何在 Kestrel 中使用 HTTPS(SSL), 也是我们目前项目中实际使用到的。 数据安全往往是开发人员很容易忽略的一个部分,包括我自己。近两年业内也出现了很多因为安全问题导致了很多严重事情发生,所以安全对我们开发人员很重要,我们要对我们的代码的安全负责。 在工作中,我们常常会见到 encode,base64,sha256, rsa, hash,encryption, md5 等,一些人对他们还傻傻分不清楚,也不知道什么时候使用他们,还有一些人认为MD5就是加密算法。 在 ASP.NET Core 中,为数据保护相关提供了一批新的 API,包括加密解密机制,下面就让我们来看看吧。 目录 加密,编码,哈希之间的区别 数据保护(Data Protection)介绍 ASP.NET Core 中的数据保护 总结 编码,加密,哈希之间的区别 编码 编码是信息从一种形式或格式转换为另一种形式的过程,他们是可逆的。 如 url、base64、jsunicode、utf-8等等。 加密 加密是可逆的,类似于编码也是把数据从一种形式转换为另一种形式,它通过一个特定的加密的密匙,相对应的有解密的过程。加解密的算法有2种:对称加密算法和非对称加密算法。 对称:DES、AES、SM1、RC4 等等。 非对称:RSA、ECC、SM2 等等。 哈希 又叫"散列",就是把任意长度的数据转换成固定长度的“指纹”,这个过程是不可逆的。而且只要输入发生改变,输出的 hash值也会有很大不同。 它还有一个特性是相同的输入总是有相同的结果, 这种特性恰好合适用来用来保存密码。 如:MD5、SHA256, SHA512, RipeMD, WHIRLPOOL等等。 数据保护(Data Protection)介绍 在看数据保护官方文档的时候,微软的文档是这样写的,大致意思就是他们基于几点需求,要开发一套数据保护的库以便用来给受信任的客户端和不受信任的客户端来使用。这几点要求就是: 1、真实性、完整性 举了一个身份验证cookie的例子,就是服务端生成了一个包含xyz权限的token,然后会在将来的某个时间过期,这个时候就需要重新请求生成一个,怎么样来保证请求的token不是被篡改过的。 2、机密性 服务器要保证请求是受信任的,所以就需要一些包含特定操作环境的信息,比如一个路径,一个权限或者一个句柄或者其他的一些东西特定于服务器的东西,这些信息不应该透漏给不受信任的客户端,也就是说类似于私钥。 3、隔离性 然后就是要求做成一个组件,并且这个组件具有独立性,可以不依赖于系统中的其他组件。如一个bearer token的组件,它要使用这个组件的话,也不需要引用anti-CSRF这种机制了。 再进一步的缩小需求范围,加密的数据不需要在系统之外的其他系统中使用,另外处理速度要尽可能的快,因为每一次web请求都会使用加密组件一次或者多次。 基于以上要求,微软提出来可以使用密码学,因为这是一个典型的密码学应用的场景。确实这是一个密码学的应用场景,并且是一个非对称加密算法的场景。但是大家都知道,非对称加密是由一个公钥和私钥用来保证安全性的,即使公钥遭泄露,整个通讯仍然是安全的,这就是它比对称加密的好处。但是非对称加密也是有缺点的,就是加密和解密花费的时间长,速度慢。 但是上面的要求又是需要速度尽可能快,怎么办呢? 于是微软的工程师们想出了可以通过精简并且优化非对称加密机制,来达到这个要求。因为不需要跨系统或者跨语言什么的,所以也不需要什么协议之类的,这就给优化带来了更多的可能性。 到这里,我就想,如果让我来基于以上几点来设计开发这样一个系统,我应该怎么样设计?怎么样达到要求? 带着这个问题,我们来进一步看看微软是怎么样做的吧? 下面是一些总结的设计原则 : 1、配置应该尽量的简单,默认情况下应该可以零配置,开发人员可以直接运行。 2、提供一个简单的API,应该容易使用,并且不会轻易用错。 3、开发人员不需要专门学习怎么样管理这些钥(公钥,私钥),系统应该自动的选择算法和管理钥的生命周期。理想情况下开发人员都不应该访问这些钥的原始文件。 4、钥应该是受保护的,不会被远程调用到。系统应该有一个自动保护机制并且可以自动应用。 如果让我设计这样一个库,我可能不会想到这么多,也许只会想到前3点。 再看一下针对的受众群体: 1、应用程序开发人员和框架开发人员(不需要学习任何知识)。 2、应用开发人员和系统管理员(不使用默认配置,只是设定一些路径等)。 3、针对具有更高安全意识的开发人员提供可扩展api,或特定需求扩展(需要重写系统的组件,有一些独特的需求)。 以上,可以看到微软在开发一个组件的时候对问题的分析,也许我们可以从中学到一些东西。 ASP.NET Core 中的数据保护 Web应用程序中经常需要存储一些敏感数据(如用户密码),Windows 系统为桌面程序提供了DPAPI用来使用,但是并不适用于 Web 系统。ASP.NET Core提供了一套简单易用的API 用来保护数据。 ASP.NET Core 中,数据保护主要是用来给服务端设计的,用来替换ASP.NET 1.x-4.x中的,machineKey主要是用来保证使用Form身份验证时Cookie数据的加密解密,以确保不会被修改。或者ViewState数据的加密解密不被篡改,以及对session状态标识进行验证。 先看一下最简单的使用方法: […]
View Details