Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
ession 是保存用戶和 Web 應(yīng)用的會(huì)話狀態(tài)的一種方法,ASP.NET Core 提供了一個(gè)用于管理會(huì)話狀態(tài)的中間件。在本文中我將會(huì)簡(jiǎn)單介紹一下 ASP.NET Core 中的 Session 的使用方法。
nuget 添加引用 Microsoft.AspNetCore.Session
ession 是基于 IDistributedCache 構(gòu)建的,所以必須引用一種 IDistributedCache 的實(shí)現(xiàn),ASP.NET Core 提供了多種 IDistributedCache 的實(shí)現(xiàn) (Redis、SQL Server、In-memory)
services.AddDistributedMemoryCache();
services.AddSession();
nuget 添加引用 Microsoft.Extensions.Caching.SqlServer
SqlServerCache實(shí)現(xiàn)允許分布式緩存使用SQL Server數(shù)據(jù)庫(kù)作為其后備存儲(chǔ)。要?jiǎng)?chuàng)建SQL Server表,您可以使用sql-cache工具,該工具將使用您指定的名稱和模式創(chuàng)建一個(gè)表。
要使用sql-cache工具,請(qǐng)?zhí)砑?/span>SqlConfig.Tools到.csproj文件的<ItemGroup>元素并運(yùn)行dotnet恢復(fù)。
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.Extensions.Caching.SqlConfig.Tools" Version="1.0.0-msbuild3-final" />
</ItemGroup>
通過(guò)運(yùn)行以下命令來(lái)測(cè)試SqlConfig.Tools
C:\DistCacheSample\src\DistCacheSample>dotnet sql-cache create --help
sql-cache工具將顯示用法,選項(xiàng)和命令幫助,現(xiàn)在你可以創(chuàng)建表到sql server中,運(yùn)行“sql-cache create”命令:
C:\DistCacheSample\src\DistCacheSample>dotnet sql-cache create "Data Source=(localdb)\v11.0;Initial Catalog=DistCache;Integrated Security=True;" dbo TestCache
info: Microsoft.Extensions.Caching.SqlConfig.Tools.Program[0]
Table and index were created successfully.
創(chuàng)建的表格具有以下架構(gòu):
注意的ConnectionString(以及可選地,SchemaName和TableName)通常應(yīng)該被存儲(chǔ)的源控制(如UserSecrets)以外,因?yàn)樗鼈兛赡馨瑧{證。
像所有的緩存實(shí)現(xiàn)一樣,你的應(yīng)用程序應(yīng)該使用一個(gè)實(shí)例來(lái)獲取和設(shè)置緩存值IDistributedCache,而不是SqlServerCache。該示例SqlServerCache在Production環(huán)境中實(shí)現(xiàn)(因此已配置ConfigureProductionServices)。
// Microsoft SQL Server implementation of IDistributedCache.
// Note that this would require setting up the session state database.
services.AddDistributedSqlServerCache(o =>
{
o.ConnectionString = "Server=.;Database=ASPNET5SessionState;Trusted_Connection=True;";
o.SchemaName = "dbo";
o.TableName = "Sessions";
});
services.AddSession();
nuget 添加引用 Microsoft.Extensions.Caching.Redis
Redis是一款開(kāi)源的內(nèi)存數(shù)據(jù)存儲(chǔ),通常用作分布式緩存。您可以在本地使用它,并且可以為Azure托管的ASP.NET Core應(yīng)用程序配置Azure Redis緩存。您的ASP.NET Core應(yīng)用程序使用RedisDistributedCache實(shí)例配置緩存實(shí)施。
您可以ConfigureServices通過(guò)請(qǐng)求一個(gè)實(shí)例IDistributedCache(參見(jiàn)上面的代碼)來(lái)配置Redis實(shí)現(xiàn)并在您的應(yīng)用代碼中訪問(wèn)它。
在示例代碼中,RedisCache當(dāng)為服務(wù)器配置Staging環(huán)境時(shí)使用實(shí)現(xiàn)。因此該ConfigureStagingServices方法配置RedisCache:
services.AddDistributedRedisCache(options => { options.Configuration = "localhost"; options.InstanceName = "SampleInstance"; });
接著在 Startup.cs 的 Config 方法中配置使用 Session 中間件,所有中間件的配置順序非常重要,必須在 UseSession 調(diào)用后才能訪問(wèn) Session 。
// 必須在 UseMvc 之前調(diào)用
app.UseSession();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
在 AddSession 和 UseSession 方法中可以傳入一個(gè) SessionOptions 參數(shù),通過(guò)該參數(shù)可以設(shè)置 Session 的 Cookie name, Cookie path 等信息。
配置完成后,就可以使用 Session 保存數(shù)據(jù)了。
具體實(shí)現(xiàn)redis實(shí)現(xiàn) https://www.cnblogs.com/liuxiaoji/p/9259747.html
Session 安裝配置好就可以通過(guò) HttpContext.Session 來(lái)保存和讀取數(shù)據(jù)了。由于 Session 是基于 IDistributedCache 構(gòu)建的,因此 Session 只能存儲(chǔ) byte[] 數(shù)據(jù),這樣使用起來(lái)很不方便,好在有很多擴(kuò)展方法可以用來(lái)直接讀取和保存 string、int 等類型數(shù)據(jù)。
一個(gè) Session 使用的簡(jiǎn)單示例:
public IActionResult Index()
{
HttpContext.Session.SetString("SessionStartedTime", "Session started time:" + DateTime.Now.ToString());
return View();
}
public IActionResult About()
{
ViewData["CurrentTime"] = "Current time:" + DateTime.Now.ToString();
ViewData["SessionStartedTime"] = HttpContext.Session.GetString("SessionStartedTime");
return View();
}
或者設(shè)置一個(gè)擴(kuò)展類也可以直接將實(shí)體類序列化成json存儲(chǔ)
essionStorage只能保存在單一的瀏覽器窗口或分頁(yè)(tab), 關(guān)閉瀏覽器后存儲(chǔ)的數(shù)據(jù)就消失了, 其最大的用途在于保存一些臨時(shí)的數(shù)據(jù), 防止用戶重新整理
網(wǎng)頁(yè)時(shí)不小心丟失這些數(shù)據(jù)。sessionStorage的操作方法與localStorage相同;
1 存儲(chǔ)
window.sessionStorage.setItem("userdata","Hello HTML5");
window.sessionStorage["userdata"] = "Hello HTML5";
window.sessionStorage.userdata = "Hello HTML5";
2 讀取
var value1 = window.sessionStorage.getItem("userdata");
var value1 = window.sessionStorage["userdata"];
var value1 = window.sessionStorage.userdata;
3 清除
window.sessionStorage.removeItem("userdata");
delete window.sessionStorage.userdata;
delete window.sessionStorage["userdata"];
//全部清除
sessionStorage.clear();
session聲明周期包括刷新網(wǎng)頁(yè)的頁(yè)面
文鏈接: https://blog.by24.cn/archives/about-session.html
Web 開(kāi)發(fā)中,Session 是經(jīng)常用到的概念,但是在日常交流中,似乎又經(jīng)常引起誤解。在我看來(lái),引發(fā)誤解的原因主要有兩個(gè):
下面,我就嘗試著捋一下整個(gè)問(wèn)題,看看能不能盡量消除這些誤會(huì)。
注:其它通訊領(lǐng)域也有使用 Session 這個(gè)詞匯,本文僅探討 Web 開(kāi)發(fā)中的 Session 使用。
本文確實(shí)冗長(zhǎng)無(wú)比,此處列出簡(jiǎn)要觀點(diǎn)方便『太長(zhǎng)不看』(Too Long; Didn't Read.)的讀者。
由于大家使用的『簡(jiǎn)稱』含義往往差別很大,在此我們先約定一些稱呼。
這些稱呼可能和你慣用的不太一樣,但統(tǒng)一定義是高效討論的重要基礎(chǔ),還請(qǐng)不要抗拒。
Session 經(jīng)常被翻譯為 『會(huì)話』,其實(shí)還是很貼切的,它代表了『一次』相互溝通,這次會(huì)話中,可能包括了多次通信。
需要注意的是:此處 Session 是一個(gè)整體性的虛擬概念,不代指任何實(shí)體或數(shù)據(jù)。
看到 Data 后綴就知道,這個(gè)詞代表了一次 Session 中,暫存或使用 的一些數(shù)據(jù)。
具體是什么數(shù)據(jù)取決于業(yè)務(wù)實(shí)現(xiàn),例如可能存儲(chǔ)了一些用戶的 ID 信息或短效配置等。
需要注意的是,此處指的是數(shù)據(jù)的『實(shí)體』,而不是『索引』。
上面說(shuō)到的數(shù)據(jù)實(shí)體 Session-Data ,未必會(huì)直接放在程序內(nèi)部,而是可能存儲(chǔ)在某一個(gè)具體的位置(后文會(huì)詳細(xì)討論)。
在大部分 Server Side Session 實(shí)現(xiàn)中(即 Session-Data 存儲(chǔ)在服務(wù)端時(shí)),為了方便取用,就需要記錄一個(gè)『索引』,用來(lái)找到具體的 Session-Data ,我們將這個(gè)『索引』稱為 Session-ID 。
需要注意的是:在某些實(shí)現(xiàn)中,可能并沒(méi)有 Session-ID 的概念(例如 Client Side Session)。
正如我前面所強(qiáng)調(diào)的,在不同的實(shí)現(xiàn)中,Session-Data 可能存儲(chǔ)在不同的位置:可能在客戶端,也可能在服務(wù)端。
在大部分實(shí)現(xiàn)中,并不會(huì)直接傳輸 Session-Data ,而是傳輸它的索引:Session-ID ,服務(wù)端程序收到 Session-ID 后,從存儲(chǔ)著 Session-Data 的地方取出具體的 Session-Data 來(lái)使用。
在這種模式下,更容易被我們接觸到的,其實(shí)是存儲(chǔ)在 Cookies 里的 Session-ID ,而許多人存在的一個(gè)誤區(qū),便是將 Session-ID 直接等同于 Session 本身,無(wú)視了 Session-Data 的存在,這其實(shí)是不正確的。
上圖表示了這種模式的大致運(yùn)行方式,綠色的 Session 是一個(gè)老用戶會(huì)話, Alice 在訪問(wèn)時(shí)發(fā)送了自己持有的 Session-ID AA01 來(lái)標(biāo)記當(dāng)前會(huì)話,服務(wù)器程序根據(jù)相應(yīng)的 Session-id 查詢出了相應(yīng)的 Session Data ,并在程序中應(yīng)用。
對(duì)于新用戶,因?yàn)樽约簺](méi)有存儲(chǔ) Session-id,只好不發(fā)送,服務(wù)器為了標(biāo)記本次會(huì)話,生成了新的 Session-ID NN02 并返回給客戶端,這樣就開(kāi)啟了一個(gè)新的會(huì)話,圖中用紅色 Session 表示。
除了上面提到的常見(jiàn)模式,其實(shí)還存在一種實(shí)現(xiàn)被許多人忽視,服務(wù)端并不存儲(chǔ) Session-Data,而是直接將 Session-Data 存儲(chǔ)在客戶端(瀏覽器)的 Cookies 里。
在這種情況下,你甚至找不到 Session-ID 的存在,許多人也會(huì)因此而搞不清 Session 究竟存儲(chǔ)在哪里。
上圖表示了這種模式的大致運(yùn)行方式,與上面那種方式不同的地方在于,客戶端直接告訴服務(wù)端,我自己是 Alice ,我的基本信息是 xxx ,于是服務(wù)端使用這些數(shù)據(jù)返回了相應(yīng)的頁(yè)面。
對(duì)于新用戶,本地一丁點(diǎn)兒信息都沒(méi)有,于是服務(wù)端返回了一個(gè)空的 Session-Data 用來(lái)初始化,在后續(xù)的通信中,這個(gè) Session-Data 會(huì)被繼續(xù)填充上有意義的數(shù)據(jù)。
很顯然,這里存在著巨大的安全問(wèn)題:我直接修改存儲(chǔ)在本地的 Session-Data,聲稱我自己是管理員怎么辦?
為此,Client side session 需要加入加密機(jī)制或簽名機(jī)制來(lái)進(jìn)行校驗(yàn),以確保所有的 Session-Data 都是由服務(wù)端生成的,具體會(huì)在下文討論。
提到 Session 就不能不提 Cookies,畢竟很多人會(huì)條件反射的把這二者弄混(苦笑臉)。
Cookies 自身就有非常多的玩兒法,拋開(kāi)那些權(quán)限相關(guān)的內(nèi)容,此處我們先只它最基礎(chǔ)的幾個(gè)特性:
正是這些特征,讓 Cookies 成為存儲(chǔ) Session-Data / Session-ID 的絕佳場(chǎng)所,在幾乎所有的常見(jiàn)實(shí)現(xiàn)中,均默認(rèn)使用 Cookies 來(lái)存儲(chǔ) Session 相關(guān)信息。
但是需要注意,Session-Data / Session-ID 選擇 Cookie 作為容器只是因?yàn)椤悍奖恪唬@并不是必然情況:
我們完全可以實(shí)現(xiàn)一個(gè) Web 框架,它使用 LocalStorage 來(lái)存儲(chǔ) Session-Data / Session-ID ,在發(fā)起 XHR 請(qǐng)求時(shí),主動(dòng)將其加進(jìn) Header 內(nèi)(甚至 URL 內(nèi)),服務(wù)端則從相應(yīng)位置讀取信息完成業(yè)務(wù)邏輯,在這套框架體系內(nèi)這是完全沒(méi)有問(wèn)題的。
實(shí)際上,許多框架都支持 Session with out Cookies 的實(shí)現(xiàn),雖然存在各種各樣的小問(wèn)題或限制,但是確實(shí)可用。
因此,需要再次強(qiáng)調(diào):對(duì)于 Session 機(jī)制來(lái)說(shuō),Session-Data / Session-ID 才是本體,Cookies 只是一個(gè)存儲(chǔ)容器,這二者沒(méi)有捆綁關(guān)系。
對(duì)于 Session 機(jī)制,之所以總是產(chǎn)生種種誤解, 和各大 語(yǔ)言 / 框架 的實(shí)現(xiàn)有著很大的關(guān)聯(lián),由于大部分人都更熟悉自己常用的框架,導(dǎo)致很容易被當(dāng)前框架的思路帶進(jìn)去。
那么接下來(lái),就看一下各種常用 語(yǔ)言 / 框架 在 Session 機(jī)制上的實(shí)現(xiàn)與差異。
注:以下只是各個(gè) 語(yǔ)言 / 框架 默認(rèn)支持的實(shí)現(xiàn),實(shí)際中我們完全可以自定義任何形式的實(shí)現(xiàn)。
PHP 不愧是為 Web 而生的語(yǔ)言,在語(yǔ)言層面就提供了 Session 機(jī)制的支持,按默認(rèn)配置就可以使用 $_SESSION 這個(gè)超全局變量。
原生 PHP 默認(rèn)使用的 Session-Data 存儲(chǔ)方式為 Server Side Session ,使用 Session-ID 進(jìn)行索引,Session-ID 默認(rèn)存儲(chǔ)在 Cookies 的 PHPSESSID 字段。
Session-Data 默認(rèn)以文件形式存儲(chǔ)在 session_save_path 所配置的路徑中, PHPSESSID字段的內(nèi)容就是文件名,文件內(nèi)容就是序列化后的 Session-Data 數(shù)據(jù)。
通過(guò)配置 session.save_handler 字段,我們可以將存儲(chǔ)方式切換為 redis 或者 memcache 。
以 redis 為例,Session-Data 會(huì)被存儲(chǔ)在PHPREDIS_SESSION:PHPSESSID 中,PHPREDIS_SESSION:為默認(rèn)的統(tǒng)一前綴。
除此之外,還有一些其它的配置可以參考官方文檔,原生 PHP 也支持其它的相關(guān)擴(kuò)展,不再一一贅述。
Laravel 果然也很強(qiáng),不但默認(rèn)提供了常規(guī)的 Server Side Session ,也支持配置為 Client Side Session 形式。
通過(guò)配置 config/session.php 的 driver 字段,可以切換 file 、cookie 、database 、memcached 、redis 、array 這 6 種不同的存儲(chǔ)方式。
這里面, file 、memcached 、redis 都是比較常見(jiàn)的存儲(chǔ)方式,就不再贅述。
database 可以將 Session-Data 存儲(chǔ)在你定義的數(shù)據(jù)庫(kù)內(nèi),但是你需要先建好一張表,里面配置好要存儲(chǔ)的字段。
array 是一種比較奇怪的方式,只是用在開(kāi)發(fā)階段,它并沒(méi)有任何持久化功能,隨著 PHP 腳本的運(yùn)行結(jié)束而消失。
cookie 則是比較少見(jiàn)的** Client Side Session**了,Laravel 使用 EncryptedStore.php 對(duì) Session-Data 進(jìn)行 『加密』后存儲(chǔ)于客戶端瀏覽器的 Cookies 中。
這里需要注意,Laravel 在實(shí)現(xiàn) **Client Side Session **的時(shí)候使用的是 『加密』方式來(lái)保障信息安全。
接下來(lái)看看老牌框架 CI 怎么樣,翻看 配置文檔 可以看到,CI4 默認(rèn)支持 file 、database 、memcached 、redis 這幾種 Server Side Session,看起來(lái)似乎平平無(wú)奇,都是常見(jiàn)套路。
但是文檔上有一行備注:
In previous CodeIgniter versions, a different, “cookie driver” was the only option and we have received negative feedback on not providing that option. While we do listen to feedback from the community, we want to warn you that it was dropped because it is unsafe and we advise you NOT to try to replicate it via a custom driver.
這就很有意思了,看起來(lái) CI 框架以前也支持Client Side Session 嘛?翻出了 CI2 的文檔 ,果然里面默認(rèn)使用 Cookies 存儲(chǔ) Session-Data ,也提供了加密的選項(xiàng)。
那為什么 CI2 當(dāng)時(shí)會(huì)因?yàn)檫@個(gè)被錘呢?我找到一篇相關(guān)的文章:(IN)secure session data in CodeIgniter - Websec ,文章中的觀點(diǎn)來(lái)看:
其實(shí)這兩個(gè)問(wèn)題都更應(yīng)該歸咎于用戶的配置不當(dāng),但是畢竟和框架的默認(rèn)配置有關(guān),被錘也就在情理之中了。
可能也是因?yàn)檫@件事,才導(dǎo)致了 CI 現(xiàn)在只提供 Server Side Session 實(shí)現(xiàn)吧。
翻看了一下 Symfony、Yii 、CakePHP、ThinkPHP 等熱門框架關(guān)于 Session 的文檔,都使用了比較常規(guī)的 Server Side Session 方式,也都支持將 Session-Data 存儲(chǔ)在文件、數(shù)據(jù)庫(kù)、內(nèi)存數(shù)據(jù)庫(kù)等,在此就不一一介紹了,有需要的可以點(diǎn)鏈接查看相應(yīng)文檔。
Tomcat 也是 Server Side Session 一派,默認(rèn)在 Cookies 中使用 JSESSIONID 字段存儲(chǔ) Session-ID,Session-Data 默認(rèn)存儲(chǔ)在內(nèi)存中 ,翻閱 org.apache.catalina.session.ManagerBase 的源碼,可以看到它使用了一個(gè)ConcurrentHashMap 結(jié)構(gòu)來(lái)存儲(chǔ)所有的 Session-Data 實(shí)例。
Tomcat 默認(rèn)使用 StandardManager 來(lái)管理 Session-Data,當(dāng)容器退出時(shí),在退出時(shí)將所有的數(shù)據(jù)進(jìn)行持久化;你也可以選擇使用 PersistentManager 來(lái)管理,它可以更加靈活的執(zhí)行持久化(但是 Session-Data 還是會(huì)存儲(chǔ)在你的內(nèi)存里)。
Jetty 也比較中規(guī)中矩,參照文檔來(lái)看,支持將 Session-Data 配置為存儲(chǔ)在 內(nèi)存、文件、JDBC 等位置。
比較不同的是,Jetty 支持為 Session-Data 配置 L1、 L2 兩級(jí)緩存,在對(duì)性能有需求的場(chǎng)景下還是挺友好。
果然是家大業(yè)大的框架,Spring Session 自己就是個(gè)完整的組件了,毫無(wú)意外的支持把 Session-Data 儲(chǔ)存在 redis 、mongodb ,jdbc 等地方,方便擴(kuò)展。
不知如此,它還有一些特有的特性:
在舊一些的版本中,Spring Session 還支持 在一個(gè)瀏覽器會(huì)話中,維護(hù)多個(gè) Session ,不過(guò)在新版本中,這個(gè)特性被刪除了,也許以后才會(huì)加回來(lái)。
這個(gè)巨無(wú)霸框架其實(shí)還支持其它一大堆特性,這里就不太多介紹了。
作為一個(gè)功能完善的重量級(jí)框架,Django 默認(rèn)直接將 Session-Data 存儲(chǔ)在了數(shù)據(jù)庫(kù),當(dāng)然,你也可以配置為其它的存儲(chǔ)方式,不再贅述。
這些存儲(chǔ)方式中,Client Side Session 的代表 Cookies 再次出現(xiàn),Session-Data 在序列化并『簽名』后,存儲(chǔ)在客戶端 Cookies 中。
這里需要注意,此處是『簽名』而不是『加密』,也就是說(shuō),客戶端雖然不能篡改 Session-Data,但是可以自己反序列化后查看內(nèi)容。
另外,Django 的 Session-data 還支持使用 Pickle 進(jìn)行序列化,對(duì)于某些場(chǎng)景會(huì)非常有用(同時(shí)也帶來(lái)了一些潛在風(fēng)險(xiǎn))。
翻下文檔,專注于輕量的框架 Flask ,毫無(wú)疑問(wèn)的選擇了實(shí)現(xiàn)起來(lái)最簡(jiǎn)單的方案:直接用Client Side Session 就行了。
把 Session-Data 序列化并『簽名』后,存儲(chǔ)在客戶端 Cookies 中就搞掂啦。這里同樣需要注意,是『簽名』而不是『加密』。
不過(guò)這畢竟是個(gè) Python 框架,人家包多啊,使用 Flask-Session 就可以獲得更多功能,支持將 Session-Data 存儲(chǔ)在Cookies、文件、Redis、數(shù)據(jù)庫(kù)等各種地方。
這個(gè)已經(jīng)接近涼涼的框架表示:不好意思,我們沒(méi)有現(xiàn)成的 Session 模塊,想要用?那你自己寫去~
Why doesn't Tornado have session - Stack Exchange
Express 自身是最小化實(shí)現(xiàn),Session 相關(guān)的功能由各種中間件來(lái)實(shí)現(xiàn),此處主要提兩個(gè)中間件。
express-session 是典型的 Server Side Session 實(shí)現(xiàn),依靠強(qiáng)大的包數(shù)量支持很多種存儲(chǔ)后端。
比較有意思的是,雖然是 Server Side Session 實(shí)現(xiàn),express-session 依然不放心,給 Cookies 里的 Session-ID 加了一層『簽名』做校驗(yàn),可以說(shuō)非常心細(xì)了。
cookie-session 相對(duì)就比較普通,就是一個(gè)常規(guī)的 Client Side Session 實(shí)現(xiàn), Session-Data 在序列化并『簽名』后,存儲(chǔ)在客戶端的 Cookies 內(nèi)。
包多就是好啊,koa 也有一大堆 Session 相關(guān)的中間件,在此就不一一介紹了。
翻了下 Gin 比較常用的中間件是 gin-contrib/sessions ,同時(shí)支持 Server / Client Side Session ,也支持多種儲(chǔ)存后端。
這個(gè)中間件默認(rèn)支持在一個(gè)瀏覽器會(huì)話中,維護(hù)多個(gè) Session ,對(duì)特定的需求會(huì)方便一些。
Echo 的文檔中,默認(rèn)使用了 gorilla/sessions 這個(gè)中間件,也是同時(shí)支持 Server / Client Side Session ,可以擴(kuò)展支持其它存儲(chǔ)后端。
這個(gè)中間件也默認(rèn)支持在一個(gè)瀏覽器會(huì)話中,維護(hù)多個(gè) Session多個(gè) Session ,對(duì)特定的需求會(huì)方便一些。
按照 Beego 的英文文檔來(lái)看,默認(rèn)只支持 Server Side Session,但是奇怪的是中文文檔上卻又寫著支持的后端引擎包括 Cookie ,可能是文檔寫錯(cuò)了。
Rails 默認(rèn)使用 Client Side Session 實(shí)現(xiàn),Session-Data 『加密』并『簽名』后存儲(chǔ)在客戶端瀏覽器的 Cookies 中。也支持配置為 Server Side Session 實(shí)現(xiàn),可以使用多種后端存儲(chǔ)。
在其它許多框架的 Session 實(shí)現(xiàn)中,都可以看到一個(gè)叫做 Flash 的特殊數(shù)據(jù),用于實(shí)現(xiàn)輕量的通知反饋等,這個(gè)功能應(yīng)該是 Rails 首創(chuàng)的。
另外,Rails 關(guān)于 Session 安全的文檔寫的非常非常細(xì)致,強(qiáng)烈建議閱讀。
Sever Side Session 是目前最常見(jiàn)的 Session 實(shí)現(xiàn),以至于不少人會(huì)誤以為它是唯一一種 Session 實(shí)現(xiàn)。
優(yōu)點(diǎn):
劣勢(shì):
Client Side Session 相對(duì)小眾一些,但也可以看到許多框架都保留了對(duì)它的支持,Rails 甚至在文檔中專門寫了一大段來(lái)描述為什么自己默認(rèn)這么用。
優(yōu)點(diǎn):
劣勢(shì):
對(duì)于 Server Side Session 來(lái)說(shuō),絕大部分實(shí)現(xiàn)都會(huì)選擇將 Session-ID 放在 Cookies 中,在發(fā)起請(qǐng)求時(shí)自動(dòng)帶上,這樣也是很符合直覺(jué)的做法,但是凡事都有例外,還是有一些特殊的實(shí)現(xiàn)。
例如,PHP 默認(rèn)就支持拋開(kāi) Cookies,使用 URL 傳遞 Session-ID,相關(guān)的配置為 use_cookies 、 use_only_cookies 字段。
在 Tomcat 6.0 中,也支持類似的功能,但似乎是通過(guò)當(dāng)前會(huì)話是否存在 Cookies 來(lái)判斷的,也可以通過(guò)配置 disableURLRewriting 來(lái)關(guān)閉。
當(dāng)然也存在不同的聲音,比如 Django 就明確拒絕這么做,為此還在文檔中專門寫了原因 ,其一是make URLs ugly,其二是不安全。
除此之外,在 WAP 時(shí)代,由于早期手機(jī)瀏覽器很多不支持 Cookie,這種行為也非常常見(jiàn)。
必須要說(shuō)的是,這樣確實(shí)很不安全,比如可以參考這篇 9 年前的文章:淺談WAP網(wǎng)站安全 - 空虛浪子心 。
其實(shí)前面在說(shuō)到 Spring 的時(shí)候就有提到,它支持將 Session-ID 放進(jìn) Header 中的X-Auth-Token 字段中。
實(shí)際上,只要前后端協(xié)調(diào)一致,其實(shí)放在任何一個(gè)字段都是沒(méi)問(wèn)題的。
這種處理方式我只在資料中看到過(guò),大意就是在輸出頁(yè)面時(shí),為每一個(gè) Form 都補(bǔ)一個(gè)隱藏的 Session-ID 字段。
<form name="anyform" action="/xxx">
<input type="hidden" name="jsessionid" value="ByOK2vjFD43aPnrF6C2HmdnV6QZcEbzWoWiBYEnLerjQ22zWpBng!-1582381342">
<input type="text">
</form>
這種方式存在非常多的弊端,也難怪現(xiàn)在基本上看不到了。
對(duì)于 Client Side Session 來(lái)說(shuō),因?yàn)椴恍枰饕恍枰春?Session-Data 就好,在這件事上,大家也有不一樣的思路。
大部分實(shí)現(xiàn)都選擇只對(duì) Session-Data 做『簽名』處理,這樣就算客戶端知道 Session-Data 內(nèi)部的數(shù)據(jù)是什么,也無(wú)法簡(jiǎn)單的篡改。
但是并不是所有的開(kāi)發(fā)者都會(huì)注意這些,保不齊會(huì)有人存敏感信息在里面,所以部分框架不但做了簽名,還做了『加密』,看也不讓看。
需要注意的是,這樣仍然存在重放攻擊的風(fēng)險(xiǎn),用戶可以將其替換為舊的合法 Session-Data,此時(shí)服務(wù)端是無(wú)法鑒別出差異的。
另外,Rails 關(guān)于 Session 安全的文檔寫的非常非常細(xì)致,再次強(qiáng)烈建議閱讀。
Session 的概念并不復(fù)雜,洋洋灑灑寫了這么多,其實(shí)還是因?yàn)橛懻摃r(shí)遇到的各種誤解,這里我們復(fù)盤一下。
不能等同,Session-ID 只是 Server Side Session 實(shí)現(xiàn)中,常用的索引信息。
Cookies 經(jīng)常用于承載 Session 的信息(ID 或 Data),但并不是必然情況。
這是經(jīng)常被誤解的一點(diǎn),在此說(shuō)下我個(gè)人的理解。
Token 并不是一個(gè)以技術(shù)手段定義的詞匯,它的定義來(lái)源于『用途』。
但凡用于認(rèn)證鑒權(quán)的 憑據(jù),我認(rèn)為都可以稱作 Token ,與這個(gè)憑據(jù)的生成方式無(wú)關(guān)。
基于此,如果 Session-ID 或 Session-Data 被用作認(rèn)證,那么我認(rèn)為這條數(shù)據(jù)就可以被稱為 Token。
但是需要注意,Session 自身是個(gè)虛擬概念,與 Token 毫不相關(guān)。
但凡提到 Session,就不可避免的會(huì)有人搬出 JWT ,也會(huì)有很多人把 Session 和 JWT 擺到對(duì)立面來(lái)討論,但是這兩個(gè)壓根就不在一個(gè)維度。
先來(lái)看看 JWT 到底是什么,它的全名是 JSON Web Token ,從名字就可以看出,它是特定的一種 Token 生成方式。
JWT 的生成方式大致是這樣:將數(shù)據(jù)按特定格式進(jìn)行序列化,標(biāo)記過(guò)期時(shí)間,對(duì)數(shù)據(jù)進(jìn)行『簽名』后編碼為 URL Safe 的 Base64URL 。
誒?好像有點(diǎn)眼熟?這怎么和 Client Side Session 那邊對(duì) Session-Data 的處理那么像呢?
除此之外,這里有一篇蠻有名的文章:Stop using JWT for sessions 也可以一起看看。
看完文章會(huì)發(fā)現(xiàn),作者提到的那些將 JWT 用做 Session 實(shí)現(xiàn)的人,實(shí)際上說(shuō)的都是** Client Side Session**啊。
那么問(wèn)題變的比較清晰了:
而那些關(guān)于 JWT 與 Session 優(yōu)劣的爭(zhēng)論,實(shí)際上大部分都是在爭(zhēng)論 Sever Side Session 和 Client Side Session 的優(yōu)劣。
扯了冗長(zhǎng)的上萬(wàn)字,其實(shí)就是為了說(shuō)明一個(gè)『定義』的問(wèn)題,Session 本身并不復(fù)雜,只要不把各種簡(jiǎn)稱搞混。
很多時(shí)候由于大量使用簡(jiǎn)稱,或者模糊定義,導(dǎo)致我們對(duì)概念本身失去了掌握,也經(jīng)常讓討論變的低效。
另一方面,不能把自己所熟悉的那些實(shí)現(xiàn)當(dāng)作全部,時(shí)不時(shí)的看看其它語(yǔ)言或框架,也許能發(fā)現(xiàn)許多好玩兒的東西。
最后,如果本文有哪些地方出現(xiàn)了疏漏或錯(cuò)誤,還請(qǐng)不吝賜教,一起交流進(jìn)步。
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。