中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁(yè) > news >正文

蘇州做網(wǎng)站多少錢廣告投放平臺(tái)系統(tǒng)

蘇州做網(wǎng)站多少錢,廣告投放平臺(tái)系統(tǒng),騰訊郵箱注冊(cè),Wordpress頁(yè)面手機(jī)不適配什么是登錄校驗(yàn)? 所謂登錄校驗(yàn),指的是我們?cè)诜?wù)器端接收到瀏覽器發(fā)送過來的請(qǐng)求之后,首先我們要對(duì)請(qǐng)求進(jìn)行校驗(yàn)。先要校驗(yàn)一下用戶登錄了沒有,如果用戶已經(jīng)登錄了,就直接執(zhí)行對(duì)應(yīng)的業(yè)務(wù)操作就可以了;如果用…

什么是登錄校驗(yàn)?

  • 所謂登錄校驗(yàn),指的是我們?cè)诜?wù)器端接收到瀏覽器發(fā)送過來的請(qǐng)求之后,首先我們要對(duì)請(qǐng)求進(jìn)行校驗(yàn)。先要校驗(yàn)一下用戶登錄了沒有,如果用戶已經(jīng)登錄了,就直接執(zhí)行對(duì)應(yīng)的業(yè)務(wù)操作就可以了;如果用戶沒有登錄,此時(shí)就不允許他執(zhí)行相關(guān)的業(yè)務(wù)操作,直接給前端響應(yīng)一個(gè)錯(cuò)誤的結(jié)果,最終跳轉(zhuǎn)到登錄頁(yè)面,要求他登錄成功之后,再來訪問對(duì)應(yīng)的數(shù)據(jù)。

了解完什么是登錄校驗(yàn)之后,接下來我們分析一下登錄校驗(yàn)大概的實(shí)現(xiàn)思路。

首先我們?cè)诤暧^上先有一個(gè)認(rèn)知:

前面在講解HTTP協(xié)議的時(shí)候,我們提到HTTP協(xié)議是無狀態(tài)協(xié)議。什么又是無狀態(tài)的協(xié)議?

所謂無狀態(tài),指的是每一次請(qǐng)求都是獨(dú)立的,下一次請(qǐng)求并不會(huì)攜帶上一次請(qǐng)求的數(shù)據(jù)。而瀏覽器與服務(wù)器之間進(jìn)行交互,基于HTTP協(xié)議也就意味著現(xiàn)在我們通過瀏覽器來訪問了登陸這個(gè)接口,實(shí)現(xiàn)了登陸的操作,接下來我們?cè)趫?zhí)行其他業(yè)務(wù)操作時(shí),服務(wù)器也并不知道這個(gè)員工到底登陸了沒有。因?yàn)镠TTP協(xié)議是無狀態(tài)的,兩次請(qǐng)求之間是獨(dú)立的,所以是無法判斷這個(gè)員工到底登陸了沒有。

那應(yīng)該怎么來實(shí)現(xiàn)登錄校驗(yàn)的操作呢?具體的實(shí)現(xiàn)思路可以分為兩部分:

  1. 在員工登錄成功后,需要將用戶登錄成功的信息存起來,記錄用戶已經(jīng)登錄成功的標(biāo)記。

  2. 在瀏覽器發(fā)起請(qǐng)求時(shí),需要在服務(wù)端進(jìn)行統(tǒng)一攔截,攔截后進(jìn)行登錄校驗(yàn)。

想要判斷員工是否已經(jīng)登錄,我們需要在員工登錄成功之后,存儲(chǔ)一個(gè)登錄成功的標(biāo)記,接下來在每一個(gè)接口方法執(zhí)行之前,先做一個(gè)條件判斷,判斷一下這個(gè)員工到底登錄了沒有。如果是登錄了,就可以執(zhí)行正常的業(yè)務(wù)操作,如果沒有登錄,會(huì)直接給前端返回一個(gè)錯(cuò)誤的信息,前端拿到這個(gè)錯(cuò)誤信息之后會(huì)自動(dòng)的跳轉(zhuǎn)到登錄頁(yè)面。

我們程序中所開發(fā)的查詢功能、刪除功能、添加功能、修改功能,都需要使用以上套路進(jìn)行登錄校驗(yàn)。此時(shí)就會(huì)出現(xiàn):相同代碼邏輯,每個(gè)功能都需要編寫,就會(huì)造成代碼非常繁瑣。

為了簡(jiǎn)化這塊操作,我們可以使用一種技術(shù):統(tǒng)一攔截技術(shù)。

通過統(tǒng)一攔截的技術(shù),我們可以來攔截瀏覽器發(fā)送過來的所有的請(qǐng)求,攔截到這個(gè)請(qǐng)求之后,就可以通過請(qǐng)求來獲取之前所存入的登錄標(biāo)記,在獲取到登錄標(biāo)記且標(biāo)記為登錄成功,就說明員工已經(jīng)登錄了。如果已經(jīng)登錄,我們就直接放行(意思就是可以訪問正常的業(yè)務(wù)接口了)。

我們要完成以上操作,會(huì)涉及到web開發(fā)中的兩個(gè)技術(shù):

  1. 會(huì)話技術(shù)

  2. 統(tǒng)一攔截技術(shù)

而統(tǒng)一攔截技術(shù)現(xiàn)實(shí)方案也有兩種:

  1. Servlet規(guī)范中的Filter過濾器

  2. Spring提供的interceptor攔截器

1會(huì)話技術(shù)

介紹了登錄校驗(yàn)的大概思路之后,我們先來學(xué)習(xí)下會(huì)話技術(shù)。

1.1.1 會(huì)話技術(shù)介紹

什么是會(huì)話?

  • 在我們?nèi)粘I町?dāng)中,會(huì)話指的就是談話、交談。

  • 在web開發(fā)當(dāng)中,會(huì)話指的就是瀏覽器與服務(wù)器之間的一次連接,我們就稱為一次會(huì)話。

    在用戶打開瀏覽器第一次訪問服務(wù)器的時(shí)候,這個(gè)會(huì)話就建立了,直到有任何一方斷開連接,此時(shí)會(huì)話就結(jié)束了。在一次會(huì)話當(dāng)中,是可以包含多次請(qǐng)求和響應(yīng)的。

    比如:打開了瀏覽器來訪問web服務(wù)器上的資源(瀏覽器不能關(guān)閉、服務(wù)器不能斷開)

    • 第1次:訪問的是登錄的接口,完成登錄操作

    • 第2次:訪問的是部門管理接口,查詢所有部門數(shù)據(jù)

    • 第3次:訪問的是員工管理接口,查詢員工數(shù)據(jù)

    只要瀏覽器和服務(wù)器都沒有關(guān)閉,以上3次請(qǐng)求都屬于一次會(huì)話當(dāng)中完成的。

需要注意的是:會(huì)話是和瀏覽器關(guān)聯(lián)的,當(dāng)有三個(gè)瀏覽器客戶端和服務(wù)器建立了連接時(shí),就會(huì)有三個(gè)會(huì)話。同一個(gè)瀏覽器在未關(guān)閉之前請(qǐng)求了多次服務(wù)器,這多次請(qǐng)求是屬于同一個(gè)會(huì)話。比如:1、2、3這三個(gè)請(qǐng)求都是屬于同一個(gè)會(huì)話。當(dāng)我們關(guān)閉瀏覽器之后,這次會(huì)話就結(jié)束了。而如果我們是直接把web服務(wù)器關(guān)了,那么所有的會(huì)話就都結(jié)束了。

