網站首頁 編程語言 正文
文章目錄
- 前言
- 一、JWT是什么?
- 二、使用步驟
- 1.創建項目,導入依賴,配置、引入工具類
- 2.編寫LoginController和UserController
- 3.編寫跨域攔截器和token驗證攔截器
- 4.全局攔截器配置
- 三、業務邏輯
- 四、測試
- 總結
前言
登陸功能是每個系統的最基本功能,在SSM技術棧中,登陸狀態驗證一般會使用服務端的session,但是session并沒有想象中的那么好用,經常會出現由于sessionid不一致導致的信息丟失,更好的解決方案就是使用JWT的Token生成。
一、JWT是什么?
JSON Web Token (JWT)是一個開放標準(RFC 7519),它定義了一種緊湊的、自包含的方式,用于作為JSON對象在各方之間安全地傳輸信息,該信息可以被驗證和信任,因為它是數字簽名的,常用于單點登錄。
JWT-token
JSON Web Token由三部分組成,它們之間用圓點(.)連接。這三部分分別是:頭部,載荷,簽名
- Header
頭部用于描述關于該JWT的最基本的信息,例如其類型以及簽名所用的算法等。這也可以被表示成一個JSON對象,示例:{“typ”:“JWT”,“alg”:“HS256”}
在頭部指明了簽名算法是HS256算法。 我們可以通過BASE64進行編碼/解碼:https://www.matools.com/base64/ - Payload
載荷就是存放有效信息的地方。這個名字像是特指飛機上承載的貨品,這些有效信息包含三個部分。
1.標準中注冊的聲明(建議但不強制使用):例如:sub表示jwt所面向的用戶
2.公共的聲明:可以添加任何的信息,一般添加用戶的相關信息或其他業務需要的必要信息.但不建議添加敏感信息,因為該部分在客戶端可解密.
3.私有的聲明:私有聲明是提供者和消費者所共同定義的聲明,一般不建議存放敏感信息,因為base64是對稱解密的,意味著該部分信息可以歸類為明文信息。
例如:定義一個payload:
{“sub”:“1234567890”,“name”:“John Doe”,“admin”:true}
然后將其進行base64加密,得到Jwt的第二部分:eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9 - Signature
jwt的第三部分是一個簽證信息,這個簽證信息由三部分組成:header + payload + secret
這個部分需要base64加密后的header和base64加密后的payload使用.連接組成的字符串,然后通過header中聲明的加密方式進行加鹽secret組合加密,然后就構成了jwt的第三部分。
注意:secret是保存在服務器端的,jwt的簽發生成也是在服務器端的,secret就是用來進行jwt的簽發和jwt的驗證,所以,它就是你服務端的私鑰,在任何場景都不應該流露出去。一旦客戶端得知這個secret, 那就意味著客戶端是可以自我簽發jwt了
本次使用jwt的token生成來實現一個登陸狀態的驗證。
二、使用步驟
1.創建項目,導入依賴,配置、引入工具類
本次使用mybatis-plus的一鍵生成項目,具體的步驟可以查看我之前的文章mybatis-plus詳解http://t.csdn.cn/F0QcR
引入依賴
<!-- jwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.8</version>
</dependency>
生成token的工具類:
package com.lzl.utils;
import io.jsonwebtoken.*;
import org.apache.commons.lang3.time.DateUtils;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.util.Date;
//jwt工具類
public class JwtHelper {
// 生成Jwt
public static String jwsWithHS(SignatureAlgorithm signatureAlgorithm, String userInfo, int num, String secret) {
Key key = new SecretKeySpec(secret.getBytes(), signatureAlgorithm.getJcaName());
Claims claims = Jwts.claims();
claims.setSubject(userInfo); // jwt的信息的本身
claims.setExpiration(DateUtils.addSeconds(new Date(), num)); //設置jwt多少秒過期
String jws = Jwts.builder()
.setClaims(claims).signWith(key, signatureAlgorithm).compact();
return jws;
}
// 校驗Jwt
public static Jwt verifySign(String jws, String secret, SignatureAlgorithm signatureAlgorithm) {
Key key = new SecretKeySpec(secret.getBytes(), signatureAlgorithm.getJcaName());
Jwt jwt = Jwts.parserBuilder().setSigningKey(key).build().parse(jws);
return jwt;
}
}
本次使用的secret:
由于我們需要在controller中使用,所以把兩個參數,密鑰和加密方式配置在yml文件中
這個secret可以去https://jwt.io/網站生成
上邊選擇加密方法,下邊輸入你想加密的信息,左側會顯示出加密后的信息。
2.編寫LoginController和UserController
package com.lzl.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lzl.pojo.User;
import com.lzl.service.UserService;
import com.lzl.utils.JwtHelper;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
* --效率,是成功的核心關鍵--
*
* @Author lzl
* @Date 2023/3/21 08:21
*/
@RestController
@RequestMapping("/login")
public class LoginController {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.signature-algorithm}")
private String algorithm;
@Autowired
private UserService service;
@RequestMapping("/verify")
public Map<String,Object> loginVerify(User user){
Map<String, Object> map = new HashMap<>();
//去庫中查詢
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("username",user.getUsername())
.eq("password",user.getPassword());
User one = service.getOne(wrapper);
if(one != null){
map.put("username",one.getUsername());
//登錄成功,生成token
String token = JwtHelper.jwsWithHS(
SignatureAlgorithm.forName(algorithm),
user.getUsername(),
3600,
secret
);
map.put("token",token);
}
return map;
}
}
package com.lzl.controller;
import com.lzl.pojo.User;
import com.lzl.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* <p>
* 前端控制器
* </p>
*
* @author zhenLong
* @since 2023-03-20
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService service;
@RequestMapping("/getAll")
public List<User> getUser(){
return service.list();
}
}
3.編寫跨域攔截器和token驗證攔截器
package com.lzl.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* --效率,是成功的核心關鍵--
* 攔截器,用于處理跨域請求
*
* @Author lzl
* @Date 2022/10/1 10:20
*/
public class CrossOriginInterceptor implements HandlerInterceptor {
//主要邏輯:在handler之前執行:抽取handler中的冗余代碼
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
String origin = request.getHeader("Origin");
// 允許的跨域
response.setHeader("Access-Control-Allow-Origin",origin);
// 允許攜帶Cookie
response.setHeader("Access-Control-Allow-Credentials","true");
// 允許的請求頭 預檢請求需要這個設置
response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization,Access-Token,token");
response.setHeader("Access-Control-Expose-Headers", "*");//響應客戶端的頭部 允許攜帶Token 等等
response.setHeader("Access-Control-Max-Age", "3600"); // 預檢請求的結果緩存時間
if (request.getMethod().equals("OPTIONS")){
return false;
}
return true;
}
//在handler之后執行:進一步的響應定制
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
//在頁面渲染完畢之后,執行:資源回收
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
package com.lzl.interceptor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* --效率,是成功的核心關鍵--
* token驗證攔截器
* @Author lzl
* @Date 2023/3/21 08:19
*/
public class VerifyTokenInterceptor implements HandlerInterceptor {
//在controller之前執行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");
/*
請求走到這,證明是非login方法(login方法已經被放行),驗證token是否存在,不存在直接攔截
存在?驗證是否有效,有效放行,無效攔截
*/
if (token == null){
response.setStatus(500);
Map<String, Object> map = new HashMap<>();
response.setContentType("application/json;charset=utf-8");
//is null or token unavailable
map.put("msg","登陸狀態已過期");
map.put("code",500);
response.getWriter().write(new ObjectMapper().writeValueAsString(map));
return false;
}
return true;
}
}
4.全局攔截器配置
在springboot中配置攔截器需要對攔截器進行注冊
package com.lzl.config;
import com.lzl.interceptor.CrossOriginInterceptor;
import com.lzl.interceptor.VerifyTokenInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* --效率,是成功的核心關鍵--
* 攔截器配置
*
* @Author lzl
* @Date 2023/1/26 14:12
*/
@Configuration
public class GlobalInterceptorConfig implements WebMvcConfigurer {
@Override //攔截器配置
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CrossOriginInterceptor()) //攔截器跨域注冊對象
.addPathPatterns("/**"); //指定要攔截的請求
registry.addInterceptor(new VerifyTokenInterceptor())//token驗證攔截器
.addPathPatterns("/**")
.excludePathPatterns("/login/*"); //排除請求
}
}
三、業務邏輯
登陸狀態驗證的業務邏輯很簡單,主要流程如下:
首先,用戶沒有登陸的情況下,用戶無法訪問除了login路徑下的接口,以外的任何一個接口,訪問的時候直接將頁面重定向到登錄頁,若是前后端分離項目,則給前端返回錯誤狀態碼,讓前端將用戶跳轉到登陸頁。用戶通過login接口輸入正確的帳號和密碼,后端生成一個token,并返回給前端,可以設置在請求頭,或者以參數的形式傳遞,當前端拿到這個token時就可以訪問登陸以外的頁面了。
四、測試
當沒有登陸狀態時
訪問login方法登陸,傳回來一個token
攜帶token訪問后端方法
訪問成功
總結
這里只是對token簡單的使用,在微服務架構中,token一般用于單點登陸驗證,即登陸完成后,將token傳到redis中存儲,當訪問除了登陸以外的其它服務時,去redis中查找。
原文鏈接:https://blog.csdn.net/l_zl2021/article/details/129696203
- 上一篇:沒有了
- 下一篇:沒有了
相關推薦
- 2022-07-09 python?監控某個進程內存的情況問題_python
- 2022-04-17 python使用openpyxl讀取日期并修改excel
- 2023-03-27 詳解C/C++高精度(加減乘除)算法中的壓位優化_C 語言
- 2021-12-14 HTML+jQuery實現簡單的登錄頁面_jquery
- 2023-01-17 Python中的迭代器與生成器使用及說明_python
- 2022-04-17 uniapp文本支持換行,后端返回一串文本
- 2023-07-08 編寫socket配置文件
- 2023-05-20 Python?seek()和tell()函數的具體使用_python
- 欄目分類
-
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支