SpringBoot中過(guò)濾器Filter+JWT令牌實(shí)現(xiàn)登錄驗(yàn)證
登錄校驗(yàn)-Filter
分析
過(guò)濾器Filter的快速入門(mén)以及使用細(xì)節(jié)我們已經(jīng)介紹完了,接下來(lái)最后一步,我們需要使用過(guò)濾器Filter來(lái)完成案例當(dāng)中的登錄校驗(yàn)功能。

我們先來(lái)回顧下前面分析過(guò)的登錄校驗(yàn)的基本流程:
要進(jìn)入到后臺(tái)管理系統(tǒng),我們必須先完成登錄操作,此時(shí)就需要訪問(wèn)登錄接口login。
登錄成功之后,我們會(huì)在服務(wù)端生成一個(gè)JWT令牌,并且把JWT令牌返回給前端,前端會(huì)將JWT令牌存儲(chǔ)下來(lái)。
在后續(xù)的每一次請(qǐng)求當(dāng)中,都會(huì)將JWT令牌攜帶到服務(wù)端,請(qǐng)求到達(dá)服務(wù)端之后,要想去訪問(wèn)對(duì)應(yīng)的業(yè)務(wù)功能,此時(shí)我們必須先要校驗(yàn)令牌的有效性。
對(duì)于校驗(yàn)令牌的這一塊操作,我們使用登錄校驗(yàn)的過(guò)濾器,在過(guò)濾器當(dāng)中來(lái)校驗(yàn)令牌的有效性。如果令牌是無(wú)效的,就響應(yīng)一個(gè)錯(cuò)誤的信息,也不會(huì)再去放行訪問(wèn)對(duì)應(yīng)的資源了。如果令牌存在,并且它是有效的,此時(shí)就會(huì)放行去訪問(wèn)對(duì)應(yīng)的web資源,執(zhí)行相應(yīng)的業(yè)務(wù)操作。
大概清楚了在Filter過(guò)濾器的實(shí)現(xiàn)步驟了,那在正式開(kāi)發(fā)登錄校驗(yàn)過(guò)濾器之前,我們思考兩個(gè)問(wèn)題:
所有的請(qǐng)求,攔截到了之后,都需要校驗(yàn)令牌嗎?
- 答案:登錄請(qǐng)求例外
攔截到請(qǐng)求后,什么情況下才可以放行,執(zhí)行業(yè)務(wù)操作?
- 答案:有令牌,且令牌校驗(yàn)通過(guò)(合法);否則都返回未登錄錯(cuò)誤結(jié)果
具體流程
我們要完成登錄校驗(yàn),主要是利用Filter過(guò)濾器實(shí)現(xiàn),而Filter過(guò)濾器的流程步驟:

基于上面的業(yè)務(wù)流程,我們分析出具體的操作步驟:
- 獲取請(qǐng)求url
- 判斷請(qǐng)求url中是否包含login,如果包含,說(shuō)明是登錄操作,放行
- 獲取請(qǐng)求頭中的令牌(token)
- 判斷令牌是否存在,如果不存在,返回錯(cuò)誤結(jié)果(未登錄)
- 解析token,如果解析失敗,返回錯(cuò)誤結(jié)果(未登錄)
- 放行
代碼實(shí)現(xiàn)
分析清楚了以上的問(wèn)題后,我們就參照接口文檔來(lái)開(kāi)發(fā)登錄功能了,登錄接口描述如下:
請(qǐng)求參數(shù)
參數(shù)格式:application/json
參數(shù)說(shuō)明:
| 名稱(chēng) | 類(lèi)型 | 是否必須 | 備注 |
|---|---|---|---|
| username | string | 必須 | 用戶名 |
| password | string | 必須 | 密碼 |
請(qǐng)求數(shù)據(jù)樣例:
{
"username": "jinyong",
"password": "123456"
}
響應(yīng)數(shù)據(jù)
參數(shù)格式:application/json
參數(shù)說(shuō)明:
| 名稱(chēng) | 類(lèi)型 | 是否必須 | 默認(rèn)值 | 備注 | 其他信息 |
|---|---|---|---|---|---|
| code | number | 必須 | 響應(yīng)碼, 1 成功 ; 0 失敗 | ||
| msg | string | 非必須 | 提示信息 | ||
| data | string | 必須 | 返回的數(shù)據(jù) , jwt令牌 |
響應(yīng)數(shù)據(jù)樣例:
{
"code": 1,
"msg": "success",
"data": "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoi6YeR5bq4IiwiaWQiOjEsInVzZXJuYW1lIjoiamlueW9uZyIsImV4cCI6MTY2MjIwNzA0OH0.KkUc_CXJZJ8Dd063eImx4H9Ojfrr6XMJ-yVzaWCVZCo"
}
備注說(shuō)明
用戶登錄成功后,系統(tǒng)會(huì)自動(dòng)下發(fā)JWT令牌,然后在后續(xù)的每次請(qǐng)求中,都需要在請(qǐng)求頭header中攜帶到服務(wù)端,請(qǐng)求頭的名稱(chēng)為 token ,值為 登錄時(shí)下發(fā)的JWT令牌。
如果檢測(cè)到用戶未登錄,則會(huì)返回如下固定錯(cuò)誤信息:
{
"code": 0,
"msg": "NOT_LOGIN",
"data": null
}
登錄校驗(yàn)過(guò)濾器:LoginCheckFilter
@Slf4j
@WebFilter(urlPatterns = "/*") //攔截所有請(qǐng)求
public class LoginCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
//前置:強(qiáng)制轉(zhuǎn)換為http協(xié)議的請(qǐng)求對(duì)象、響應(yīng)對(duì)象 (轉(zhuǎn)換原因:要使用子類(lèi)中特有方法)
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//1.獲取請(qǐng)求url
String url = request.getRequestURL().toString();
log.info("請(qǐng)求路徑:{}", url); //請(qǐng)求路徑:http://localhost:8080/login
//2.判斷請(qǐng)求url中是否包含login,如果包含,說(shuō)明是登錄操作,放行
if(url.contains("/login")){
chain.doFilter(request, response);//放行請(qǐng)求
return;//結(jié)束當(dāng)前方法的執(zhí)行
}
//3.獲取請(qǐng)求頭中的令牌(token)
String token = request.getHeader("token");
log.info("從請(qǐng)求頭中獲取的令牌:{}",token);
//4.判斷令牌是否存在,如果不存在,返回錯(cuò)誤結(jié)果(未登錄)
if(!StringUtils.hasLength(token)){
log.info("Token不存在");
Result responseResult = Result.error("NOT_LOGIN");
//把Result對(duì)象轉(zhuǎn)換為JSON格式字符串 (fastjson是阿里巴巴提供的用于實(shí)現(xiàn)對(duì)象和json的轉(zhuǎn)換工具類(lèi))
String json = JSONObject.toJSONString(responseResult);
response.setContentType("application/json;charset=utf-8");
//響應(yīng)
response.getWriter().write(json);
return;
}
//5.解析token,如果解析失敗,返回錯(cuò)誤結(jié)果(未登錄)
try {
JwtUtils.parseJWT(token);
}catch (Exception e){
log.info("令牌解析失敗!");
Result responseResult = Result.error("NOT_LOGIN");
//把Result對(duì)象轉(zhuǎn)換為JSON格式字符串 (fastjson是阿里巴巴提供的用于實(shí)現(xiàn)對(duì)象和json的轉(zhuǎn)換工具類(lèi))
String json = JSONObject.toJSONString(responseResult);
response.setContentType("application/json;charset=utf-8");
//響應(yīng)
response.getWriter().write(json);
return;
}
//6.放行
chain.doFilter(request, response);
}
}
JWT導(dǎo)入的maven依賴(lài)
<!--JWT令牌-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
JWT的工具類(lèi)
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.Map;
public class JwtUtils {
private static String signKey = "shisan";
private static Long expire = 43200000L;
/**
* 生成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))
.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)
.getBody();
return claims;
}
}
在上述過(guò)濾器的功能實(shí)現(xiàn)中,我們使用到了一個(gè)第三方j(luò)son處理的工具包fastjson。我們要想使用,需要引入如下依賴(lài):
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
登錄校驗(yàn)的過(guò)濾器我們編寫(xiě)完成了,接下來(lái)我們就可以重新啟動(dòng)服務(wù)來(lái)做一個(gè)測(cè)試:
測(cè)試前先把之前所編寫(xiě)的測(cè)試使用的過(guò)濾器,暫時(shí)注釋掉。直接將@WebFilter注解給注釋掉即可。
測(cè)試1:未登錄是否可以訪問(wèn)部門(mén)管理頁(yè)面
首先關(guān)閉瀏覽器,重新打開(kāi)瀏覽器,在地址欄中輸入:http://localhost:9528/#/system/dept
由于用戶沒(méi)有登錄,登錄校驗(yàn)過(guò)濾器返回錯(cuò)誤信息,前端頁(yè)面根據(jù)返回的錯(cuò)誤信息結(jié)果,自動(dòng)跳轉(zhuǎn)到登錄頁(yè)面了

測(cè)試2:先進(jìn)行登錄操作,再訪問(wèn)部門(mén)管理頁(yè)面
登錄校驗(yàn)成功之后,可以正常訪問(wèn)相關(guān)業(yè)務(wù)操作頁(yè)面

總結(jié): 這個(gè)過(guò)濾器檢查請(qǐng)求的URL是否包含 “login”。如果包含,則直接允許請(qǐng)求繼續(xù)。如果不包含 “login”,則檢查JWT令牌的存在和有效性。如果令牌不存在或無(wú)效,返回一個(gè)JSON格式的 “NOT_LOGIN” 錯(cuò)誤響應(yīng)。如果令牌存在并且有效,則允許請(qǐng)求繼續(xù)。
到此這篇關(guān)于SpringBoot-過(guò)濾器Filter+JWT令牌實(shí)現(xiàn)登錄驗(yàn)證的文章就介紹到這了,更多相關(guān)SpringBoot 登錄驗(yàn)證內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
FastJson時(shí)間格式化問(wèn)題避坑經(jīng)驗(yàn)分享
這篇文章主要為大家介紹了FastJson時(shí)間格式化問(wèn)題避坑經(jīng)驗(yàn)分享,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
SpringBoot接收請(qǐng)求參數(shù)的四種方式總結(jié)
這篇文章主要給大家介紹了關(guān)于SpringBoot接收請(qǐng)求參數(shù)的四種方式,文中通過(guò)代碼以及圖文介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用SpringBoot具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09
線程池ThreadPoolExecutor使用簡(jiǎn)介與方法實(shí)例
今天小編就為大家分享一篇關(guān)于線程池ThreadPoolExecutor使用簡(jiǎn)介與方法實(shí)例,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03
spring cloud 配置中心客戶端啟動(dòng)遇到的問(wèn)題
這篇文章主要介紹了spring cloud 配置中心客戶端啟動(dòng)遇到的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
SpringBoot處理HTTP請(qǐng)求的詳細(xì)流程
這篇文章主要介紹了SpringBoot處理HTTP請(qǐng)求的詳細(xì)流程,文中通過(guò)代碼示例給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-05-05
springboot jta atomikos實(shí)現(xiàn)分布式事物管理
這篇文章主要介紹了springboot jta atomikos實(shí)現(xiàn)分布式事物管理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12
spring-boot報(bào)錯(cuò)javax.servlet.http不存在的問(wèn)題解決
當(dāng)springboot項(xiàng)目從2.7.x的升級(jí)到3.0.x的時(shí)候,會(huì)遇到j(luò)avax.servlet.http不存在,本文就來(lái)介紹一下這個(gè)問(wèn)題的解決,具有一定的參考價(jià)值,感興趣的可以了解一下2024-06-06
java后臺(tái)利用Apache poi 生成excel文檔提供前臺(tái)下載示例
本篇文章主要介紹了java后臺(tái)利用Apache poi 生成excel文檔提供前臺(tái)下載示例,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-05-05