知道了會(huì)話的概念了,接下來我們?cè)賮砹私庀聲?huì)話跟蹤。

會(huì)話跟蹤:一種維護(hù)瀏覽器狀態(tài)的方法,服務(wù)器需要識(shí)別多次請(qǐng)求是否來自于同一瀏覽器,以便在同一次會(huì)話的多次請(qǐng)求間共享數(shù)據(jù)。

服務(wù)器會(huì)接收很多的請(qǐng)求,但是服務(wù)器是需要識(shí)別出這些請(qǐng)求是不是同一個(gè)瀏覽器發(fā)出來的。比如:1和2這兩個(gè)請(qǐng)求是不是同一個(gè)瀏覽器發(fā)出來的,3和5這兩個(gè)請(qǐng)求不是同一個(gè)瀏覽器發(fā)出來的。如果是同一個(gè)瀏覽器發(fā)出來的,就說明是同一個(gè)會(huì)話。如果是不同的瀏覽器發(fā)出來的,就說明是不同的會(huì)話。而識(shí)別多次請(qǐng)求是否來自于同一瀏覽器的過程,我們就稱為會(huì)話跟蹤。

我們使用會(huì)話跟蹤技術(shù)就是要完成在同一個(gè)會(huì)話中,多個(gè)請(qǐng)求之間進(jìn)行共享數(shù)據(jù)。

為什么要共享數(shù)據(jù)呢?

由于HTTP是無狀態(tài)協(xié)議,在后面請(qǐng)求中怎么拿到前一次請(qǐng)求生成的數(shù)據(jù)呢?此時(shí)就需要在一次會(huì)話的多次請(qǐng)求之間進(jìn)行數(shù)據(jù)共享

會(huì)話跟蹤技術(shù)有兩種:

  1. Cookie(客戶端會(huì)話跟蹤技術(shù))

    • 數(shù)據(jù)存儲(chǔ)在客戶端瀏覽器當(dāng)中

  2. Session(服務(wù)端會(huì)話跟蹤技術(shù))

    • 數(shù)據(jù)存儲(chǔ)在儲(chǔ)在服務(wù)端

  3. 令牌技術(shù)

1.2.1 會(huì)話跟蹤方案

上面我們介紹了什么是會(huì)話,什么是會(huì)話跟蹤,并且也提到了會(huì)話跟蹤 3 種常見的技術(shù)方案。接下來,我們就來對(duì)比一下這 3 種會(huì)話跟蹤的技術(shù)方案,來看一下具體的實(shí)現(xiàn)思路,以及它們之間的優(yōu)缺點(diǎn)。

1.2.1.1 方案一 - Cookie

cookie 是客戶端會(huì)話跟蹤技術(shù),它是存儲(chǔ)在客戶端瀏覽器的,我們使用 cookie 來跟蹤會(huì)話,我們就可以在瀏覽器第一次發(fā)起請(qǐng)求來請(qǐng)求服務(wù)器的時(shí)候,我們?cè)诜?wù)器端來設(shè)置一個(gè)cookie。

比如第一次請(qǐng)求了登錄接口,登錄接口執(zhí)行完成之后,我們就可以設(shè)置一個(gè)cookie,在 cookie 當(dāng)中我們就可以來存儲(chǔ)用戶相關(guān)的一些數(shù)據(jù)信息。比如我可以在 cookie 當(dāng)中來存儲(chǔ)當(dāng)前登錄用戶的用戶名,用戶的ID。

服務(wù)器端在給客戶端在響應(yīng)數(shù)據(jù)的時(shí)候,會(huì)自動(dòng)的將 cookie 響應(yīng)給瀏覽器,瀏覽器接收到響應(yīng)回來的 cookie 之后,會(huì)自動(dòng)的將 cookie 的值存儲(chǔ)在瀏覽器本地。接下來在后續(xù)的每一次請(qǐng)求當(dāng)中,都會(huì)將瀏覽器本地所存儲(chǔ)的 cookie 自動(dòng)地?cái)y帶到服務(wù)端。

接下來在服務(wù)端我們就可以獲取到 cookie 的值。我們可以去判斷一下這個(gè) cookie 的值是否存在,如果不存在這個(gè)cookie,就說明客戶端之前是沒有訪問登錄接口的;如果存在 cookie 的值,就說明客戶端之前已經(jīng)登錄完成了。這樣我們就可以基于 cookie 在同一次會(huì)話的不同請(qǐng)求之間來共享數(shù)據(jù)。

我剛才在介紹流程的時(shí)候,用了 3 個(gè)自動(dòng):

  • 服務(wù)器會(huì) 自動(dòng) 的將 cookie 響應(yīng)給瀏覽器。

  • 瀏覽器接收到響應(yīng)回來的數(shù)據(jù)之后,會(huì) 自動(dòng) 的將 cookie 存儲(chǔ)在瀏覽器本地。

  • 在后續(xù)的請(qǐng)求當(dāng)中,瀏覽器會(huì) 自動(dòng) 的將 cookie 攜帶到服務(wù)器端。

為什么這一切都是自動(dòng)化進(jìn)行的?

是因?yàn)?cookie 它是 HTP 協(xié)議當(dāng)中所支持的技術(shù),而各大瀏覽器廠商都支持了這一標(biāo)準(zhǔn)。在 HTTP 協(xié)議官方給我們提供了一個(gè)響應(yīng)頭和請(qǐng)求頭:

  • 響應(yīng)頭 Set-Cookie :設(shè)置Cookie數(shù)據(jù)的

  • 請(qǐng)求頭 Cookie:攜帶Cookie數(shù)據(jù)的

代碼測(cè)試

@Slf4j
@RestController
public class SessionController {

? ? //設(shè)置Cookie
? ? @GetMapping("/c1")
? ? public Result cookie1(HttpServletResponse response){
? ? ? ? response.addCookie(new Cookie("login_username","itheima")); //設(shè)置Cookie/響應(yīng)Cookie
? ? ? ? return 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();
? ? }
} ? ?

A. 訪問c1接口,設(shè)置Cookie,http://localhost:8080/c1

我們可以看到,設(shè)置的cookie,通過響應(yīng)頭Set-Cookie響應(yīng)給瀏覽器,并且瀏覽器會(huì)將Cookie,存儲(chǔ)在瀏覽器端。

B. 訪問c2接口 http://localhost:8080/c2,此時(shí)瀏覽器會(huì)自動(dòng)的將Cookie攜帶到服務(wù)端,是通過請(qǐng)求頭Cookie,攜帶的。

優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn):HTTP協(xié)議中支持的技術(shù)(像Set-Cookie 響應(yīng)頭的解析以及 Cookie 請(qǐng)求頭數(shù)據(jù)的攜帶,都是瀏覽器自動(dòng)進(jìn)行的,是無需我們手動(dòng)操作的)

  • 缺點(diǎn):

    • 移動(dòng)端APP(Android、IOS)中無法使用Cookie

    • 不安全,用戶可以自己禁用Cookie

    • Cookie不能跨域

1.2.1.2 方案二 - Session

