電子勞動合同來了 足不出戶也能簽約】日前,北京市人力資源和社會保障局公開發布《關于推進電子勞動合同相關工作的實施意見》,這標志著延續多年的紙質勞動合同將逐步進入電子化時代。專家認為,這項重要的制度創新,將在全國起到示范引領作用。據了解,電子勞動合同與紙質勞動合同具有同等法律效力。推行電子勞動合同有利于用人單位降本增效,提高人力資源管理效率。電子勞動合同來了 足不出戶也能簽約 (經濟日報)
聲明:轉載此文是出于傳遞更多信息之目的。若有來源標注錯誤或侵犯了您的合法權益,請作者持權屬證明與本網聯系,我們將及時更正、刪除,謝謝。
來源: 中國搜索
者 | 阿文
責編 | 屠敏
出品 | CSDN(ID:CSDNnews)
最近幾年,隨著RESTful API開始風靡,使用HTTP header來傳遞認證令牌似乎變得理所應當,通過 RESTful 的API 接口設計簡化了系統架構, 減少了耦合性, 可以讓所有模塊各自獨立的進行改進。
不過,在實際的REST API 接口設計過程中,我們需要考慮如何讓鑒權變得更安全可靠,例如不會被第三方惡意請求或者保證傳輸過程中的數據安全以及防止重復提交,本文就一起聊一聊。
傳統的Session 認證方式
首先,我們說一些傳統的認證方式,眾所周知,HTTP 協議是一種無狀態的協議,而這就意味著如果用戶向我們的應用提供了用戶名和密碼來進行用戶認證,那么下一次請求時,用戶還要再一次進行用戶認證才行,因為我們并不能知道是哪個用戶發出的請求,所以為了讓我們的應用能識別是哪個用戶發出的請求,我們只能在服務器存儲一份用戶登錄的信息,這份登錄信息會在響應時傳遞給瀏覽器,告訴其保存為cookie,以便下次請求時發送給我們的應用,這樣我們的應用就能識別請求來自哪個用戶了,這就是傳統的基于session認證。
而這種方式有很多問題:
首先,占用資源,這種方式需要每個用戶經過認證之后,都要在服務端做一次記錄,以方便用戶下次請求的鑒別,通常而言session都是保存在內存中,而隨著認證用戶的增多,服務端的開銷會明顯增大。
其次,擴展性差: 客戶端認證之后,服務端做認證記錄,如果認證的記錄被保存在內存中的話,這意味著用戶下次請求還必須要請求在這臺服務器上,這樣才能拿到授權的資源,在一些分布式的場景下會限制了負載均衡器的能力,會限制了應用的擴展能力。
第三,容易遭受攻擊: 這種基于cookie來進行用戶識別的認證方式, 很容易被截獲,用戶就會很容易受到跨站請求偽造的攻擊。
基于Token 的鑒權方式
由于session 認證的諸多問題,因此出現了基于token 的鑒權方式,這種方式不需要在服務端去保留用戶的認證信息或者會話信息。這就意味著基于token認證機制的應用不需要去考慮用戶在哪一臺服務器登錄了,這就為應用的擴展提供了便利。
基于token 鑒權的工作流程如下:
首先,客戶端通過用戶名密碼來請求對應的API接口
第二,服務器會驗證用戶的信息
第三,服務器通過驗證后會發送token給客戶端
第四,客戶端存儲token,并在每次請求時附送上這個token值
第五,服務端驗證token值,并返回數據
這種方式的典型代表就是JWT(Json web token , 它是為了在網絡應用環境間傳遞聲明而執行的一種基于JSON的開放標準((RFC 7519),該token被設計為緊湊且安全的,特別適用于分布式站點的單點登錄(SSO)場景。JWT的聲明一般被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,以便于從資源服務器獲取資源,也可以增加一些額外的其它業務邏輯所必須的聲明信息,該token也可直接被用于認證,也可被加密。
它的特點如下:
體積小(一串字符串)。因而傳輸速度快
傳輸方式多樣。可以通過 HTTP 頭部(推薦)/URL/POST 參數等方式傳輸
嚴謹的結構化。它自身(在 payload 中)就包含了所有與用戶相關的驗證消息,如用戶可訪問路由、訪問有效期等信息,服務器無需再去連接數據庫驗證信息的有效性,并且 payload 支持應用定制
支持跨域驗證,多應用于單點登錄
JWT通常由三部分組成:
頭信息(header)
消息體(payload)
簽名(signature)
如下所示:
// Header { "alg": "HS256", "typ": "JWT" }
// Payload { // reserved claims "iss": "a.com", "exp": "1d", // public claims "http://a.com": true, // private claims "company": "A", "awesome": true }
// $Signature HS256(Base64(Header) + "." + Base64(Payload), secretKey)
// JWT JWT=Base64(Header) + "." + Base64(Payload) + "." + $Signature
其工作流程如下:
首先,客戶端通過發送HTTP 請求把賬號密碼發送給服務短,通常使用的是POST請求, 服務器會校驗賬號與密碼是否合法,如果一致,則根據密鑰生成一個 token 并返回,客戶端收到這個 token 并保存在本地。在這之后,需要訪問一個受保護的路由或資源時,只要附加上 token(通常使用 Header 的 Authorization 屬性)發送到服務器,服務器就會檢查這個 token 是否有效,并做出響應。
服務端接收到 token 之后,會逆向構造過程,解碼出 JWT 的三個部分,這一步可以得到 sign 的算法及 payload,結合服務端配置的 secretKey,可以再次進行 $Signature 的生成得到新的 $Signature,與原有的 $Signature 比對以驗證 token 是否有效,完成用戶身份的認證,驗證通過才會使用 payload 的數據。
如何保證接口安全性
要想實現接口的安全性,我們可以做到以下幾點:
首先,我們需要采用HTTPS 對傳輸過程中的數據進行加密,避免使用HTTP 這種明文傳輸的協議,防止數據直接暴露在公網中,在使用HTTPS的同時要保證時間安全可靠的加密方法和SSL 協議,目前主流的是TLS1.2 和最新的TLS1.3。同時要對證書進行校驗,因為即使是HTTPS協議,證書也是能夠被偽造的。
其次,對接口設計一般會加入 token、timestamp和sign 這些參數,不同的參數有自己不同的用途:
timestamp,即時間戳,它是客戶端調用接口時傳入的當前時間戳,時間戳的目的是用于防止DoS攻擊。每次調用接口時接口都會判斷服務器當前系統時間和接口中傳的的timestamp的差值,如果這個差值超過某個設置的時間,例如設置的時間是3分鐘,那么這個請求將被攔截掉,如果在設置的超時時間范圍內,是不能阻止DoS攻擊的。timestamp機制只能減輕DoS攻擊的時間,縮短攻擊時間。如果黑客修改了時間戳的值可通過sign簽名機制來處理。
sign,即簽名,通常用于參數簽名,防止參數被非法篡改,最常見的是修改金額等重要敏感參數, sign的值一般是將所有非空參數按照升續排序然后+token+key+timestamp+nonce(隨機數)拼接在一起,然后使用某種加密算法進行加密,這種方式的好處就是,當被劫持后,修改其中的參數值,然后再繼續調用接口,雖然參數的值被修改了,但是因為攻擊者并不清楚sign是如何計算出來的,所以即可是篡改參數的值,但沒法修改sign的值,當服務器調用接口前會按照sign的規則重新計算出sign的值然后和接口傳遞的sign參數的值做比較,如果相等表示參數值沒有被篡改,如果不等,表示參數被非法篡改了,則不會返回真實的響應信息。
此外,接口設計時候要實現冪等性操作,所謂的冪等性操作就是為了防止重復性運算,我們可以將生成的簽名和key保存到redis 中,并且設置超時時間,過期自動刪除,當有重復的值存在則不會處理,就可以防止重復提交,從而保證請求結果一致性。
其使用流程如下:
接口調用方(客戶端)向接口提供方(服務器)申請接口調用賬號,申請成功后,接口提供方會給接口調用方一個AppKey和一個APP Secret參數
調用方申請App Key 和 App Secret 在生成請求時,將參數拼接后進行加密,例如使用HMAC-SHA256 或MD5加密,然后將 App Key, 加密結果追加到請求上。sign=加密(appId + timestamp + key)
服務收到請求后,根據App Key識別出調用方,解密得到參數以及對時間進行對比,判斷是否超時,然后從字典中查詢到對應的App Secret,與請求參數拼接、加密,與請求中的簽名進行對比,簽名結果相同的為合法請求。
以上就是給API 接口設計的一些建議,僅供參考,在實際的應用中還可以追加一些公共的參數,例如Host、接口的版本等等參數去進行校驗保證接口的安全性。
雯 發自 凹非寺
量子位 | 公眾號 QbitAI
取整求個無符號整數的平均值,居然也能整出花兒來?
這不,微軟大神Raymond Chen最近的一篇長文直接引爆外網技術平臺,引發無數討論:
無數人點進去時無比自信:不就是一個簡單的相加后除二的小學生編程題嗎?
unsigned average(unsigned a, unsigned b)
{
return (a + b) / 2;
}
但跟著大神的一路深挖,卻逐漸目瞪狗呆……
先從開頭提到的小學生都會的方法看起,這個簡單的方法有個致命的缺陷:
如果無符號整數的長度為32位,那么如果兩個相加的值都為最大長度的一半,那么僅在第一步相加時,就會發生內存溢出。
也就是average(0x80000000U, 0x80000000U)=0。
不過解決方法也不少,大多數有經驗的開發者首先能想到的,就是預先限制相加的數字長度,避免溢出。
具體有兩種方法:
1、當知道相加的兩個無符號整數中的較大值時,減去較小值再除二,以提前減少長度:
unsigned average(unsigned low, unsigned high)
{
return low + (high - low) / 2;
}
2、對兩個無符號整數預先進行除法,同時通過按位與修正低位數字,保證在兩個整數都為奇數時,結果仍然正確。
(順帶一提,這是一個被申請了專利的方法,2016年過期)
unsigned average(unsigned a, unsigned b)
{
return (a / 2) + (b / 2) + (a & b & 1);
}
這兩個都是較為常見的思路,不少網友也表示,自己最快想到的就是2016年專利方法。
同樣能被廣大網友快速想到的方法還有SWAR(SIMD within a register):
unsigned average(unsigned a, unsigned b)
{
return (a & b) + (a ^ b) / 2;// 變體 (a ^ b) + (a & b) * 2
以及C++ 20版本中的std: : midpoint函數。
接下來,作者提出了第二種思路:
如果無符號整數是32位而本機寄存器大小是64位,或者編譯器支持多字運算,就可以將相加值強制轉化為長整型數據。
unsigned average(unsigned a, unsigned b)
{
// Suppose "unsigned" is a 32-bit type and
// "unsigned long long" is a 64-bit type.
return ((unsigned long long)a + b) / 2;
}
不過,這里有一個需要特別注意的點:
必須要保證64位寄存器的前32位都為0,才不會影響剩余的32位值。
像是x86-64和aarch64這些架構會自動將32位值零擴展為64位值:
// x86-64: Assume ecx=a, edx=b, upper 32 bits unknown
mov eax, ecx ; rax=ecx zero-extended to 64-bit value
mov edx, edx ; rdx=edx zero-extended to 64-bit value
add rax, rdx ; 64-bit addition: rax=rax + rdx
shr rax, 1 ; 64-bit shift: rax=rax >> 1
; result is zero-extended
; Answer in eax
// AArch64 (ARM 64-bit): Assume w0=a, w1=b, upper 32 bits unknown
uxtw x0, w0 ; x0=w0 zero-extended to 64-bit value
uxtw x1, w1 ; x1=w1 zero-extended to 64-bit value
add x0, x1 ; 64-bit addition: x0=x0 + x1
ubfx x0, x0, 1, 32 ; Extract bits 1 through 32 from result
; (shift + zero-extend in one instruction)
; Answer in x0
而Alpha AXP、mips64等架構則會將32位值符號擴展為64位值。
這種時候,就需要額外增加歸零的指令,比如通過向左進位兩字的刪除指令rldicl:
// Alpha AXP: Assume a0=a, a1=b, both in canonical form
insll a0, #0, a0 ; a0=a0 zero-extended to 64-bit value
insll a1, #0, a1 ; a1=a1 zero-extended to 64-bit value
addq a0, a1, v0 ; 64-bit addition: v0=a0 + a1
srl v0, #1, v0 ; 64-bit shift: v0=v0 >> 1
addl zero, v0, v0 ; Force canonical form
; Answer in v0
// MIPS64: Assume a0=a, a1=b, sign-extended
dext a0, a0, 0, 32 ; Zero-extend a0 to 64-bit value
dext a1, a1, 0, 32 ; Zero-extend a1 to 64-bit value
daddu v0, a0, a1 ; 64-bit addition: v0=a0 + a1
dsrl v0, v0, #1 ; 64-bit shift: v0=v0 >> 1
sll v0, #0, v0 ; Sign-extend result
; Answer in v0
// Power64: Assume r3=a, r4=b, zero-extended
add r3, r3, r4 ; 64-bit addition: r3=r3 + r4
rldicl r3, r3, 63, 32 ; Extract bits 63 through 32 from result
; (shift + zero-extend in one instruction)
; result in r3
或者直接訪問比本機寄存器更大的SIMD寄存器,當然,從通用寄存器跨越到SIMD寄存器肯定也會增加內存消耗。
如果電腦的處理器支持進位加法,那么還可以采用第三種思路。
這時,如果寄存器大小為n位,那么兩個n位的無符號整數的和就可以理解為n+1位,通過RCR(帶進位循環右移)指令,就可以得到正確的平均值,且不損失溢出的位。
帶進位循環右移
// x86-32
mov eax, a
add eax, b ; Add, overflow goes into carry bit
rcr eax, 1 ; Rotate right one place through carry
// x86-64
mov rax, a
add rax, b ; Add, overflow goes into carry bit
rcr rax, 1 ; Rotate right one place through carry
// 32-bit ARM (A32)
mov r0, a
adds r0, b ; Add, overflow goes into carry bit
rrx r0 ; Rotate right one place through carry
// SH-3
clrt ; Clear T flag
mov a, r0
addc b, r0 ; r0=r0 + b + T, overflow goes into T bit
rotcr r0 ; Rotate right one place through carry
那如果處理器不支持帶進位循環右移操作呢?
也可以使用內循環(rotation intrinsic):
unsigned average(unsigned a, unsigned b)
{
#if defined(_MSC_VER)
unsigned sum;
auto carry=_addcarry_u32(0, a, b, &sum);
sum=(sum & ~1) | carry;
return _rotr(sum, 1);
#elif defined(__clang__)
unsigned carry;
sum=(sum & ~1) | carry;
auto sum=__builtin_addc(a, b, 0, &carry);
return __builtin_rotateright32(sum, 1);
#else
#error Unsupported compiler.
#endif
}
結果是,x86架構下的代碼生成沒有發生什么變化,MSCver架構下的代碼生成變得更糟,而arm-thumb2的clang 的代碼生成更好了。
// _MSC_VER
mov ecx, a
add ecx, b ; Add, overflow goes into carry bit
setc al ; al=1 if carry set
and ecx, -2 ; Clear bottom bit
movzx ecx, al ; Zero-extend byte to 32-bit value
or eax, ecx ; Combine
ror ear, 1 ; Rotate right one position
; Result in eax
// __clang__
mov ecx, a
add ecx, b ; Add, overflow goes into carry bit
setc al ; al=1 if carry set
shld eax, ecx, 31 ; Shift left 64-bit value
// __clang__ with ARM-Thumb2
movs r2, #0 ; Prepare to receive carry
adds r0, r0, r1 ; Calculate sum with flags
adcs r2, r2 ; r2 holds carry
lsrs r0, r0, #1 ; Shift sum right one position
lsls r1, r2, #31 ; Move carry to bit 31
adds r0, r1, r0 ; Combine
Raymond Chen1992年加入微軟,迄今為止已任職25年,做UEX-Shell,也參與Windows開發,Windows系統的很多最初UI架構就是他搞起來的。
他在MSDN 上建立的blogThe Old New Thing也是業內非常出名的純技術向產出網站。
這篇博客的評論區們也是微軟的各路大神出沒,繼續深入探討。
有人提出了新方法,在MIPS ASM共有36個循環:
unsigned avg(unsigned a, unsigned b
{
return (a & b) + (a ^ b) / 2;
}
// lw $3,8($fp) # 5
// lw $2,12($fp) # 5
// and $3,$3,$2 # 4
// lw $4,8($fp) # 5
// lw $2,12($fp) # 5
// xor $2,$4,$2 # 4
// srl $2,$2,1 # 4
// addu $2,$3,$2 # 4
有人針對2016年專利法表示,與其用(a / 2) + (b / 2) + (a & b & 1)的方法,為啥不直接把 (a & 1) & ( b & 1 ) ) 作為進位放入加法器中計算呢?
還有人在評論區推薦了TopSpeed編譯器,能夠通過指定合適的代碼字節和調用約定來定義一個內聯函數,以解決“乘除結果是16位,中間計算值卻不是”的情況。
只能說,學無止境啊。
原文:
https://devblogs.microsoft.com/oldnewthing/20220207-00/?p=106223
參考鏈接:
https://news.ycombinator.com/item?id=30252263
*請認真填寫需求信息,我們會在24小時內與您取得聯系。