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í)就需要訪(fǎng)問(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ù)端之后,要想去訪(fǎng)問(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ì)再去放行訪(fǎng)問(wèn)對(duì)應(yīng)的資源了。如果令牌存在,并且它是有效的,此時(shí)就會(huì)放行去訪(fǎng)問(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 | 必須 | 用戶(hù)名 |
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ō)明
用戶(hù)登錄成功后,系統(tǒng)會(huì)自動(dòng)下發(fā)JWT令牌,然后在后續(xù)的每次請(qǐng)求中,都需要在請(qǐng)求頭header中攜帶到服務(wù)端,請(qǐng)求頭的名稱(chēng)為 token ,值為 登錄時(shí)下發(fā)的JWT令牌。
如果檢測(cè)到用戶(hù)未登錄,則會(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:未登錄是否可以訪(fǎng)問(wèn)部門(mén)管理頁(yè)面
首先關(guān)閉瀏覽器,重新打開(kāi)瀏覽器,在地址欄中輸入:http://localhost:9528/#/system/dept
由于用戶(hù)沒(méi)有登錄,登錄校驗(yàn)過(guò)濾器返回錯(cuò)誤信息,前端頁(yè)面根據(jù)返回的錯(cuò)誤信息結(jié)果,自動(dòng)跳轉(zhuǎn)到登錄頁(yè)面了
測(cè)試2:先進(jìn)行登錄操作,再訪(fǎng)問(wèn)部門(mén)管理頁(yè)面
登錄校驗(yàn)成功之后,可以正常訪(fǎng)問(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-08SpringBoot接收請(qǐng)求參數(shù)的四種方式總結(jié)
這篇文章主要給大家介紹了關(guān)于SpringBoot接收請(qǐng)求參數(shù)的四種方式,文中通過(guò)代碼以及圖文介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用SpringBoot具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09線(xiàn)程池ThreadPoolExecutor使用簡(jiǎn)介與方法實(shí)例
今天小編就為大家分享一篇關(guān)于線(xiàn)程池ThreadPoolExecutor使用簡(jiǎn)介與方法實(shí)例,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03spring cloud 配置中心客戶(hù)端啟動(dòng)遇到的問(wèn)題
這篇文章主要介紹了spring cloud 配置中心客戶(hù)端啟動(dòng)遇到的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09SpringBoot處理HTTP請(qǐng)求的詳細(xì)流程
這篇文章主要介紹了SpringBoot處理HTTP請(qǐng)求的詳細(xì)流程,文中通過(guò)代碼示例給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-05-05springboot jta atomikos實(shí)現(xiàn)分布式事物管理
這篇文章主要介紹了springboot jta atomikos實(shí)現(xiàn)分布式事物管理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12spring-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-06java后臺(tái)利用Apache poi 生成excel文檔提供前臺(tái)下載示例
本篇文章主要介紹了java后臺(tái)利用Apache poi 生成excel文檔提供前臺(tái)下載示例,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-05-05