前面介紹的時(shí)候,我們提到Session,它是服務(wù)器端會(huì)話跟蹤技術(shù),所以它是存儲(chǔ)在服務(wù)器端的。而 Session 的底層其實(shí)就是基于我們剛才所介紹的 Cookie 來實(shí)現(xiàn)的。

獲取Session

如果我們現(xiàn)在要基于 Session 來進(jìn)行會(huì)話跟蹤,瀏覽器在第一次請(qǐng)求服務(wù)器的時(shí)候,我們就可以直接在服務(wù)器當(dāng)中來獲取到會(huì)話對(duì)象Session。如果是第一次請(qǐng)求Session ,會(huì)話對(duì)象是不存在的,這個(gè)時(shí)候服務(wù)器會(huì)自動(dòng)的創(chuàng)建一個(gè)會(huì)話對(duì)象Session 。而每一個(gè)會(huì)話對(duì)象Session ,它都有一個(gè)ID(示意圖中Session后面括號(hào)中的1,就表示ID),我們稱之為 Session 的ID。

響應(yīng)Cookie (JSESSIONID)

接下來,服務(wù)器端在給瀏覽器響應(yīng)數(shù)據(jù)的時(shí)候,它會(huì)將 Session 的 ID 通過 Cookie 響應(yīng)給瀏覽器。其實(shí)在響應(yīng)頭當(dāng)中增加了一個(gè) Set-Cookie 響應(yīng)頭。這個(gè) Set-Cookie 響應(yīng)頭對(duì)應(yīng)的值是不是cookie? cookie 的名字是固定的 JSESSIONID 代表的服務(wù)器端會(huì)話對(duì)象 Session 的 ID。瀏覽器會(huì)自動(dòng)識(shí)別這個(gè)響應(yīng)頭,然后自動(dòng)將Cookie存儲(chǔ)在瀏覽器本地。

查找Session

接下來,在后續(xù)的每一次請(qǐng)求當(dāng)中,都會(huì)將 Cookie 的數(shù)據(jù)獲取出來,并且攜帶到服務(wù)端。接下來服務(wù)器拿到JSESSIONID這個(gè) Cookie 的值,也就是 Session 的ID。拿到 ID 之后,就會(huì)從眾多的 Session 當(dāng)中來找到當(dāng)前請(qǐng)求對(duì)應(yīng)的會(huì)話對(duì)象Session。

這樣我們是不是就可以通過 Session 會(huì)話對(duì)象在同一次會(huì)話的多次請(qǐng)求之間來共享數(shù)據(jù)了?好,這就是基于 Session 進(jìn)行會(huì)話跟蹤的流程。

代碼測(cè)試

@Slf4j
@RestController
public class SessionController {

? ? @GetMapping("/s1")
? ? public Result session1(HttpSession session){
? ? ? ? log.info("HttpSession-s1: {}", session.hashCode());

? ? ? ? session.setAttribute("loginUser", "tom"); //往session中存儲(chǔ)數(shù)據(jù)
? ? ? ? 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中獲取數(shù)據(jù)
? ? ? ? log.info("loginUser: {}", loginUser);
? ? ? ? return Result.success(loginUser);
? ? }
}

A. 訪問 s1 接口,http://localhost:8080/s1

請(qǐng)求完成之后,在響應(yīng)頭中,就會(huì)看到有一個(gè)Set-Cookie的響應(yīng)頭,里面響應(yīng)回來了一個(gè)Cookie,就是JSESSIONID,這個(gè)就是服務(wù)端會(huì)話對(duì)象 Session 的ID。

接下來,在后續(xù)的每次請(qǐng)求時(shí),都會(huì)將Cookie的值,攜帶到服務(wù)端,那服務(wù)端呢,接收到Cookie之后,會(huì)自動(dòng)的根據(jù)JSESSIONID的值,找到對(duì)應(yīng)的會(huì)話對(duì)象Session。

那經(jīng)過這兩步測(cè)試,大家也會(huì)看到,在控制臺(tái)中輸出如下日志:

兩次請(qǐng)求,獲取到的Session會(huì)話對(duì)象的hashcode是一樣的,就說明是同一個(gè)會(huì)話對(duì)象。而且,第一次請(qǐng)求時(shí),往Session會(huì)話對(duì)象中存儲(chǔ)的值,第二次請(qǐng)求時(shí),也獲取到了。 那這樣,我們就可以通過Session會(huì)話對(duì)象,在同一個(gè)會(huì)話的多次請(qǐng)求之間來進(jìn)行數(shù)據(jù)共享了。

優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn):Session是存儲(chǔ)在服務(wù)端的,安全

  • 缺點(diǎn):

    • 服務(wù)器集群環(huán)境下無法直接使用Session

    • 移動(dòng)端APP(Android、IOS)中無法使用Cookie

    • 用戶可以自己禁用Cookie

    • Cookie不能跨域

1.2.1.3 方案三 - 令牌技術(shù)

這里我們所提到的令牌,其實(shí)它就是一個(gè)用戶身份的標(biāo)識(shí),看似很高大上,很神秘,其實(shí)本質(zhì)就是一個(gè)字符串。

如果通過令牌技術(shù)來跟蹤會(huì)話,我們就可以在瀏覽器發(fā)起請(qǐng)求。在請(qǐng)求登錄接口的時(shí)候,如果登錄成功,我就可以生成一個(gè)令牌,令牌就是用戶的合法身份憑證。接下來我在響應(yīng)數(shù)據(jù)的時(shí)候,我就可以直接將令牌響應(yīng)給前端。

接下來我們?cè)谇岸顺绦虍?dāng)中接收到令牌之后,就需要將這個(gè)令牌存儲(chǔ)起來。這個(gè)存儲(chǔ)可以存儲(chǔ)在 cookie 當(dāng)中,也可以存儲(chǔ)在其他的存儲(chǔ)空間(比如:localStorage)當(dāng)中。

接下來,在后續(xù)的每一次請(qǐng)求當(dāng)中,都需要將令牌攜帶到服務(wù)端。攜帶到服務(wù)端之后,接下來我們就需要來校驗(yàn)令牌的有效性。如果令牌是有效的,就說明用戶已經(jīng)執(zhí)行了登錄操作,如果令牌是無效的,就說明用戶之前并未執(zhí)行登錄操作。

此時(shí),如果是在同一次會(huì)話的多次請(qǐng)求之間,我們想共享數(shù)據(jù),我們就可以將共享的數(shù)據(jù)存儲(chǔ)在令牌當(dāng)中就可以了。

優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn):

    • 支持PC端、移動(dòng)端

    • 解決集群環(huán)境下的認(rèn)證問題

    • 減輕服務(wù)器的存儲(chǔ)壓力(無需在服務(wù)器端存儲(chǔ))

  • 缺點(diǎn):需要自己實(shí)現(xiàn)(包括令牌的生成、令牌的傳遞、令牌的校驗(yàn))

針對(duì)于這三種方案,現(xiàn)在企業(yè)開發(fā)當(dāng)中使用的最多的就是第三種令牌技術(shù)進(jìn)行會(huì)話跟蹤。而前面的這兩種傳統(tǒng)的方案,現(xiàn)在企業(yè)項(xiàng)目開發(fā)當(dāng)中已經(jīng)很少使用了。所以在我們的課程當(dāng)中,我們也將會(huì)采用令牌技術(shù)來解決案例項(xiàng)目當(dāng)中的會(huì)話跟蹤問題。

