Java中JWT(JSON?Web?Token)的運(yùn)用具體案例
前言
JWT(JSON Web Token)是一種開放標(biāo)準(zhǔn)(RFC 7519),用于在網(wǎng)絡(luò)應(yīng)用環(huán)境間以緊湊的方式安全地傳遞信息。JWT可以被用作身份驗(yàn)證和信息交換的手段,特別適合用于前后端分離的應(yīng)用程序。
1. JWT的結(jié)構(gòu)
JWT由三部分組成:
Header(頭部): 通常包含令牌的類型(JWT)和所使用的簽名算法(如HMAC SHA256或RSA)。
Payload(負(fù)載): 包含聲明(claims),即要傳遞的數(shù)據(jù)。其中可以包含注冊(cè)聲明(如 iss、exp、sub 等)和自定義聲明。
Signature(簽名): 用于驗(yàn)證消息的完整性和發(fā)送者的身份。通過將編碼后的header和payload與一個(gè)密鑰結(jié)合,利用指定的算法生成。
JWT的格式如下:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
2. JWT的優(yōu)點(diǎn)
- 無狀態(tài): 不需要在服務(wù)器存儲(chǔ)會(huì)話信息,減輕了服務(wù)器的負(fù)擔(dān)。
- 跨域: 可以在不同的域之間進(jìn)行身份驗(yàn)證。
- 自包含: 含有用戶身份信息,避免頻繁查詢數(shù)據(jù)庫(kù)。
3. JWT的流轉(zhuǎn)過程
- 用戶通過登錄接口發(fā)送用戶名和密碼。
- 服務(wù)器驗(yàn)證用戶信息,如果成功,則生成JWT并返回給用戶。
- 用戶在后續(xù)請(qǐng)求中帶上JWT,通常放在HTTP請(qǐng)求的Authorization頭中。
- 服務(wù)器解析JWT,驗(yàn)證其有效性,允許或拒絕請(qǐng)求。
4.具體案例
好的,讓我們更詳細(xì)地探討如何在Java Spring Boot應(yīng)用程序中實(shí)現(xiàn)JWT身份驗(yàn)證,包括每一步的代碼和說明。
一、項(xiàng)目結(jié)構(gòu)
Spring Boot項(xiàng)目結(jié)構(gòu)如下:
src ├── main │ ├── java │ │ └── com │ │ └── example │ │ └── jwt │ │ ├── JwtApplication.java │ │ ├── config │ │ │ └── SecurityConfig.java │ │ ├── controller │ │ │ └── AuthController.java │ │ ├── model │ │ │ └── User.java │ │ ├── service │ │ │ └── JwtUtil.java │ │ └── filter │ │ └── JwtRequestFilter.java │ └── resources │ └── application.properties └── test
二、依賴配置
在pom.xml中添加必要的依賴:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies>
三、用戶模型
創(chuàng)建一個(gè)簡(jiǎn)單的用戶模型:
package com.example.jwt.model;
public class User {
private String username;
private String password;
// Constructors, getters, and setters
//也可以使用Lombok
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}
四、JWT工具類
創(chuàng)建JWT工具類,負(fù)責(zé)生成和解析JWT:
package com.example.jwt.service;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
*Jwt工具類
*/
@Component//加入Spring容器
public class JwtUtil {
private String secret = "your_secret_key"; // 強(qiáng)密碼
private long expiration = 60 * 60 * 1000; // 1小時(shí)
//這個(gè)方法用于生成 JWT。它接受用戶名作為參數(shù)。
public String generateToken(String username) {
Map<String, Object> claims = new HashMap<>();
return createToken(claims, username);
}
//私有方法,用于實(shí)際生成 JWT
private String createToken(Map<String, Object> claims, String subject) {
return Jwts.builder()//開始構(gòu)建 JWT 的構(gòu)建器
.setClaims(claims)//設(shè)置 JWT 中的聲明
.setSubject(subject)//設(shè)置主題(通常是用戶名)
.setIssuedAt(new Date(System.currentTimeMillis()))//設(shè)置 JWT 的簽發(fā)時(shí)間
.setExpiration(new Date(System.currentTimeMillis() + expiration))//設(shè)置 JWT 的過期時(shí)間
.signWith(SignatureAlgorithm.HS256, secret)//使用指定的算法(HS256)和密鑰對(duì) JWT 進(jìn)行簽名
.compact();//生成最終的 JWT 字符串
}
//用于驗(yàn)證給定的 JWT 是否有效。
public Boolean validateToken(String token, String username) {
final String extractedUsername = extractUsername(token);//提取 JWT 中的用戶名
return (extractedUsername.equals(username) && !isTokenExpired(token));//檢查提取的用戶名與提供的用戶名是否匹配,并且檢查 JWT 是否未過期
}
//從 JWT 中提取用戶名
public String extractUsername(String token) {
return extractAllClaims(token).getSubject();
}
//解析 JWT 并返回所有聲明
private Claims extractAllClaims(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}//使用密鑰解析 JWT 獲取其主體部分(聲明)
//檢查 JWT 是否已過期
private Boolean isTokenExpired(String token) {
return extractAllClaims(token).getExpiration().before(new Date());
}
}
五、JWT請(qǐng)求過濾器
創(chuàng)建JWT請(qǐng)求過濾器,用于攔截請(qǐng)求并驗(yàn)證JWT:
package com.example.jwt.filter;
import com.example.jwt.service.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component // 將該類標(biāo)記為 Spring 組件,以便自動(dòng)掃描和管理
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired // 自動(dòng)注入 JwtUtil 實(shí)例
private JwtUtil jwtUtil;
@Autowired // 自動(dòng)注入 UserDetailsService 實(shí)例
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
// 從請(qǐng)求中獲取 Authorization 頭部
final String authorizationHeader = request.getHeader("Authorization");
String username = null; // 初始化用戶名
String jwt = null; // 初始化 JWT 令牌
// 檢查 Authorization 頭部是否存在且以 "Bearer " 開頭
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
// 提取 JWT 令牌(去掉 "Bearer " 前綴)
jwt = authorizationHeader.substring(7);
// 從 JWT 中提取用戶名
username = jwtUtil.extractUsername(jwt);
}
// 如果用戶名不為空且當(dāng)前 SecurityContext 沒有身份驗(yàn)證信息
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
// 根據(jù)用戶名加載用戶詳細(xì)信息
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
// 驗(yàn)證 JWT 是否有效
if (jwtUtil.validateToken(jwt, userDetails.getUsername())) {
// 創(chuàng)建身份驗(yàn)證令牌,并設(shè)置用戶的權(quán)限
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
// 設(shè)置請(qǐng)求的詳細(xì)信息
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// 將身份驗(yàn)證信息存入 SecurityContext
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
// 繼續(xù)過濾器鏈
chain.doFilter(request, response);
}
}
六、安全配置
配置Spring Security,以保護(hù)API并使用JWT:
package com.example.jwt.config;
import com.example.jwt.filter.JwtRequestFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtRequestFilter jwtRequestFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/auth/login").permitAll() // 公開登錄接口
.anyRequest().authenticated() // 其他接口需要認(rèn)證
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 無狀態(tài)會(huì)話
// 添加JWT過濾器
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password(passwordEncoder().encode("password")).roles("USER"); // 示例用戶
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
七、身份驗(yàn)證控制器
創(chuàng)建一個(gè)控制器來處理登錄請(qǐng)求并返回JWT:
package com.example.jwt.controller;
import com.example.jwt.model.User;
import com.example.jwt.service.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/auth")
public class AuthController {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@PostMapping("/login")
public String login(@RequestBody User user) {
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword())
);
} catch (Exception e) {
throw new RuntimeException("Invalid credentials");
}
final UserDetails userDetails = userDetailsService.loadUserByUsername(user.getUsername());
return jwtUtil.generateToken(userDetails.getUsername());
}
}
八、測(cè)試JWT
- 啟動(dòng)Spring Boot應(yīng)用程序。
- 使用Postman或其他工具測(cè)試登錄接口。
請(qǐng)求示例:
POST /auth/login
Content-Type: application/json
{
"username": "user",
"password": "password"
}
響應(yīng)示例:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5c..."
}
- 使用返回的token訪問受保護(hù)的資源:
GET /protected/resource Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5c...
總結(jié)
到此這篇關(guān)于Java中JWT(JSON Web Token)運(yùn)用的文章就介紹到這了,更多相關(guān)Java中JWT的運(yùn)用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java集合框架之Stack Queue Deque使用詳解刨析
早在 Java 2 中之前,Java 就提供了特設(shè)類。比如:Dictionary, Vector, Stack, 和 Properties 這些類用來存儲(chǔ)和操作對(duì)象組。雖然這些類都非常有用,但是它們?nèi)鄙僖粋€(gè)核心的,統(tǒng)一的主題。由于這個(gè)原因,使用 Vector 類的方式和使用 Properties 類的方式有著很大不同2021-10-10
零基礎(chǔ)寫Java知乎爬蟲之獲取知乎編輯推薦內(nèi)容
上篇文章我們拿百度首頁做了個(gè)小測(cè)試,今天我們來個(gè)復(fù)雜的,直接抓取知乎編輯推薦的內(nèi)容,小伙伴們可算松了口氣,終于進(jìn)入正題了,哈哈。2014-11-11
Spring?Data?JPA命名約定查詢實(shí)現(xiàn)方法
這篇文章主要為大家介紹了Spring?Data?JPA命名約定查詢實(shí)現(xiàn)方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
Java實(shí)現(xiàn)將彩色PDF轉(zhuǎn)為灰度PDF的示例代碼
本文以Java代碼為例介紹如何實(shí)現(xiàn)將彩色PDF文件轉(zhuǎn)為灰度(黑白)的PDF文件,文中的示例代碼講解詳細(xì),感興趣的小伙伴快跟隨小編一起學(xué)習(xí)一下吧2022-03-03
Springboot使用zxing實(shí)現(xiàn)二維碼生成和解析
ZXing支持各種條形碼,二維碼掃描,由多個(gè)模塊組成,?而且支持PC端,移動(dòng)端,本文將利用zxing實(shí)現(xiàn)二維碼生成和解析,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-10-10
Spring MVC---數(shù)據(jù)綁定和表單標(biāo)簽詳解
本篇文章主要介紹了Spring MVC---數(shù)據(jù)綁定和表單標(biāo)簽詳解,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-01-01
Java多線程編程之訪問共享對(duì)象和數(shù)據(jù)的方法
這篇文章主要介紹了Java多線程編程之訪問共享對(duì)象和數(shù)據(jù)的方法,多個(gè)線程訪問共享對(duì)象和數(shù)據(jù)的方式有兩種情況,本文分別給出代碼實(shí)例,需要的朋友可以參考下2015-05-05

