Java實(shí)現(xiàn)JWT令牌的示例代碼
一、簡(jiǎn)介
JWT (JSON Web Token) 是一種 跨服務(wù)、跨語(yǔ)言的認(rèn)證解決方案,常用于前后端分離項(xiàng)目(比如 Spring Boot + Vue)。
它的主要作用是:
- 身份認(rèn)證
- 用戶登錄成功后,后端生成一個(gè) JWT 返回給前端。
- 前端每次請(qǐng)求時(shí)帶上這個(gè) JWT,后端根據(jù)它來(lái)確認(rèn)“你是誰(shuí)”。
- 無(wú)狀態(tài)認(rèn)證(不依賴 session)
- 傳統(tǒng)的 session 登錄需要服務(wù)器保存狀態(tài),用戶多了容易占用大量?jī)?nèi)存。
- JWT 是 自包含的,不需要服務(wù)器保存用戶狀態(tài),后端只要驗(yàn)證 token 就行。
- 安全傳遞信息
- JWT 由三部分組成:Header.Payload.Signature。
- Payload 里可以存儲(chǔ)用戶 ID、角色、過期時(shí)間等信息。
- Signature 簽名部分保證 token 不會(huì)被篡改。
二、實(shí)現(xiàn)流程
- 用戶在 Vue 前端輸入用戶名、密碼 → 發(fā)送到 Spring Boot 登錄接口。
- Spring Boot 驗(yàn)證賬號(hào)密碼成功后 → 生成 JWT → 返回給前端。
- Vue 將 JWT 保存(通常存在 localStorage 或 sessionStorage)。
- 前端每次請(qǐng)求后端 API 時(shí),在請(qǐng)求頭 Authorization 里帶上:Authorization: Bearer <token>
- Spring Boot 攔截請(qǐng)求,驗(yàn)證 JWT 是否有效:
- 簽名是否正確?
- 是否過期?
- 是否被篡改?
- 驗(yàn)證通過才放行,否則返回 401(未認(rèn)證)。
如圖:

三、實(shí)戰(zhàn)
這里我們用spring boot + vue 來(lái)演示,
在配置中:
# JWT #密鑰 注意密鑰要夠長(zhǎng)58位 jwt.secret=abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+ #過期時(shí)間 jwt.expiration= 3600000
創(chuàng)建工具類:
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private long expiration; // 例如 3600000 = 1小時(shí)
// 生成 token
public String generateToken(String username) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + expiration);
return Jwts.builder()
.setSubject(username)
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(Keys.hmacShaKeyFor(secret.getBytes()))
.compact();
}
// 獲取用戶名
public String getUsernameFromToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(Keys.hmacShaKeyFor(secret.getBytes()))
.build()
.parseClaimsJws(token)
.getBody()
.getSubject();
}
// 驗(yàn)證 token
public boolean validateToken(String token) {
try {
Jwts.parserBuilder()
.setSigningKey(Keys.hmacShaKeyFor(secret.getBytes()))
.build()
.parseClaimsJws(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}創(chuàng)建SecurityConfig文件
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
String path = exchange.getRequest().getURI().getPath();
// 1. 放行登錄注冊(cè)
if (path.startsWith("/user/login") || path.startsWith("/user/register") || path.startsWith("/chat-stream")) {
return chain.filter(exchange);
}
//如果沒有Authorization頭,返回401
if (!exchange.getRequest().getHeaders().containsKey("Authorization")) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 2. 處理Authorization頭
String authHeader = exchange.getRequest().getHeaders().getFirst("Authorization");
if (!authHeader.startsWith("Bearer ")) {
// 無(wú)有效Token,返回401
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 3. 解析Token
String token = authHeader.substring(7);
if (!jwtUtil.validateToken(token)) {
// Token驗(yàn)證失?。ㄟ^期、簽名錯(cuò)誤等),返回401并打印日志
System.out.println("JWT驗(yàn)證失?。篢oken無(wú)效或過期 - " + token);
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 4. Token有效,設(shè)置認(rèn)證信息到安全上下文
String username = jwtUtil.getUsernameFromToken(token);
Authentication auth = new UsernamePasswordAuthenticationToken(
username, null, Collections.emptyList() // 無(wú)權(quán)限,可根據(jù)需求添加
);
// 5. 傳遞認(rèn)證信息并繼續(xù)執(zhí)行過濾鏈
return chain.filter(exchange)
.contextWrite(ReactiveSecurityContextHolder.withAuthentication(auth));
}接著創(chuàng)建JwtFilter
@Autowired
private JwtUtil jwtUtil;
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
String path = exchange.getRequest().getURI().getPath();
// 1. 放行登錄注冊(cè)
if (path.startsWith("/user/login") || path.startsWith("/user/register") || path.startsWith("/chat-stream")) {
return chain.filter(exchange);
}
// 2. 處理Authorization頭
String authHeader = exchange.getRequest().getHeaders().getFirst("Authorization");
if (!authHeader.startsWith("Bearer ")) {
// 無(wú)有效Token,返回401
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 3. 解析Token
String token = authHeader.substring(7);
if (!jwtUtil.validateToken(token)) {
// Token驗(yàn)證失?。ㄟ^期、簽名錯(cuò)誤等),返回401并打印日志
System.out.println("JWT驗(yàn)證失?。篢oken無(wú)效或過期 - " + token);
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 4. Token有效,設(shè)置認(rèn)證信息到安全上下文
String username = jwtUtil.getUsernameFromToken(token);
Authentication auth = new UsernamePasswordAuthenticationToken(
username, null, Collections.emptyList() // 無(wú)權(quán)限,可根據(jù)需求添加
);
// 5. 傳遞認(rèn)證信息并繼續(xù)執(zhí)行過濾鏈
return chain.filter(exchange)
.contextWrite(ReactiveSecurityContextHolder.withAuthentication(auth));
}在控制層中,我們就可以在登錄邏輯中創(chuàng)建、返回給密鑰了:
//生成token
String token = jwtUtil.generateToken(user.getUserName());
//封裝返回?cái)?shù)據(jù)
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("token", token);在前端中,處理登錄請(qǐng)求,將登錄成功后后端返回的token保存到瀏覽器:
const onFinish = async (values) => {
loading.value = true
try {
if (isLogin.value) {
//加密數(shù)據(jù)
const encrypted = Encrypt(JSON.stringify(values))
// 實(shí)現(xiàn)登錄邏輯
const response = await request.post('/user/login', { data: encrypted })
//保存token
localStorage.setItem("token", JSON.parse(response.data).data.token)
alert("登錄成功")
// 刷新頁(yè)面
// window.location.reload()
} else {
//加密數(shù)據(jù)
const encrypted = Encrypt(JSON.stringify(values))
// 實(shí)現(xiàn)注冊(cè)邏輯
const response = await request.post('/user/register', { data: encrypted })
console.log('注冊(cè):', values)
}
} catch (error) {
console.error(error)
alert("登錄失敗")
} finally {
loading.value = false
}
}創(chuàng)建request.Js,將保存到本地瀏覽器的token放到請(qǐng)求頭中:
import axios from "axios";
const request = axios.create({
baseURL: "/api",
timeout: 30000, // 修改為30秒
responseType: "stream" // 流式響應(yīng)
});
// 請(qǐng)求攔截器:自動(dòng)攜帶 token
request.interceptors.request.use(
(config) => {
const token = localStorage.getItem("token");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
export default request;發(fā)送請(qǐng)求:
// 刪除
const deleteChat = (id) => {
Modal.confirm({
title: '確認(rèn)刪除',
content: '確定要?jiǎng)h除此對(duì)話嗎?(對(duì)話歷史也將會(huì)全部刪除)',
okText: '確認(rèn)',
cancelText: '取消',
onOk() {
// 調(diào)用后端接口刪除對(duì)話
eventSource?.close();
request.delete(`/chat/delete/${id}`)
.then((response) => {
if (response.data.code === "200") {
//顯示更新成功
message.success('刪除成功');
//刷新對(duì)話列表
getChatList();
} else {
//顯示更新失敗
message.error('刪除失敗');
}
})
.catch(err => {
message.error('刪除失敗');
// console.error('刪除失敗:', err);
});
},
onCancel() {
console.log('取消刪除');
},
});
};運(yùn)行可以看到,登錄成功后后端成功返回token:

Token成功保存:

如果token國(guó)企或者錯(cuò)誤:

再次請(qǐng)求會(huì)顯示401無(wú)權(quán)限報(bào)錯(cuò)

到此這篇關(guān)于Java實(shí)現(xiàn)JWT令牌的示例代碼的文章就介紹到這了,更多相關(guān)Java JWT令牌內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java實(shí)現(xiàn)單鏈表倒轉(zhuǎn)的方法
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)單鏈表倒轉(zhuǎn)的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05
SpringCloud中NacosNamingService的作用詳解
SpringCloud服務(wù)接口調(diào)用OpenFeign及使用詳解
關(guān)于Spring項(xiàng)目對(duì)JDBC的支持與基本使用詳解
Java如何獲取resources下的文件路徑和創(chuàng)建臨時(shí)文件