2.?JWT令牌

前面我們介紹了基于令牌技術(shù)來實(shí)現(xiàn)會(huì)話追蹤。這里所提到的令牌就是用戶身份的標(biāo)識(shí),其本質(zhì)就是一個(gè)字符串。令牌的形式有很多,我們使用的是功能強(qiáng)大的 JWT令牌。

2.1 介紹

JWT全稱:JSON Web Token (官網(wǎng):JSON Web Tokens - jwt.io)

  • 定義了一種簡(jiǎn)潔的、自包含的格式,用于在通信雙方以json數(shù)據(jù)格式安全的傳輸信息。由于數(shù)字簽名的存在,這些信息是可靠的。

    簡(jiǎn)潔:是指jwt就是一個(gè)簡(jiǎn)單的字符串??梢栽谡?qǐng)求參數(shù)或者是請(qǐng)求頭當(dāng)中直接傳遞。

    自包含:指的是jwt令牌,看似是一個(gè)隨機(jī)的字符串,但是我們是可以根據(jù)自身的需求在jwt令牌中存儲(chǔ)自定義的數(shù)據(jù)內(nèi)容。如:可以直接在jwt令牌中存儲(chǔ)用戶的相關(guān)信息。

    簡(jiǎn)單來講,jwt就是將原始的json數(shù)據(jù)格式進(jìn)行了安全的封裝,這樣就可以直接基于jwt在通信雙方安全的進(jìn)行信息傳輸了。

JWT的組成: (JWT令牌由三個(gè)部分組成,三個(gè)部分之間使用英文的點(diǎn)來分割)

  • 第一部分:Header(頭), 記錄令牌類型、簽名算法等。 例如:{"alg":"HS256","type":"JWT"}

  • 第二部分:Payload(有效載荷),攜帶一些自定義信息、默認(rèn)信息等。 例如:{"id":"1","username":"Tom"}

  • 第三部分:Signature(簽名),防止Token被篡改、確保安全性。將header、payload,并加入指定秘鑰,通過指定簽名算法計(jì)算而來。

    簽名的目的就是為了防jwt令牌被篡改,而正是因?yàn)閖wt令牌最后一個(gè)部分?jǐn)?shù)字簽名的存在,所以整個(gè)jwt 令牌是非常安全可靠的。一旦jwt令牌當(dāng)中任何一個(gè)部分、任何一個(gè)字符被篡改了,整個(gè)令牌在校驗(yàn)的時(shí)候都會(huì)失敗,所以它是非常安全可靠的。

JWT是如何將原始的JSON格式數(shù)據(jù),轉(zhuǎn)變?yōu)樽址哪?#xff1f;

其實(shí)在生成JWT令牌時(shí),會(huì)對(duì)JSON格式的數(shù)據(jù)進(jìn)行一次編碼:進(jìn)行base64編碼

Base64:是一種基于64個(gè)可打印的字符來表示二進(jìn)制數(shù)據(jù)的編碼方式。既然能編碼,那也就意味著也能解碼。所使用的64個(gè)字符分別是A到Z、a到z、 0- 9,一個(gè)加號(hào),一個(gè)斜杠,加起來就是64個(gè)字符。任何數(shù)據(jù)經(jīng)過base64編碼之后,最終就會(huì)通過這64個(gè)字符來表示。當(dāng)然還有一個(gè)符號(hào),那就是等號(hào)。等號(hào)它是一個(gè)補(bǔ)位的符號(hào)

需要注意的是Base64是編碼方式,而不是加密方式。

JWT令牌最典型的應(yīng)用場(chǎng)景就是登錄認(rèn)證:

  1. 在瀏覽器發(fā)起請(qǐng)求來執(zhí)行登錄操作,此時(shí)會(huì)訪問登錄的接口,如果登錄成功之后,我們需要生成一個(gè)jwt令牌,將生成的 jwt令牌返回給前端。

  2. 前端拿到j(luò)wt令牌之后,會(huì)將jwt令牌存儲(chǔ)起來。在后續(xù)的每一次請(qǐng)求中都會(huì)將jwt令牌攜帶到服務(wù)端。

  3. 服務(wù)端統(tǒng)一攔截請(qǐng)求之后,先來判斷一下這次請(qǐng)求有沒有把令牌帶過來,如果沒有帶過來,直接拒絕訪問,如果帶過來了,還要校驗(yàn)一下令牌是否是有效。如果有效,就直接放行進(jìn)行請(qǐng)求的處理。

在JWT登錄認(rèn)證的場(chǎng)景中我們發(fā)現(xiàn),整個(gè)流程當(dāng)中涉及到兩步操作:

  1. 在登錄成功之后,要生成令牌。

  2. 每一次請(qǐng)求當(dāng)中,要接收令牌并對(duì)令牌進(jìn)行校驗(yàn)。

稍后我們?cè)賮韺W(xué)習(xí)如何來生成jwt令牌,以及如何來校驗(yàn)jwt令牌。

2.2 生成和校驗(yàn)

簡(jiǎn)單介紹了JWT令牌以及JWT令牌的組成之后,接下來我們就來學(xué)習(xí)基于Java代碼如何生成和校驗(yàn)JWT令牌。

首先我們先來實(shí)現(xiàn)JWT令牌的生成。要想使用JWT令牌,需要先引入JWT的依賴:

<!-- JWT依賴-->
<dependency>
? ? <groupId>io.jsonwebtoken</groupId>
? ? <artifactId>jjwt</artifactId>
? ? <version>0.9.1</version>
</dependency>

在引入完JWT來賴后,就可以調(diào)用工具包中提供的API來完成JWT令牌的生成和校驗(yàn)

工具類:Jwts

生成JWT代碼實(shí)現(xiàn):

@Test
public void genJwt(){
? ? Map<String,Object> claims = new HashMap<>();
? ? claims.put("id",1);
? ? claims.put("username","Tom");
? ??
? ? String jwt = Jwts.builder()
? ? ? ? .setClaims(claims) //自定義內(nèi)容(載荷) ? ? ? ? ?
? ? ? ? .signWith(SignatureAlgorithm.HS256, "xxx") //簽名算法? ? xxx是密鑰? ? ?
? ? ? ? .setExpiration(new Date(System.currentTimeMillis() + 24*3600*1000)) //有效期 ??
? ? ? ? .compact();
? ??
? ? System.out.println(jwt);
}

運(yùn)行測(cè)試方法:

eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjcyNzI5NzMwfQ.fHi0Ub8npbyt71UqLXDdLyipptLgxBUg_mSuGJtXtBk

輸出的結(jié)果就是生成的JWT令牌,,通過英文的點(diǎn)分割對(duì)三個(gè)部分進(jìn)行分割,我們可以將生成的令牌復(fù)制一下,然后打開JWT的官網(wǎng),將生成的令牌直接放在Encoded位置,此時(shí)就會(huì)自動(dòng)的將令牌解析出來。

第一部分解析出來,看到JSON格式的原始數(shù)據(jù),所使用的簽名算法為HS256。

第二個(gè)部分是我們自定義的數(shù)據(jù),之前我們自定義的數(shù)據(jù)就是id,還有一個(gè)exp代表的是我們所設(shè)置的過期時(shí)間。

