JWT介绍
JWT(JSON Web Token)是一种用于在网络应用环境中安全地传递信息的开放标准(RFC 7519)。它是一种基于 JSON 的令牌格式,广泛用于身份验证和信息交换。
JWT的结构
JWT 通常由三部分组成:头部(Header)、有效载荷(Payload)和签名(Signature)。
这三部分通过 . 字符连接在一起,形成一个字符串header.payload.signature
Header
通常包含令牌的类型(即 “JWT”)和所使用的签名算法(如 HMAC SHA256 或 RSA)
Payload
包含实际的声明(claims),即要传递的信息。声明可以是预定义的(如 iss、exp 等)或自定义的。
有效载荷部分不加密,因此不应在其中存储敏感信息。
Signature
通过将头部和有效载荷进行编码后,用指定的算法(如 HMAC SHA256)和密钥生成的签名。这个签名用于验证消息的完整性和来源。
JWT的使用
依赖添加
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version> <!-- 检查最新版本 -->
</dependency>
在pom.xml
文件里添加依赖
创建 JWT 工具类
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;
@Component
public class JwtUtil {
private final String SECRET_KEY = "your_secret_key"; // 密钥
private final long EXPIRATION_TIME = 1000 * 60 * 60; // 设置过期时间
// 生成 JWT
public String generateToken(String username) {
Map<String, Object> claims = new HashMap<>();
return createToken(claims, username);
}
private String createToken(Map<String, Object> claims, String subject) {
return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
// 验证 JWT
public boolean validateToken(String token, String username) {
final String extractedUsername = extractUsername(token);
return (extractedUsername.equals(username) && !isTokenExpired(token));
}
private String extractUsername(String token) {
return extractAllClaims(token).getSubject();
}
private Claims extractAllClaims(String token) {
return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
}
private boolean isTokenExpired(String token) {
return extractAllClaims(token).getExpiration().before(new Date());
}
}
创建认证控制器
创建一个控制器,用于处理用户登录并返回 JWT
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private JwtUtil jwtUtil;
@PostMapping("/login")
public String login(@RequestBody AuthRequest authRequest) {
// 在这里你需要验证用户的用户名和密码
// 如果验证成功,生成 JWT
String token = jwtUtil.generateToken(authRequest.getUsername());
return token;
}
}
创建请求模型
public class AuthRequest {
private String username;
private String password;
// ... ...
}
配置安全性
与Spring Security整合,每次请求时都验证
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtRequestFilter jwtRequestFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/login").permitAll() // 允许登录请求
.anyRequest().authenticated(); // 其他请求需要认证
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
创建 JWT 过滤器
创建一个过滤器,用于在每次请求中解析和验证 JWT
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.WebAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class JwtRequestFilter extends WebAuthenticationFilter {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
username = jwtUtil.extractUsername(jwt);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwt, userDetails.getUsername())) {
// 设置用户身份
// 这里可以设置 Authentication 对象到 SecurityContext
}
}
chain.doFilter(request, response);
}
}
应用
启动 Spring Boot 应用,使用 Postman 或其他工具发送 POST 请求到 /api/auth/login,并传递用户名和密码,成功后会返回 JWT
在后续的请求中,需要在请求头中添加Authorization
字段
Authorization: Bearer <your_jwt_token>
JWT的优势
无状态(Stateless)
无状态意味着服务器在处理请求时,不需要存储任何客户端的会话状态。每次请求都是独立的,服务器不会在其内存或数据库中保存关于用户的会话信息。
客户端在后续请求中将 JWT 作为身份凭证发送,服务器只需验证 JWT 的有效性(如签名和过期时间),就可以立即获取用户的身份信息,而无需查询数据库。
减少对数据库的依赖
在传统的会话管理中,服务器通常会在数据库或内存中保存用户的会话信息(如用户ID、权限、登录状态等)。每次请求都需要查找和验证这些信息。这增加了数据库的负担,并可能导致性能瓶颈。
JWT 自包含了所有的用户信息,无需存储在服务器端。令牌中包含的有效载荷可以包括用户的角色、权限等信息,服务器在接收到请求时无需查询数据库来获取用户信息。
由于每个 JWT 令牌都包含所有必要的信息,因此在微服务架构中,服务之间可以相对独立地工作。各个服务可以根据 JWT 中的信息进行身份验证和授权,而不需要访问中央数据库,从而提高了系统的扩展性和响应速度。
JWT的缺点
当登录一个网站时,网站会生成一个JWT给用户,里面包括所有权限信息,之后的每一次通信都会携带此JWT。
那么,如果在此网站,用户点击了退出登录,看似用户下线了,但实际上JWT还未到过期时间,那么此时这个JWT还在有效期内,若JWT被拦截,则会产生安全隐患。
实际上,JWT的缺点不止这些。
大小
我们需要存储一个用户ID 为sienna
如果存储到cookie里面,我们的总大小只有6个字节。
如果我们将 ID 存储在 一个 JWT 里。他的大小就会增加几十倍
这大大增加了宽带负担
令牌撤销
JWT就相当于一个令牌,发出后无法收回,安全性有很大的隐患。
首先,注销并不会使其真的被注销。
其次,信息的更改也不能够及时的同步到令牌上面。
JWT的保密性
JWT的内容不加密,意味着获得针对身份验证凭据的攻击变得容易。
总结
但JWT也是具有很多应用性的,比如单次授权的时候,若考虑长期存储,特别是管理用户会话,JWT会带来很大的安全问题,此时,还是传统的会话机制,比如cookie更加适用。
标签:String,优劣,JWT,springframework,private,import,org,SpringBoot From: https://blog.csdn.net/weixin_47510148/article/details/143440017