20191126 Java Web之單系統登錄知識點

1、前言

在工作中登錄功能十分常見,自己也接觸了不少,如常見的單系統賬密登錄,以及無需輸入賬密的掃碼登錄,以及通過第三方登錄實現快速註冊登錄,還有微信小程序的授權登錄。這裏要做個總結的話也是挺多東西可以寫的。

雖然工作中沒有單點登錄的需求,但它也是Java WEB中很重要的一塊知識,於是覺得很有必要了解一下。而要學懂單點登錄,單系統登錄是基礎。

文中也不由自主的介紹Java Web相關的一些知識。因此可能全文讀下來會抓不到重點,也是我寫作技巧不夠還需努力提升。我也盡力去整理下章節順序和小標題了,讓文章整體更加清晰。

涉及到的知識點有:

  • http協議
  • session
  • cookie
  • 會話機制
  • tomcat
  • redis
  • 跨域問題
  • 瀏覽器的同源策略

2、單系統登錄

單系統登錄的實現是單點登錄實現的基礎,而且也涉及到了不少知識點,因此理解單系統是十分有必要的。

2.1、B/S架構

web應用採用的是B/S架構,以http協議爲通信協議。而http屬於無狀態的請求-響應模式,即瀏覽器向服務器發起一次請求,服務器會給予一次響應。並且對於服務器而言,每次的請求都是全新的,即跟之前的請求沒有任何關聯。

2.2、web資源需要被保護

但是對於一些資源的訪問,如私密的信息,如我的相冊、我的好友列表等,需要被保護,即需要驗證身份才能進行訪問,而登錄操作就是身份驗證的一種方式。

那麼我們可以規定:只有通過登錄驗證的,才能去訪問自己的相冊圖片。可是這樣並不安全,因爲登錄操作實質上是發起一次http請求,而訪問圖片實質上也是發起一次http請求。也就是說,通過上面規定的簡單的流程控制,並不能實現資源保護。因爲我可以通過跳過登錄操作,直接在瀏覽器發起訪問圖片資源的請求,如此越過登錄驗證這道坎。http協議的無狀態性使得服務器無法知道哪個用戶登錄過了而哪些用戶沒有登錄過。而每一次發起請求時要求用戶輸入賬號密碼來驗證身份又是不現實的。

那麼可不可以這樣:登錄驗證通過後,服務器返回一個令牌(如一串隨機數),並記錄下這個令牌對應着哪個用戶。對於登錄之外的每個請求,都要帶上這個令牌,而服務器會進行相應的驗證。這樣,只要這個令牌不泄漏出去,就能保證資源不會被非法訪問。

也就是說,通過服務器和瀏覽器共同存儲一些數據(瀏覽器端存儲令牌,服務器端存儲令牌即對應的賬號的登錄狀態),如此在無狀態的http協議下維持一個有狀態的會話。

2.3、會話機制中的session和cookie

而瀏覽器端具體使用的是cookie機制,而服務器端使用的是session機制。對於使用tomcat作爲web容器的java程序,當在服務器端獲取session實例的時候,就會創建一個JSESSIONID,並放到cookie返回給瀏覽器。而瀏覽器端之後發起的請求中的cookie裏,也會包含這個JSESSIONID。通過這個機制,就可以在通過登錄驗證之後,服務器端創建session並通過session.setAttribute("isLogin",true)存儲用戶的登錄狀態,之後瀏覽器的後續請求中,服務端會通過session.getAttribute("isLogin")獲取到該請求的用戶的登錄狀態,如果不爲true,說明該請求是非法請求或者是無效請求,則不給予正常的響應信息。

2.4、對session、tomcat、web服務器的思考

這裏有一塊我個人覺得挺重要的知識: 對於我們在tomcat運行的web程序,通過瀏覽器的開發者工具,我們可以看到這個sessionId叫做JSESSIONID。而對於其他web容器,其創建的sessionId可能是別的名字。因此,當我們說php程序和java程序中sessionId不一致時,其實不是編程語言不同導致的,而是web容器的實現不同導致的。這說明session的實現是web容器完成的。tomcat的實現決定了使用tomcat作爲web容器的java程序返回的sessionId的名字爲JSESSIONID。
簡而言之,request、response、session等是由web容器實現的。我想這些知識在學習Java Web的時候也會有提及過,但是至少於我而言,在當時是無法理解的,也不會去注意這個知識點。不知道大家的情況如何。

2.5、由web服務器、session聯想到面向接口編程的好處

我們在寫service層服務的時候,通常是先定義接口,然後再寫具體實現。即面向接口編程。可是我一直沒有感覺這樣寫的好處,可能因爲寫的代碼太簡單,模塊架構太簡單吧。

而當我意識到session是由web服務器實現的時候,才突然意識到面向接口編程有多強大,我們在獲取session對象的時候,通常是這麼寫的:

HttpSession session = request.getSession();

也就是說,我們創建的HttpSession引用類型是接口而不是具體實現類。即session具體是由哪種web容器實現的,這對我們獲取以及使用session是無感知的,除了引入的依賴不同,其餘是沒區別的。這樣的好處是,我們可以隨意的切換web容器。jetty或者tomcat。

2.6、使用redis替代session

而實際應用中,我是使用redis來替代session。爲什麼好好的session不用,要額外的去使用redis呢?這是因爲當我重新部署war包時,該程序中所有的session都會失效。也就是說,原本處於登錄驗證通過的用戶,他們的登錄狀態都會失效。而這是因爲重新部署war包時,tomcat會重新加載web程序中的web.xml文件,因此會把該程序的上下文環境給清空掉,這就導致了所有的session都被清空掉。因此將信息存儲在redis中。這裏可以參考session機制,自己創建一個sessionId,在登錄驗證通過後,手動創建一個用於存儲我們創建的sessionId存到cookie中,一樣的,瀏覽器在之後的接口會回傳該cookie。這樣,當重新部署war程序,由於redis是獨立的程序,不會受到影響。這樣就能無縫的用redis實現session機制了。
並且,當該程序集羣部署時,也能正常運行。(而使用session則不行)

2.7、web容器——tomcat的重要性

題外話,也是我個人覺得很重要的知識:我們寫的接口即war程序之所以能被瀏覽器訪問,很多部分功勞是因爲有web服務器如tomcat。因此要好好理解web服務器——tomcat存在的意義。好好想想爲什麼我們寫好的程序都要部署到tomcat中,而爲什麼我們用socket寫的程序則可以不用部署到tomcat中也能被外界訪問。

2.8、跨域問題

在前後端對接接口時,也經常會遇到跨域問題。如前端用vue實現而後端用java實現時,前後端聯調時可能會出現:在登錄頁面,輸入賬密後點擊登錄,會向後端發起登錄驗證請求,後端驗證通過後返回一些信息,但是前端卻接收不到。但是單獨用postman測試後端接口卻是能返回數據的,而前端表示其他程序中也是用同樣的方式向後端發起請求。這其實什麼奇異的事情。因爲出在瀏覽器的一個安全保護——同源策略。簡單的說是因爲不同源。而爲什麼以前學習Java Web的時候,用jsp+servlet做一個簡陋web程序的時候,卻不會出現跨域問題呢?簡單的說是因爲同源。源指的是什麼?我們說跨域,跨域的主體是誰?而同源策略是什麼?爲什麼瀏覽器會有同源策略?這裏不去介紹,因爲這一塊的知識會單獨寫一篇的總結理解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章