由于前兩個(gè)部分是base64編碼,所以是可以直接解碼出來。但最后一個(gè)部分并不是base64編碼,是經(jīng)過簽名算法計(jì)算出來的,所以最后一個(gè)部分是不會(huì)解析的。

實(shí)現(xiàn)了JWT令牌的生成,下面我們接著使用Java代碼來校驗(yàn)JWT令牌(解析生成的令牌):

@Test
public void parseJwt(){
? ? Claims claims = Jwts.parser()
? ? ? ? .setSigningKey("xxx")//指定簽名密鑰(必須保證和生成令牌時(shí)使用相同的簽名密鑰) ?
?? ? ? ?.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjcyNzI5NzMwfQ.fHi0Ub8npbyt71UqLXDdLyipptLgxBUg_mSuGJtXtBk")
? ? ? ? .getBody();

? ? System.out.println(claims);
}

運(yùn)行測(cè)試方法:

{id=1, exp=1672729730}

令牌解析后,我們可以看到id和過期時(shí)間,如果在解析的過程當(dāng)中沒有報(bào)錯(cuò),就說明解析成功了。

下面我們做一個(gè)測(cè)試:把令牌header中的數(shù)字9變?yōu)?,運(yùn)行測(cè)試方法后發(fā)現(xiàn)報(bào)錯(cuò):

原h(huán)eader: eyJhbGciOiJIUzI1NiJ9

修改為: eyJhbGciOiJIUzI1NiJ8

結(jié)論:篡改令牌中的任何一個(gè)字符,在對(duì)令牌進(jìn)行解析時(shí)都會(huì)報(bào)錯(cuò),所以JWT令牌是非常安全可靠的。

在springboot開發(fā)中我可以將jwt寫成一個(gè)工具類,方便使用

JWT工具類

public class JwtUtils {

? ? private static String signKey = "xxx";//簽名密鑰
? ? private static Long expire = 43200000L; //有效時(shí)間

? ? /**
? ? ?* 生成JWT令牌
? ? ?* @param claims JWT第二部分負(fù)載 payload 中存儲(chǔ)的內(nèi)容
? ? ?* @return
? ? ?*/
? ? public static String generateJwt(Map<String, Object> claims){
? ? ? ? String jwt = Jwts.builder()
? ? ? ? ? ? ? ? .addClaims(claims)//自定義信息(有效載荷)
? ? ? ? ? ? ? ? .signWith(SignatureAlgorithm.HS256, signKey)//簽名算法(頭部)
? ? ? ? ? ? ? ? .setExpiration(new Date(System.currentTimeMillis() + expire))//過期時(shí)間
? ? ? ? ? ? ? ? .compact();
? ? ? ? return jwt;
? ? }

? ? /**
? ? ?* 解析JWT令牌
? ? ?* @param jwt JWT令牌
? ? ?* @return JWT第二部分負(fù)載 payload 中存儲(chǔ)的內(nèi)容
? ? ?*/
? ? public static Claims parseJWT(String jwt){
? ? ? ? Claims claims = Jwts.parser()
? ? ? ? ? ? ? ? .setSigningKey(signKey)//指定簽名密鑰
? ? ? ? ? ? ? ? .parseClaimsJws(jwt)//指定令牌Token
? ? ? ? ? ? ? ? .getBody();
? ? ? ? return claims;
? ? }
}

3. 過濾器Filter

剛才通過瀏覽器的開發(fā)者工具,我們可以看到在后續(xù)的請(qǐng)求當(dāng)中,都會(huì)在請(qǐng)求頭中攜帶JWT令牌到服務(wù)端,而服務(wù)端需要統(tǒng)一攔截所有的請(qǐng)求,從而判斷是否攜帶的有合法的JWT令牌。 那怎么樣來統(tǒng)一攔截到所有的請(qǐng)求校驗(yàn)令牌的有效性呢?這里我們會(huì)學(xué)習(xí)兩種解決方案:

  1. Filter過濾器

  2. Interceptor攔截器

我們首先來學(xué)習(xí)過濾器Filter。

3.1 快速入門

什么是Filter?

  • Filter表示過濾器,是 JavaWeb三大組件(Servlet、Filter、Listener)之一。

  • 過濾器可以把對(duì)資源的請(qǐng)求攔截下來,從而實(shí)現(xiàn)一些特殊的功能

    • 使用了過濾器之后,要想訪問web服務(wù)器上的資源,必須先經(jīng)過濾器,過濾器處理完畢之后,才可以訪問對(duì)應(yīng)的資源。

  • 過濾器一般完成一些通用的操作,比如:登錄校驗(yàn)、統(tǒng)一編碼處理、敏感字符處理等。

下面我們通過Filter快速入門程序掌握過濾器的基本使用操作:

  • 第1步,定義過濾器 :1.定義一個(gè)類,實(shí)現(xiàn) Filter 接口,并重寫其所有方法。

  • 第2步,配置過濾器:Filter類上加 @WebFilter 注解,配置攔截資源的路徑。引導(dǎo)類上加 @ServletComponentScan 開啟Servlet組件支持。

定義過濾器

@WebFilter(urlPatterns = "/*") //配置過濾器要攔截的請(qǐng)求路徑( /* 表示攔截瀏覽器的所有請(qǐng)求 )
public class DemoFilter implements Filter {?//定義一個(gè)類,實(shí)現(xiàn)一個(gè)標(biāo)準(zhǔn)的Filter過濾器的接口
? ? @Override //初始化方法, 只調(diào)用一次
? ? public void init(FilterConfig filterConfig) throws ServletException {
? ? ? ? System.out.println("init 初始化方法執(zhí)行了");
? ? }

? ? @Override //攔截到請(qǐng)求之后調(diào)用, 調(diào)用多次
? ? public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
? ? ? ? System.out.println("Demo 攔截到了請(qǐng)求...放行前邏輯");
? ? ? ? //放行
? ? ? ? chain.doFilter(request,response);
? ? }

? ? @Override //銷毀方法, 只調(diào)用一次
? ? public void destroy() {
? ? ? ? System.out.println("destroy 銷毀方法執(zhí)行了");
? ? }
}

  • init方法:過濾器的初始化方法。在web服務(wù)器啟動(dòng)的時(shí)候會(huì)自動(dòng)的創(chuàng)建Filter過濾器對(duì)象,在創(chuàng)建過濾器對(duì)象的時(shí)候會(huì)自動(dòng)調(diào)用init初始化方法,這個(gè)方法只會(huì)被調(diào)用一次。

  • doFilter方法:這個(gè)方法是在每一次攔截到請(qǐng)求之后都會(huì)被調(diào)用,所以這個(gè)方法是會(huì)被調(diào)用多次的,每攔截到一次請(qǐng)求就會(huì)調(diào)用一次doFilter()方法。

  • destroy方法: 是銷毀的方法。當(dāng)我們關(guān)閉服務(wù)器的時(shí)候,它會(huì)自動(dòng)的調(diào)用銷毀方法destroy,而這個(gè)銷毀方法也只會(huì)被調(diào)用一次。

在定義完Filter之后,Filter其實(shí)并不會(huì)生效,還需要完成Filter的配置,Filter的配置非常簡(jiǎn)單,只需要在Filter類上添加一個(gè)注解:@WebFilter,并指定屬性u(píng)rlPatterns,通過這個(gè)屬性指定過濾器要攔截哪些請(qǐng)求

