在SpringBoot中使用jwt實(shí)現(xiàn)token身份認(rèn)證的實(shí)例代碼
- jwt (JSON Web Tokens)
jwt結(jié)構(gòu)
- 用(.)分割的三個(gè)部位組成
- 標(biāo)頭
- 有效載荷
- 簽名
xxx.xxx.xxx
標(biāo)頭
- 標(biāo)頭通常由兩部分組成:令牌的類型(JWT)和所使用的簽名算法(例如 HMAC SHA256 或 RSA
{ "alg": "HS256", "typ": "JWT" }
- 然后,對(duì)該 JSON 進(jìn)行Base64Url編碼以形成 JWT 的第一部分。
有效載荷
- 令牌的第二部分是有效負(fù)載,其中包含聲明。聲明是關(guān)于實(shí)體(通常是用戶)和附加數(shù)據(jù)的聲明。索賠分為三種類型:注冊(cè)索賠、公開索賠和私人索賠。
- 注冊(cè)聲明
- 這些是一組預(yù)定義的聲明,不是強(qiáng)制性的,而是推薦的,以提供一組有用的、可互操作的聲明。其中一些是: iss(發(fā)行者)、 exp(到期時(shí)間)、 sub(主題)、 aud(受眾)等。
- 公共聲明
- 這些可以由使用 JWT 的人隨意定義。但為了避免沖突,它們應(yīng)該在[IANA JSON Web Token Registry]中定義,或者定義為包含防沖突命名空間的 URI
- 私人聲明
- 這些是為在同意使用它們的各方之間共享信息而創(chuàng)建的自定義聲明,既不是注冊(cè)聲明也不是公開聲明。
有效負(fù)載示例
{ "sub": "1234567890", "name": "John Doe", "admin": true }
- 然后對(duì)有效負(fù)載進(jìn)行Base64Url編碼以形成 JSON Web 令牌的第二部分。
請(qǐng)注意,對(duì)于簽名令牌,此信息雖然受到防止篡改的保護(hù),但任何人都可以讀取。除非加密,否則請(qǐng)勿將秘密信息放入 JWT 的有效負(fù)載或標(biāo)頭元素中。
簽名
- 要?jiǎng)?chuàng)建簽名部分,您必須獲取編碼的標(biāo)頭、編碼的有效負(fù)載、秘密、標(biāo)頭中指定的算法,然后對(duì)其進(jìn)行簽名。
使用HMAC SHA256算法,則將通過(guò)以下方式創(chuàng)建簽名
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
簽名用于驗(yàn)證消息在傳輸過(guò)程中沒(méi)有發(fā)生更改,并且在使用私鑰簽名的令牌的情況下,它還可以驗(yàn)證 JWT 的發(fā)送者是否是其所說(shuō)的人。
將所有內(nèi)容放在一起
- 輸出是三個(gè)由點(diǎn)分隔的 Base64-URL 字符串,可以在 HTML 和 HTTP 環(huán)境中輕松傳遞,同時(shí)與基于 XML 的標(biāo)準(zhǔn)(例如 SAML)相比更加緊湊。
- 下面顯示了一個(gè) JWT,它具有先前的標(biāo)頭和有效負(fù)載編碼,并且使用密鑰進(jìn)行簽名。
- 官網(wǎng)翻譯的,先了解一點(diǎn)前置知識(shí),開整!
使用
依賴
- pom.xml文件咱們先加入jwt的依賴
JwtTokenUtils工具類
public class JwtTokenUtils { // token私鑰 private static final String TOKEN_SECRET = "jwtSECRET"; // token過(guò)期時(shí)長(zhǎng)30分鐘 private static final long EXPIRE_TIME = 1800L; // 單位為秒 /** * 生成用戶token,設(shè)置token超時(shí)時(shí)間 * * @return 生成的token */ public static String createToken(LoginVo loginVo) { Date expireDate = new Date(System.currentTimeMillis() + EXPIRE_TIME * 1000); // 創(chuàng)建 Token 的 payload Map<String, Object> map = new HashMap<>(); map.put("alg", "HS256"); map.put("typ", "JWT"); // 生成 JWT Token String token = JWT.create() .withHeader(map)// 添加頭部 .withClaim("account", loginVo.getAccount()) .withExpiresAt(expireDate) //超時(shí)設(shè)置,設(shè)置過(guò)期的日期 .withIssuedAt(new Date()) //簽發(fā)時(shí)間 .sign(Algorithm.HMAC256(TOKEN_SECRET)); //SECRET加密 return token; } /** * 檢驗(yàn)token是否正確 * @param token 需要校驗(yàn)的token * @return 校驗(yàn)是否成功 */ public static Map<String, Claim> verifyToken(String token) { DecodedJWT jwt = null; token = token.replace("Bearer ", ""); // 這里要去掉Bearer ,postman默認(rèn)會(huì)加這個(gè),導(dǎo)致匹配不上 try { //設(shè)置簽名的加密算法:HMAC256 Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); // 私鑰需要一致 JWTVerifier verifier = JWT.require(algorithm).build(); jwt = verifier.verify(token); } catch (Exception e) { // logger.error(e.getMessage()); // 還沒(méi)安裝log4j等日志依賴,先注釋 // logger.error("token解碼異常"); //解碼異常則拋出異常 return null; } return jwt.getClaims(); } }
- createToken方法主要就是創(chuàng)建token,并把賬號(hào)密碼作為自定義聲明添加到 JWT 的 payload 中,最終返回
- verifyToken方法其實(shí)就是解碼 token 并將其轉(zhuǎn)換成 DecodedJWT 對(duì)象,通過(guò)
getClaims()
方法獲取 payload 中的所有聲明信息,并以 Map 的形式返回,如果 token 不合法或者解碼過(guò)程中出現(xiàn)異常,則返回 null,校驗(yàn)的效果。
- 注意Bearer 這個(gè)玩意
JwtFilter.java
@Component @WebFilter(filterName = "JwtFilter", urlPatterns = "/*") public class JwtFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("jwt初始化方法"); } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) req; final HttpServletResponse response = (HttpServletResponse) res; response.setCharacterEncoding("UTF-8"); final String token = request.getHeader("Authorization"); String requestPath = request.getRequestURI(); // 登錄接口,直接放行 if (requestPath.contains("/user/login")) { chain.doFilter(request, response); return; } if ("OPTIONS".equals(request.getMethod())) { // 是否是 OPTIONS 請(qǐng)求 response.setStatus(HttpServletResponse.SC_OK); chain.doFilter(request, response); } else { if (token == null) { response.getWriter().write("沒(méi)有令牌"); return; } Map<String, Claim> userData = JwtTokenUtils.verifyToken(token); // 檢驗(yàn) token if (userData == null) { response.getWriter().write("令牌非法"); return; } String account = userData.get("account").asString(); // 獲取用戶名 request.setAttribute("account", account); // 設(shè)置用戶名 chain.doFilter(req, res); // 過(guò)濾成功 } } @Override public void destroy() { System.out.println("jwt銷毀方法執(zhí)行的邏輯"); } }
- 這其實(shí)就是一個(gè)過(guò)濾類,你請(qǐng)求接口前就會(huì)先到這里,執(zhí)行相對(duì)應(yīng)的邏輯。
- init: 初始化方法
- doFilter:過(guò)濾攔截方法(主要邏輯就寫在這)
- destroy:銷毀方法
注意
urlPatterns = "/*"
: 這個(gè)代表攔截所有的請(qǐng)求,我在doFilter
里面判斷了包含/login
直接放行。意思就是登錄接口直接放行,返回token
給前端,如果是別的請(qǐng)求則走對(duì)應(yīng)邏輯。@Component
:這個(gè)需要加上,這樣才能掃描到你這個(gè)類不然不生效的?;蛘吣阍趩?dòng)類加上@ServletComponentScan(basePackages = "com.yjx.boot.config")
,我的過(guò)濾器是寫在config
包下的。
LoginController
@Controller @RestController // 可處理 HTTP 請(qǐng)求的控制器,并且能夠返回?cái)?shù)據(jù)給客戶端 @RequestMapping("/user") @RequiredArgsConstructor public class LoginController { private final AdminService adminService; // 用戶登陸 @GetMapping("/login") ResultVO<Object> login(LoginVo loginVo) { Admin admin = new Admin(); admin.setAccount(loginVo.getAccount()); admin.setAdminPassword(loginVo.getAdminPassword()); // 查找是否有該管理員 Admin an = adminService.queryByAdmin(admin); if (an == null) { return ResultResponse.failure(ResultCodeEnum.INTERNAL_NOT_FOUND); } else { String token = JwtTokenUtils.createToken(loginVo); loginVo.setToken(token); an.setToken(token); return ResultResponse.success(an); } } @GetMapping("/secure/current_registrant") public String currentRegistrant(HttpServletRequest request) { System.out.println("request" + request); String id = request.getParameter("id"); String account = request.getParameter("account"); String user_password = request.getParameter("adminPassword"); return "當(dāng)前用戶信息: id=" + id + " ,account=" + account + " ,user_password=" + user_password; } }
- 代碼部分就完事了,測(cè)試一下。
測(cè)試
- 我們請(qǐng)求下/secure/current_registrant接口,此時(shí)是沒(méi)有token的。
正常,輸出了我們想要的:沒(méi)有令牌
- 我們先去請(qǐng)求/login,看看token以及用戶信息會(huì)不會(huì)如我們所想 返回給我們呢?
很好,也拿到了我們想要的,token也返回了,我們復(fù)制一下這個(gè)token,我們?cè)俅握?qǐng)求/secure/current_registrant,并把token放入請(qǐng)求頭,試試能不能成功
- 成功
我們來(lái)試試把token寫錯(cuò)一個(gè)字母會(huì)輸出什么。
我們?cè)谡?qǐng)求頭不傳入token呢
完事!測(cè)試成功!寫的并沒(méi)有很好 甚至可能有些地方都寫錯(cuò)了,慢慢完善吧
以上就是在SpringBoot中使用jwt實(shí)現(xiàn)token身份認(rèn)證的實(shí)例代碼的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot jwt實(shí)現(xiàn)token認(rèn)證的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- 基于SpringBoot整合oauth2實(shí)現(xiàn)token認(rèn)證
- springboot+jwt實(shí)現(xiàn)token登陸權(quán)限認(rèn)證的實(shí)現(xiàn)
- SpringBoot和Redis實(shí)現(xiàn)Token權(quán)限認(rèn)證的實(shí)例講解
- SpringBoot整合Sa-Token實(shí)現(xiàn)登錄認(rèn)證的示例代碼
- SpringBoot整合token實(shí)現(xiàn)登錄認(rèn)證的示例代碼
- SpringBoot使用Sa-Token實(shí)現(xiàn)登錄認(rèn)證
- SpringBoot使用Sa-Token實(shí)現(xiàn)權(quán)限認(rèn)證
- Springboot微服務(wù)分布式框架Rouyi Cloud權(quán)限認(rèn)證(登錄流程之token解析)
- Springboot 如何使用 SaToken 進(jìn)行登錄認(rèn)證、權(quán)限管理及路由規(guī)則接口攔截
- springBoot整合jwt實(shí)現(xiàn)token令牌認(rèn)證的示例代碼
相關(guān)文章
Java實(shí)現(xiàn)FIFO功能的完整代碼實(shí)踐
在軟件開發(fā)中,隊(duì)列(Queue)是一種常見的數(shù)據(jù)結(jié)構(gòu),其特點(diǎn)是先進(jìn)先出(FIFO,First In First Out),FIFO 隊(duì)列在生產(chǎn)者-消費(fèi)者模型、任務(wù)調(diào)度、緩沖區(qū)管理等場(chǎng)景中具有廣泛的應(yīng)用,本文給大家介紹了Java實(shí)現(xiàn)FIFO功能的完整代碼實(shí)踐,需要的朋友可以參考下2025-03-03SpringBoot實(shí)現(xiàn)國(guó)際化i18n詳解
國(guó)際化(Internationalization,簡(jiǎn)稱i18n)是指在軟件應(yīng)用中支持多種語(yǔ)言和文化的能力,本文將介紹如何在Spring?Boot應(yīng)用中實(shí)現(xiàn)國(guó)際化,需要的可以參考下2024-12-12SpringBoot使用iText7實(shí)現(xiàn)將HTML轉(zhuǎn)成PDF并添加頁(yè)眉頁(yè)腳水印
這篇文章主要為大家詳細(xì)介紹了SpringBoot使用iText7實(shí)現(xiàn)將HTML轉(zhuǎn)成PDF并添加頁(yè)眉頁(yè)腳水印的相關(guān)知識(shí),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-03-03淺談spring中isolation和propagation的用法
這篇文章主要介紹了淺談spring中isolation 和propagation的用法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07java long轉(zhuǎn)String +Codeforces110A案例
這篇文章主要介紹了java long轉(zhuǎn)String +Codeforces110A案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09Spring?Session(分布式Session共享)實(shí)現(xiàn)示例
這篇文章主要介紹了Spring?Session(分布式Session共享)實(shí)現(xiàn)示例,文章內(nèi)容詳細(xì),需要的朋友可以參考下2023-01-01