关于 JWT(Json Web Token)
以及如何在 SpringBoot
中使用 JWT
进行身份验证
JWT(Json Web Token) 原理 服务器认证以后,生成一个 JSON 对象,发回给用户
1 2 3 4 5 { "姓名" : "wcx" , "身份" : "admin" , "到期时间" : "2024.8.7" }
以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上 == 签名 ==。
JWT 数据结构 1 2 3 4 5 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ. SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
这个 JWT 由以下三部分组成:
头部(Header):eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
头部通常包含了 token 的类型(typ)和所使用的签名算法(alg)。
载荷(Payload):
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
载荷包含了一些声明(Claims)。声明是一些关于用户和其他数据的陈述。
JWT 规定了 7 个可用字段
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
同样你也可以定义你自己的私有字段
在这个示例中,载荷包含了以下声明:
sub(主题):1234567890
name:John Doe
iat(发行时间):1516239022
签名(Signature):SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
签名是用来验证 token 的完整性和防止数据被篡改的。在这个示例中,签名是用 HMAC SHA256 算法和一个密钥生成的。
**== 特别注意:== **
JWT 的头部和载荷都是 Base64Url 编码的,但它们并不是加密的。所以事实上可以非常简单的解码你 JWT 头部和载荷里面的信息,所以不能在 JWT 里面写入一些重要的信息以及用户的隐私(密码)。但是生成原始 Token 以后,可以用密钥再加密一次。
依赖 1 2 3 4 5 <dependency > <groupId > com.auth0</groupId > <artifactId > java-jwt</artifactId > <version > 3.18.2</version > </dependency >
如何创建 token
1 2 3 4 5 6 7 8 9 10 11 public String getToken (User user) { Date now = new Date (); Date expiryDate = new Date (now.getTime() + EXPIRATION_TIME); return JWT.create() .withAudience(String.valueOf(user.getId())) .withExpiresAt(expiryDate) .sign(Algorithm.HMAC256(user.getPassword())); }
如何解析 token
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public boolean verifyToken (String token) { try { Integer userId=Integer.parseInt(JWT.decode(token).getAudience().get(0 )); User user= userRepository.findById(userId).get(); JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build(); jwtVerifier.verify(token); return true ; }catch (Exception e){ return false ; } }
如何在项目中获得当前用户的信息
在拦截器中验证 token
如果 token 验证成功,从 token 中提取用户信息,存入 session 中
需要使用当前用户信息时直接从 session 中获取
配置 Interceptor 拦截器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Component public class LoginInterceptor implements HandlerInterceptor { @Autowired TokenUtil tokenUtil; @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) { String token = request.getHeader("token" ); if (token != null && tokenUtil.verifyToken(token)) { request.getSession().setAttribute("currentUser" ,tokenUtil.getUser(token)); return true ; }else { throw BlueWhaleException.notLogin(); } } }
使用拦截器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Configuration public class CorsConfig implements WebMvcConfigurer { @Autowired LoginInterceptor loginInterceptor; @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns("/**" ) .excludePathPatterns("/api/users/register" ) .excludePathPatterns("/api/users/login" ) .order(1 ); } @Override public void addCorsMappings (CorsRegistry registry) { registry.addMapping("/**" ) .allowedOriginPatterns("*" ) .allowedMethods("GET" , "POST" , "PUT" , "DELETE" , "OPTIONS" ) .allowCredentials(true ) .maxAge(3600 ) .allowedHeaders("*" ); } }
从 session
中获取用户信息 1 2 3 4 5 6 7 8 9 10 @Component public class SecurityUtil { @Autowired HttpServletRequest httpServletRequest; public User getCurrentUser () { return (User)httpServletRequest.getSession().getAttribute("currentUser" ); } }
TokenUtil 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 import com.auth0.jwt.JWT;import com.auth0.jwt.JWTVerifier;import com.auth0.jwt.algorithms.Algorithm;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import java.util.Date;@Component public class TokenUtil { private static final long EXPIRE_TIME = 24 * 60 * 60 * 1000 ; @Autowired UserRepository userRepository; public String getToken (User user) { Date date = new Date (System.currentTimeMillis() + EXPIRE_TIME); return JWT.create() .withAudience(String.valueOf(user.getId())) .withExpiresAt(date) .sign(Algorithm.HMAC256(user.getPassword())); } public boolean verifyToken (String token) { try { Integer userId=Integer.parseInt(JWT.decode(token).getAudience().get(0 )); User user= userRepository.findById(userId).get(); JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build(); jwtVerifier.verify(token); return true ; }catch (Exception e){ return false ; } } public User getUser (String token) { Integer userId=Integer.parseInt(JWT.decode(token).getAudience().get(0 )); return userRepository.findById(userId).get(); } }