當(dāng)我們?cè)贔ilter類上面加了@WebFilter注解之后,接下來我們還需要在啟動(dòng)類上面加上一個(gè)注解@ServletComponentScan,通過這個(gè)@ServletComponentScan注解來開啟SpringBoot項(xiàng)目對(duì)于Servlet組件的支持。

@ServletComponentScan
@SpringBootApplication
public class TliasWebManagementApplication {

? ? public static void main(String[] args) {
? ? ? ? SpringApplication.run(TliasWebManagementApplication.class, args);
? ? }

}

重新啟動(dòng)服務(wù),打開瀏覽器,執(zhí)行部門管理的請(qǐng)求,可以看到控制臺(tái)輸出了過濾器中的內(nèi)容:

注意事項(xiàng):

在過濾器Filter中,如果不執(zhí)行放行操作,將無法訪問后面的資源。 放行操作:chain.doFilter(request, response);

3.2 Filter詳解

Filter過濾器的快速入門程序我們已經(jīng)完成了,接下來我們就要詳細(xì)的介紹一下過濾器Filter在使用中的一些細(xì)節(jié)。主要介紹以下3個(gè)方面的細(xì)節(jié):

  1. 過濾器的執(zhí)行流程

  2. 過濾器的攔截路徑配置

  3. 過濾器鏈

3.2.1 執(zhí)行流程

首先我們先來看下過濾器的執(zhí)行流程:

過濾器當(dāng)中我們攔截到了請(qǐng)求之后,如果希望繼續(xù)訪問后面的web資源,就要執(zhí)行放行操作,放行就是調(diào)用 FilterChain對(duì)象當(dāng)中的doFilter()方法,在調(diào)用doFilter()這個(gè)方法之前所編寫的代碼屬于放行之前的邏輯。

在放行后訪問完 web 資源之后還會(huì)回到過濾器當(dāng)中,回到過濾器之后如有需求還可以執(zhí)行放行之后的邏輯,放行之后的邏輯我們寫在doFilter()這行代碼之后。

3.2.2 攔截路徑

執(zhí)行流程我們搞清楚之后,接下來再來介紹一下過濾器的攔截路徑,Filter可以根據(jù)需求,配置不同的攔截資源路徑:

攔截路徑urlPatterns值含義
攔截具體路徑/login只有訪問 /login 路徑時(shí),才會(huì)被攔截
目錄攔截/emps/*訪問/emps下的所有資源,都會(huì)被攔截
攔截所有/*訪問所有資源,都會(huì)被攔截

下面我們來測(cè)試"攔截具體路徑":

@WebFilter(urlPatterns = "/login") ?//攔截/login具體路徑
public class DemoFilter implements Filter {
? ? @Override
? ? public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
? ? ? ? System.out.println("DemoFilter ? 放行前邏輯.....");

? ? ? ? //放行請(qǐng)求
? ? ? ? filterChain.doFilter(servletRequest,servletResponse);

? ? ? ? System.out.println("DemoFilter ? 放行后邏輯.....");
? ? }


? ? @Override
? ? public void init(FilterConfig filterConfig) throws ServletException {
? ? ? ? Filter.super.init(filterConfig);
? ? }

? ? @Override
? ? public void destroy() {
? ? ? ? Filter.super.destroy();
? ? }
}

訪問登錄請(qǐng)求/login,發(fā)現(xiàn)過濾器攔截請(qǐng)求

3.2.3 過濾器鏈

最后我們?cè)趤斫榻B下過濾器鏈,什么是過濾器鏈呢?所謂過濾器鏈指的是在一個(gè)web應(yīng)用程序當(dāng)中,可以配置多個(gè)過濾器,多個(gè)過濾器就形成了一個(gè)過濾器鏈。

比如:在我們web服務(wù)器當(dāng)中,定義了兩個(gè)過濾器,這兩個(gè)過濾器就形成了一個(gè)過濾器鏈。

而這個(gè)鏈上的過濾器在執(zhí)行的時(shí)候會(huì)一個(gè)一個(gè)的執(zhí)行,會(huì)先執(zhí)行第一個(gè)Filter,放行之后再來執(zhí)行第二個(gè)Filter,如果執(zhí)行到了最后一個(gè)過濾器放行之后,才會(huì)訪問對(duì)應(yīng)的web資源。

訪問完web資源之后,按照我們剛才所介紹的過濾器的執(zhí)行流程,還會(huì)回到過濾器當(dāng)中來執(zhí)行過濾器放行后的邏輯,而在執(zhí)行放行后的邏輯的時(shí)候,順序是反著的。

先要執(zhí)行過濾器2放行之后的邏輯,再來執(zhí)行過濾器1放行之后的邏輯,最后在給瀏覽器響應(yīng)數(shù)據(jù)。

以上就是當(dāng)我們?cè)趙eb應(yīng)用當(dāng)中配置了多個(gè)過濾器,形成了這樣一個(gè)過濾器鏈以及過濾器鏈的執(zhí)行順序。下面我們通過idea來驗(yàn)證下過濾器鏈。

驗(yàn)證步驟:

  1. 在filter包下再來新建一個(gè)Filter過濾器類:AbcFilter

  2. 在AbcFilter過濾器中編寫放行前和放行后邏輯

  3. 配置AbcFilter過濾器攔截請(qǐng)求路徑為:/*

  4. 重啟SpringBoot服務(wù),查看DemoFilter、AbcFilter的執(zhí)行日志

AbcFilter過濾器

DemoFilter過濾器

打開瀏覽器訪問接口:

通過控制臺(tái)日志的輸出,大家發(fā)現(xiàn)AbcFilter先執(zhí)行DemoFilter后執(zhí)行,這是為什么呢?

其實(shí)是和過濾器的類名有關(guān)系。以注解方式配置的Filter過濾器,它的執(zhí)行優(yōu)先級(jí)是按時(shí)過濾器類名的自動(dòng)排序確定的,類名排名越靠前,優(yōu)先級(jí)越高。

4. 攔截器Interceptor

學(xué)習(xí)完了過濾器Filter之后,接下來我們繼續(xù)學(xué)習(xí)攔截器Interseptor。

攔截器我們主要分為三個(gè)方面進(jìn)行講解:

  1. 介紹下什么是攔截器,并通過快速入門程序上手?jǐn)r截器

  2. 攔截器的使用細(xì)節(jié)

  3. 通過攔截器Interceptor完成登錄校驗(yàn)功能

我們先學(xué)習(xí)第一塊內(nèi)容:攔截器快速入門

4.1 快速入門

什么是攔截器?

  • 是一種動(dòng)態(tài)攔截方法調(diào)用的機(jī)制,類似于過濾器。

  • 攔截器是Spring框架中提供的,用來動(dòng)態(tài)攔截控制器方法的執(zhí)行。

攔截器的作用:

  • 攔截請(qǐng)求,在指定方法調(diào)用前后,根據(jù)業(yè)務(wù)需要執(zhí)行預(yù)先設(shè)定的代碼。

在攔截器當(dāng)中,我們通常也是做一些通用性的操作,比如:我們可以通過攔截器來攔截前端發(fā)起的請(qǐng)求,將登錄校驗(yàn)的邏輯全部編寫在攔截器當(dāng)中。在校驗(yàn)的過程當(dāng)中,如發(fā)現(xiàn)用戶登錄了(攜帶JWT令牌且是合法令牌),就可以直接放行,去訪問spring當(dāng)中的資源。如果校驗(yàn)時(shí)發(fā)現(xiàn)并沒有登錄或是非法令牌,就可以直接給前端響應(yīng)未登錄的錯(cuò)誤信息。

下面我們通過快速入門程序,來學(xué)習(xí)下攔截器的基本使用。攔截器的使用步驟和過濾器類似,也分為兩步:

  1. 定義攔截器

  2. 注冊(cè)配置攔截器

自定義攔截器:實(shí)現(xiàn)HandlerInterceptor接口,并重寫其所有方法

//自定義攔截器
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
? ? //目標(biāo)資源方法執(zhí)行前執(zhí)行。 返回true:放行 ? ?返回false:不放行
? ? @Override
? ? public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
? ? ? ? System.out.println("preHandle .... ");
? ? ? ??
? ? ? ? return true; //true表示放行
? ? }

? ? //目標(biāo)資源方法執(zhí)行后執(zhí)行
? ? @Override
? ? public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
? ? ? ? System.out.println("postHandle ... ");
? ? }

? ? //視圖渲染完畢后執(zhí)行,最后執(zhí)行
? ? @Override
? ? public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
? ? ? ? System.out.println("afterCompletion .... ");
? ? }
}

注意:

preHandle方法:目標(biāo)資源方法執(zhí)行前執(zhí)行。 返回true:放行 返回false:不放行

postHandle方法:目標(biāo)資源方法執(zhí)行后執(zhí)行

afterCompletion方法:視圖渲染完畢后執(zhí)行,最后執(zhí)行

注冊(cè)配置攔截器:實(shí)現(xiàn)WebMvcConfigurer接口,并重寫addInterceptors方法

@Configuration ?
public class WebConfig implements WebMvcConfigurer {

? ? //自定義的攔截器對(duì)象
? ? @Autowired
? ? private LoginCheckInterceptor loginCheckInterceptor;

? ??
? ? @Override
? ? public void addInterceptors(InterceptorRegistry registry) {
? ? ? ?//注冊(cè)自定義攔截器對(duì)象
? ? ? ? registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");//設(shè)置攔截器攔截的請(qǐng)求路徑( /** 表示攔截所有請(qǐng)求)
? ? }
}

重新啟動(dòng)SpringBoot服務(wù),結(jié)果如下所示:

接下來我們?cè)賮碜鲆粋€(gè)測(cè)試:將攔截器中返回值改為false

使用postman,再次點(diǎn)擊send發(fā)送請(qǐng)求后,沒有響應(yīng)數(shù)據(jù),說明請(qǐng)求被攔截了沒有放行

4.2?Interceptor詳解

攔截器的入門程序完成之后,接下來我們來介紹攔截器的使用細(xì)節(jié)。攔截器的使用細(xì)節(jié)我們主要介紹兩個(gè)部分:

  1. 攔截器的攔截路徑配置

  2. 攔截器的執(zhí)行流程

4.2.1?攔截路徑

首先我們先來看攔截器的攔截路徑的配置,在注冊(cè)配置攔截器的時(shí)候,我們要指定攔截器的攔截路徑,通過addPathPatterns("要攔截路徑")方法,就可以指定要攔截哪些資源。

在入門程序中我們配置的是/**,表示攔截所有資源,而在配置攔截器時(shí),不僅可以指定要攔截哪些資源,還可以指定不攔截哪些資源,只需要調(diào)用excludePathPatterns("不攔截路徑")方法,指定哪些資源不需要攔截。

@Configuration ?
public class WebConfig implements WebMvcConfigurer {

? ? //攔截器對(duì)象
? ? @Autowired
? ? private LoginCheckInterceptor loginCheckInterceptor;

? ? @Override
? ? public void addInterceptors(InterceptorRegistry registry) {
? ? ? ? //注冊(cè)自定義攔截器對(duì)象
? ? ? ? registry.addInterceptor(loginCheckInterceptor)
? ? ? ? ? ? ? ? .addPathPatterns("/**")//設(shè)置攔截器攔截的請(qǐng)求路徑( /** 表示攔截所有請(qǐng)求)
? ? ? ? ? ? ? ? .excludePathPatterns("/login");//設(shè)置不攔截的請(qǐng)求路徑
? ? }
}

在攔截器中除了可以設(shè)置/**攔截所有資源外,還有一些常見攔截路徑設(shè)置:

攔截路徑含義舉例
/*一級(jí)路徑能匹配/depts,/emps,/login,不能匹配 /depts/1
/**任意級(jí)路徑能匹配/depts,/depts/1,/depts/1/2
/depts/*/depts下的一級(jí)路徑能匹配/depts/1,不能匹配/depts/1/2,/depts
/depts/**/depts下的任意級(jí)路徑能匹配/depts,/depts/1,/depts/1/2,不能匹配/emps/1
4.2.2 執(zhí)行流程

