# SpringBootSecurity中JWT的使用方法
## 一、JWT基礎概念
### 1.1 什么是JWT
JSON Web Token(JWT)是一種開放標準(RFC 7519),用于在各方之間安全地傳輸信息作為JSON對象。JWT由三部分組成:
- Header(頭部)
- Payload(負載)
- Signature(簽名)
典型JWT格式:`xxxxx.yyyyy.zzzzz`
### 1.2 JWT的工作流程
1. 客戶端通過用戶名/密碼登錄
2. 服務端驗證后生成JWT返回
3. 客戶端后續請求攜帶JWT
4. 服務端驗證JWT有效性后響應請求
### 1.3 JWT的優勢
- 無狀態:服務端不需要存儲會話信息
- 跨域支持:適合分布式系統和微服務架構
- 安全性:基于數字簽名保證數據完整性
## 二、Spring Security集成JWT
### 2.1 環境準備
```xml
<!-- pom.xml 依賴 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
</dependencies>
public class JwtTokenUtil {
private static final String SECRET_KEY = "your-256-bit-secret";
private static final long EXPIRATION_TIME = 864_000_000; // 10天
public static String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public static Boolean validateToken(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
private static Boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
public static String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
private static Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
private static <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
private static Claims extractAllClaims(String token) {
return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
}
}
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Autowired
private UserDetailsService jwtUserDetailsService;
@Autowired
private JwtRequestFilter jwtRequestFilter;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/authenticate", "/register").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private JwtUserDetailsService jwtUserDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.extractUsername(jwtToken);
} catch (IllegalArgumentException e) {
logger.error("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
logger.error("JWT Token has expired");
}
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
chain.doFilter(request, response);
}
}
@RestController
public class JwtAuthenticationController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private JwtUserDetailsService userDetailsService;
@PostMapping("/authenticate")
public ResponseEntity<?> createAuthenticationToken(
@RequestBody JwtRequest authenticationRequest) throws Exception {
authenticate(authenticationRequest.getUsername(),
authenticationRequest.getPassword());
final UserDetails userDetails = userDetailsService
.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtResponse(token));
}
private void authenticate(String username, String password) throws Exception {
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
throw new Exception("USER_DISABLED", e);
} catch (BadCredentialsException e) {
throw new Exception("INVALID_CREDENTIALS", e);
}
}
}
public class JwtRequest implements Serializable {
private String username;
private String password;
// getters & setters
}
public class JwtResponse implements Serializable {
private final String token;
// constructor & getter
}
/authenticate
獲取tokencurl -X POST \
http://localhost:8080/authenticate \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"password"}'
curl -X GET \
http://localhost:8080/api/protected \
-H 'Authorization: Bearer <token>'
@PostMapping("/refresh")
public ResponseEntity<?> refreshToken(HttpServletRequest request) {
String oldToken = request.getHeader("Authorization").substring(7);
if (jwtTokenUtil.canTokenBeRefreshed(oldToken)) {
String newToken = jwtTokenUtil.refreshToken(oldToken);
return ResponseEntity.ok(new JwtResponse(newToken));
}
return ResponseEntity.badRequest().body("Token cannot be refreshed");
}
實現token失效功能:
@Component
public class TokenBlacklist {
private Set<String> blacklist = Collections.newSetFromMap(new ConcurrentHashMap<>());
public void add(String token) {
blacklist.add(token);
}
public boolean contains(String token) {
return blacklist.contains(token);
}
}
在微服務架構中: 1. 使用統一的認證服務 2. 配置相同的secret key 3. 考慮使用JWT + OAuth2組合方案
本文詳細介紹了在Spring Boot Security中集成JWT的完整方案,從基礎概念到具體實現,涵蓋了認證流程、安全配置、接口開發和進階優化等內容。JWT為現代分布式系統提供了輕量級的安全解決方案,結合Spring Security可以構建出既安全又靈活的認證授權體系。
實際項目中,開發者還需要根據具體業務需求進行調整,比如添加多因素認證、審計日志等功能,以構建更加完善的安保系統。 “`
注:本文實際字數為約3200字,包含了完整的代碼實現和理論說明。如需調整內容或字數,可以進一步修改補充。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。