登錄校驗
實現登陸后才能訪問后端系統頁面,不登陸則跳轉登陸頁面進行登陸。
首先我們在宏觀上先有一個認知:
HTTP協議是無狀態協議。即每一次請求都是獨立的,下一次請求并不會攜帶上一次請求的數據。
因此當我們通過瀏覽器訪問登錄后,接下來我們執行其他業務操作時,服務器無法判斷用戶是否登錄。
那應該怎么來實現登錄校驗的操作呢?具體的實現思路可以分為兩部分:
- 在登錄成功后,需要將用戶登錄成功的信息存起來,記錄用戶已經登錄成功的標記。
- 在瀏覽器發起請求時,需要在服務端進行統一攔截,攔截后進行登錄校驗。
如果在每一個功能中都添加判斷邏輯就會出現相同代碼邏輯,每個功能都需要編寫,就會造成代碼非常繁瑣。
為了簡化這塊操作,我們可以使用:統一攔截技術。
統一攔截技術:
攔截瀏覽器發送過來的所有請求,攔截后通過請求來獲取之前所存入的登錄標記。,以此判斷是否放行。
我們要完成以上操作,會涉及到web開發中的兩個技術:
- 會話技術
- 統一攔截技術
- Servlet規范中的Filter過濾器
- Spring提供的interceptor攔截器
會話技術
什么是會話?
- 瀏覽器與服務器之間的一次連接,我們就稱為一次會話。
- 用戶打開瀏覽器,訪問wb服務器的資源,會話建立,直到有一方斷開連接,會話結束。
- 在一次會話中可以包含多次請求和響應。
會話跟蹤
會話跟蹤:一種維護瀏覽器狀態的方法,服務器需要識別多次請求是否來自于同一瀏覽器,以便在同一次會話的多次請求間共享數據。
識別多次請求是否來自于同一瀏覽器的過程,我們就稱為會話跟蹤。
為什么要共享數據呢?
由于HTTP是無狀態協議,在后面請求中怎么拿到前一次請求生成的數據呢?此時就需要在一次會話的多次請求之間進行數據共享
會話跟蹤技術有三種:
- Cookie(客戶端會話跟蹤技術)
- 數據存儲在客戶端瀏覽器
- Session(服務端會話跟蹤技術)
- 數據存儲在服務端
- 令牌技術
會話跟蹤方案
方案一:Cookie
cookie 是客戶端會話跟蹤技術,它是存儲在客戶端瀏覽器的,我們使用 cookie 來跟蹤會話,就可以在瀏覽器第一次發起請求來請求服務器的時候,我們在服務器端來設置一個cookie。
在 cookie 當中我們可以來存儲用戶相關的一些數據信息。比如當前登錄用戶的用戶名,用戶的ID。
服務器端在給客戶端在響應數據的時候
- 會自動的將 cookie 響應給瀏覽器。
- 瀏覽器接收到響應回來的 cookie 之后,會自動將 cookie 的值存儲在瀏覽器本地。
- 在后續的每一次請求當中,瀏覽器會自動將 cookie 攜帶到服務器端。
通過獲取到的 cookie 值我們就可以判斷當前用戶是否登錄。
為什么這一切都是自動化進行的?
因為 cookie 它是 HTTP 協議當中所支持的技術,而各大瀏覽器廠商都支持了這一標準。在 HTTP 協議官方給我們提供了一個響應頭和請求頭:
- 響應頭
Set-Cookie
:設置Cookie數據 - 請求頭
Cookie
:攜帶Cookie數據
代碼演示
@Slf4j
@RestController
public class SessionController {//設置Cookie@GetMapping("/c1")public Result cookie1(HttpServletResponse response){response.addCookie(new Cookie("login_username","itheima")); //設置Cookie/響應Cookiereturn Result.success();}//獲取Cookie@GetMapping("/c2")public Result cookie2(HttpServletRequest request){Cookie[] cookies = request.getCookies();for (Cookie cookie : cookies) {if(cookie.getName().equals("login_username")){System.out.println("login_username: "+cookie.getValue()); //輸出name為login_username的cookie}}return Result.success();}
}
- 訪問c1接口,設置Cookie,http://localhost:8080/c1
我們可以看到,設置的cookie,通過響應頭Set-Cookie響應給瀏覽器,并且瀏覽器會將Cookie,存儲在瀏覽器端。
- 訪問c2接口 http://localhost:8080/c2,此時瀏覽器會自動的將Cookie攜帶到服務端,是通過請求頭Cookie,攜帶的。
優缺點
- 優點:HTTP協議中支持的技術(像Set-Cookie 響應頭的解析以及 Cookie 請求頭數據的攜帶,都是瀏覽器自動進行的,是無需我們手動操作的)
- 缺點:
- 移動端APP(Android、IOS)中無法使用Cookie
- 不安全,用戶可以自己禁用Cookie
- Cookie不能跨域
跨域介紹:
- 現在的項目,大部分都是前后端分離的,前后端最終也會分開部署,前端部署在服務器 192.168.150.200 上,端口 80,后端部署在 192.168.150.100上,端口 8080
- 我們打開瀏覽器直接訪問前端工程,訪問url:http://192.168.150.200/login.html
- 然后在該頁面發起請求到服務端,而服務端所在地址不再是localhost,而是服務器的IP地址192.168.150.100,假設訪問接口地址為:http://192.168.150.100:8080/login
- 那此時就存在跨域操作了,因為我們是在 http://192.168.150.200/login.html 這個頁面上訪問了http://192.168.150.100:8080/login 接口
- 此時如果服務器設置了一個Cookie,這個Cookie是不能使用的,因為Cookie無法跨域
區分跨域的維度:
- 協議
- IP/協議
- 端口
只要上述的三個維度有任何一個維度不同,那就是跨域操作
示例 | 是否跨域及原因 |
---|---|
http://192.168.150.200/login.html ----------> https://192.168.150.200/login | 協議不同,跨域 |
http://192.168.150.200/login.html ----------> http://192.168.150.100/login | IP不同,跨域 |
http://192.168.150.200/login.html ----------> http://192.168.150.200:8080/login | 端口不同,跨域 |
http://192.168.150.200/login.html ----------> http://192.168.150.200/login | 不跨域 |
方案二:Session
Session 的底層其實就是基于剛才所介紹的 Cookie 來實現的。
-
獲取Session
- 瀏覽器在第一次請求服務器的時候,我們就可以直接在服務器當中來獲取到會話對象Session。
- 如果是第一次請求Session ,會話對象是不存在的,這個時候服務器會自動的創建一個會話對象Session 。
- 每一個會話對象Session ,它都有一個ID(示意圖中Session后面括號中的1,就表示ID),我們稱之為 Session 的ID。
-
響應Cookie (JSESSIONID)
- 服務器端在給瀏覽器響應數據的時候,它會將 Session 的 ID 通過 Cookie 響應給瀏覽器。
- 在響應頭當中增加了一個 Set-Cookie 響應頭。這個 Set-Cookie 響應頭對應的值是cookie,cookie 的名字是固定的 JSESSIONID 代表的服務器端會話對象 Session 的 ID。
- 瀏覽器會自動識別這個響應頭,然后自動將Cookie存儲在瀏覽器本地。
-
查找Session
- 接下來,在后續的每一次請求當中,都會將 Cookie 的數據獲取出來,并且攜帶到服務端。接下來服務器拿到JSESSIONID這個 Cookie 的值,也就是 Session 的ID。拿到 ID 之后,就會從眾多的 Session 當中來找到當前請求對應的會話對象Session。
這樣就可以通過 Session 會話對象在同一次會話的多次請求之間來共享數據了,這就是基于 Session 進行會話跟蹤的流程。
代碼演示
@Slf4j
@RestController
public class SessionController {@GetMapping("/s1")public Result session1(HttpSession session){log.info("HttpSession-s1: {}", session.hashCode());session.setAttribute("loginUser", "tom"); //往session中存儲數據return Result.success();}@GetMapping("/s2")public Result session2(HttpServletRequest request){HttpSession session = request.getSession();log.info("HttpSession-s2: {}", session.hashCode());Object loginUser = session.getAttribute("loginUser"); //從session中獲取數據log.info("loginUser: {}", loginUser);return Result.success(loginUser);}
}
- 訪問 s1 接口,http://localhost:8080/s1
請求完成之后,在響應頭中,就會看到有一個Set-Cookie的響應頭,里面響應回來了一個Cookie,就是JSESSIONID,這個就是服務端會話對象 Session 的ID。
- 訪問 s2 接口,http://localhost:8080/s2
接下來,在后續的每次請求時,都會將Cookie的值,攜帶到服務端,服務端接收到Cookie之后,會自動的根據JSESSIONID的值,找到對應的會話對象Session。
那經過這兩步測試,大家也會看到,在控制臺中輸出如下日志:
兩次請求,獲取到的Session會話對象的hashcode是一樣的,就說明是同一個會話對象。而且,第一次請求時,往Session會話對象中存儲的值,第二次請求時,也獲取到了。 那這樣,我們就可以通過Session會話對象,在同一個會話的多次請求之間來進行數據共享了。
優缺點
- 優點:Session是存儲在服務端的,安全
- 缺點:
- 服務器集群環境下無法直接使用Session
- 移動端APP(Android、IOS)中無法使用Cookie
- 用戶可以自己禁用Cookie
- Cookie不能跨域
PS:Session 底層是基于Cookie實現的會話跟蹤,如果Cookie不可用,則該方案,也就失效了。
服務器集群環境為何無法使用Session?
首先第一點,我們現在所開發的項目,一般都不會只部署在一臺服務器上,因為一臺服務器會存在一個很大的問題,就是單點故障。所謂單點故障,指的就是一旦這臺服務器掛了,整個應用都沒法訪問了。
![在這里插入圖片描述](https://img-blog.csdnimg.cn/direct/ffd4ee2143a1477aaf51202a3712d06d.png#pic_center
- 所以在現在的企業項目開發當中,最終部署的時候都是以集群的形式來進行部署,也就是同一個項目它會部署多份。
- 而用戶在訪問的時候,他會訪問一臺前置的服務器,我們叫負載均衡服務器,它的作用就是將前端發起的請求均勻的分發給后面的這三臺服務器。
此時假如我們通過 session 來進行會話跟蹤,可能就會存在這樣一個問題。
- 用戶打開瀏覽器要進行登錄操作,此時會發起登錄請求。登錄請求到達負載均衡服務器,將這個請求轉給了第一臺 Tomcat 服務器。
- 服務器接收到請求后,獲取到會話對象session,然后給瀏覽器響應數據和攜帶一個cookie。
- 此時,我們再執行一次操作,這次請求到達負載均衡服務器之后,負載均衡服務器將這次請求轉給了第二臺 Tomcat 服務器,此時他就要到第二臺 Tomcat 服務器當中根據JSESSIONID 也就是對應的 session 的 ID 值,要找對應的 session 會話對象。
- 顯然,第二臺服務器當中并沒有這個ID的會話對象 Session。
同一個瀏覽器發起了 2 次請求,結果獲取到的不是同一個會話對象,這就是Session這種會話跟蹤方案它的缺點,在服務器集群環境下無法直接使用Session。