介紹完攔截路徑的配置之后,接下來我們?cè)賮斫榻B攔截器的執(zhí)行流程。通過執(zhí)行流程,大家就能夠清晰的知道過濾器與攔截器的執(zhí)行時(shí)機(jī)。

  • 當(dāng)我們打開瀏覽器來訪問部署在web服務(wù)器當(dāng)中的web應(yīng)用時(shí),此時(shí)我們所定義的過濾器會(huì)攔截到這次請(qǐng)求。攔截到這次請(qǐng)求之后,它會(huì)先執(zhí)行放行前的邏輯,然后再執(zhí)行放行操作。而由于我們當(dāng)前是基于springboot開發(fā)的,所以放行之后是進(jìn)入到了spring的環(huán)境當(dāng)中,也就是要來訪問我們所定義的controller當(dāng)中的接口方法。

  • Tomcat并不識(shí)別所編寫的Controller程序,但是它識(shí)別Servlet程序,所以在Spring的Web環(huán)境中提供了一個(gè)非常核心的Servlet:DispatcherServlet(前端控制器),所有請(qǐng)求都會(huì)先進(jìn)行到DispatcherServlet,再將請(qǐng)求轉(zhuǎn)給Controller。

  • 當(dāng)我們定義了攔截器后,會(huì)在執(zhí)行Controller的方法之前,請(qǐng)求被攔截器攔截住。執(zhí)行preHandle()方法,這個(gè)方法執(zhí)行完成后需要返回一個(gè)布爾類型的值,如果返回true,就表示放行本次操作,才會(huì)繼續(xù)訪問controller中的方法;如果返回false,則不會(huì)放行(controller中的方法也不會(huì)執(zhí)行)。

  • 在controller當(dāng)中的方法執(zhí)行完畢之后,再回過來執(zhí)行postHandle()這個(gè)方法以及afterCompletion() 方法,然后再返回給DispatcherServlet,最終再來執(zhí)行過濾器當(dāng)中放行后的這一部分邏輯的邏輯。執(zhí)行完畢之后,最終給瀏覽器響應(yīng)數(shù)據(jù)。

過濾器和攔截器之間的區(qū)別,其實(shí)它們之間的區(qū)別主要是兩點(diǎn):

  • 接口規(guī)范不同:過濾器需要實(shí)現(xiàn)Filter接口,而攔截器需要實(shí)現(xiàn)HandlerInterceptor接口。

  • 攔截范圍不同:過濾器Filter會(huì)攔截所有的資源,而Interceptor只會(huì)攔截Spring環(huán)境中的資源。

5. 異常處理

5.1 當(dāng)前問題

當(dāng)我們的程序出現(xiàn)異常后,最終服務(wù)端給前端響應(yīng)回來的數(shù)據(jù)長(zhǎng)什么樣。

響應(yīng)回來的數(shù)據(jù)是一個(gè)JSON格式的數(shù)據(jù)。但這種JSON格式的數(shù)據(jù)還是我們開發(fā)規(guī)范當(dāng)中所提到的統(tǒng)一響應(yīng)結(jié)果Result嗎?顯然并不是。由于返回的數(shù)據(jù)不符合開發(fā)規(guī)范,所以前端并不能解析出響應(yīng)的JSON數(shù)據(jù)。

接下來我們需要思考的是出現(xiàn)異常之后,當(dāng)前案例項(xiàng)目的異常是怎么處理的?

  • 答案:沒有做任何的異常處理

當(dāng)我們沒有做任何的異常處理時(shí),我們?nèi)龑蛹軜?gòu)處理異常的方案:

  • Mapper接口在操作數(shù)據(jù)庫(kù)的時(shí)候出錯(cuò)了,此時(shí)異常會(huì)往上拋(誰調(diào)用Mapper就拋給誰),會(huì)拋給service。

  • service 中也存在異常了,會(huì)拋給controller。

  • 而在controller當(dāng)中,我們也沒有做任何的異常處理,所以最終異常會(huì)再往上拋。最終拋給框架之后,框架就會(huì)返回一個(gè)JSON格式的數(shù)據(jù),里面封裝的就是錯(cuò)誤的信息,但是框架返回的JSON格式的數(shù)據(jù)并不符合我們的開發(fā)規(guī)范。

5.2 解決方案

那么在三層構(gòu)架項(xiàng)目中,出現(xiàn)了異常,該如何處理?

  • 方案一:在所有Controller的所有方法中進(jìn)行try…catch處理

    • 缺點(diǎn):代碼臃腫(不推薦)

  • 方案二:全局異常處理器

    • 好處:簡(jiǎn)單、優(yōu)雅(推薦)

5.3 全局異常處理器

我們?cè)撛趺礃佣x全局異常處理器?

  • 定義全局異常處理器非常簡(jiǎn)單,就是定義一個(gè)類,在類上加上一個(gè)注解@RestControllerAdvice,加上這個(gè)注解就代表我們定義了一個(gè)全局異常處理器。

  • 在全局異常處理器當(dāng)中,需要定義一個(gè)方法來捕獲異常,在這個(gè)方法上需要加上注解@ExceptionHandler。通過@ExceptionHandler注解當(dāng)中的value屬性來指定我們要捕獲的是哪一類型的異常。

代碼展示:

@RestControllerAdvice
public class GlobalExceptionHandler {

? ? //處理異常
? ? @ExceptionHandler(Exception.class) //指定能夠處理的異常類型
? ? public Result ex(Exception e){
? ? ? ? e.printStackTrace();//打印堆棧中的異常信息

? ? ? ? //捕獲到異常之后,響應(yīng)一個(gè)標(biāo)準(zhǔn)的Result
? ? ? ? return Result.error("對(duì)不起,操作失敗,請(qǐng)聯(lián)系管理員");
? ? }
}

@RestControllerAdvice = @ControllerAdvice + @ResponseBody

處理異常的方法返回值會(huì)轉(zhuǎn)換為json后再響應(yīng)給前端

以上就是全局異常處理器的使用,主要涉及到兩個(gè)注解:

  • @RestControllerAdvice //表示當(dāng)前類為全局異常處理器

  • @ExceptionHandler //指定可以捕獲哪種類型的異常進(jìn)行處理

http://www.risenshineclean.com/news/53939.html

相關(guān)文章:

  • 購(gòu)買域名需要注意什么seo關(guān)鍵詞選取工具
  • 網(wǎng)站關(guān)于我們?cè)趺醋鼍W(wǎng)絡(luò)營(yíng)銷策劃需要包括哪些內(nèi)容
  • 程序開發(fā)外包平臺(tái)公司百度官網(wǎng)優(yōu)化
  • 做全球視頻網(wǎng)站賺錢嗎英文網(wǎng)站推廣
  • 公司名字大全及寓意seo排名首頁(yè)
  • iOS開發(fā) 隱私政策網(wǎng)站怎么做軟文發(fā)稿公司
  • 華藝網(wǎng)絡(luò)網(wǎng)站開發(fā)手機(jī)網(wǎng)站自助建站系統(tǒng)
  • 石家莊網(wǎng)站開發(fā)建設(shè)小程序如何推廣運(yùn)營(yíng)
  • 網(wǎng)上可以注冊(cè)公司嗎?都需要什么seo標(biāo)題優(yōu)化的方法
  • 重慶市城市建設(shè)規(guī)劃官方網(wǎng)站企業(yè)網(wǎng)站推廣方案的策劃
  • 安徽茶葉學(xué)會(huì) 網(wǎng)站建設(shè)百度收錄好的免費(fèi)網(wǎng)站
  • 武漢外貿(mào)網(wǎng)站制作百度一下移動(dòng)版首頁(yè)
  • 香港服務(wù)器的網(wǎng)站可以做競(jìng)價(jià)百度競(jìng)價(jià)調(diào)價(jià)軟件
  • 如何在eclipse上做網(wǎng)站網(wǎng)絡(luò)營(yíng)銷推廣價(jià)格
  • 怎么制作網(wǎng)站獲取ip模板網(wǎng)站免費(fèi)
  • 網(wǎng)站開發(fā)wbs實(shí)例seo關(guān)鍵詞排名優(yōu)化系統(tǒng)源碼
  • 做課件可賺錢的網(wǎng)站西安網(wǎng)站seo排名優(yōu)化
  • 什么網(wǎng)站做視頻賺錢網(wǎng)絡(luò)推廣營(yíng)銷方案100例
  • 做網(wǎng)站機(jī)構(gòu)搜狗網(wǎng)址導(dǎo)航
  • 朝陽區(qū)手機(jī)網(wǎng)站建設(shè)服務(wù)百度搜索風(fēng)云榜手機(jī)版
  • 自己這么做網(wǎng)站瀏覽器打開是2345網(wǎng)址導(dǎo)航
  • 網(wǎng)站建設(shè)江陰高端網(wǎng)站定制開發(fā)
  • 西安網(wǎng)站建設(shè)陽建seo網(wǎng)站排名優(yōu)化公司
  • zhaosf做這樣網(wǎng)站競(jìng)價(jià)托管哪家公司好
  • 鄭州網(wǎng)站開發(fā)比較好的網(wǎng)絡(luò)公司網(wǎng)絡(luò)營(yíng)銷管理
  • 如何不要錢做網(wǎng)站營(yíng)銷模式有幾種
  • 在西寧做網(wǎng)站可以嗎目前小說網(wǎng)站排名
  • 網(wǎng)站開發(fā)與維護(hù)工資多少西安百度公司官網(wǎng)
  • 做教育的網(wǎng)站有哪些指數(shù)
  • 武進(jìn)區(qū)城鄉(xiāng)建設(shè)局網(wǎng)站營(yíng)銷網(wǎng)站搭建