JWT
分三部分,头部信息,承载信息,验证签名;
- 头部信息
通常由两部分组成:令牌的类型,即 JWT,以及正在使用的签名算法,例如 HMAC SHA256 或 RSA。 - 承载信息
一般用于放置用户信息等。 - 验证签名
要创建签名部分,必须获取头部信息、承载信息、秘钥,按头部信息中指定的算法对其进行签名。
HMACSHA256(
base64UrlEncode(header) + “.” +
base64UrlEncode(payload),
secret) - 注意头部信息及承载信息是基于Base64编码,等于是明文,除非再加密,不建议把重要信息放置在这两部分中。
应用场景
-
授权:这是使用 JWT 最常见的场景。用户登录后,每个后续请求都将包含 JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是当今广泛使用 JWT 的一项功能,因为它的开销很小,并且能够轻松跨不同域使用。
-
信息交换:JSON Web Tokens 是一种在各方之间安全传输信息的好方法。因为 JWT 可以被签名。此外,由于使用标头和有效负载计算签名,因此还可以验证内容是否未被篡改。
依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.18.0</version>
</dependency>
编写一个工具类,备用
/**
* 管理员表(SysUser)实体类
*
* @author chanyu
* @since 2020-06-01 20:48:27
*/
@TableName("sys_user")
@Data
public class SysUser extends BaseHenEntity {
@TableId(type = IdType.AUTO)
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
/**
* 密码
*/
@NotNull
@Length(min = 6,max = 20)
private String password;
/**
* 名字
*/
private String name;
}
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.chan.hen.entity.SysUser;
/**
* @Description TODO
* @Author chanyu
* @Date 2021/9/2 21:33
* @Version 1.0
**/
public class JwtUtil {
private JwtUtil(){}
/**
* 获取token
* @param sysUser
* @return
*/
public static String getToken(SysUser sysUser){
String token = JWT.create()
.withAudience(String.valueOf(sysUser.getId()))//payload的aud固定值
.sign(Algorithm.HMAC256(sysUser.getPassword()));//以用户pwd签名
return token;
}
/**
* 验证token
* @param sysUser
* @param token
* @throws JWTVerificationException
*/
public static void verify(SysUser sysUser,String token) throws JWTVerificationException {
JWTVerifier jwtVerifier = JWT.require(getAlgorithm(sysUser.getPassword())).build();
jwtVerifier.verify(token);
}
/** 签名 验证工具 */
private static Algorithm getAlgorithm(String key){
return Algorithm.HMAC256(key);
}
}
建拦截器,处理token校验
import com.chan.hen.entity.SysUser;
import com.chan.hen.service.SysUserService;
import com.chan.hen.util.JwtUtil;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Description TODO
* @Author chanyu
* @Date 2021/9/2 22:09
* @Version 1.0
**/
public class AuthenticationInterceptor implements HandlerInterceptor {
@Resource
private SysUserService sysUserService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");
if(StringUtils.isEmpty(token)){
throw new RuntimeException("未登陆用户,请先登陆");
}
Long userid = JwtUtil.getAud(token);
if (userid == null || userid <1){
throw new RuntimeException("token不合法,未获取用户信息");
}
SysUser sysUser = sysUserService.getById(userid);
try {
JwtUtil.verify(sysUser,token);
} catch (JWTVerificationException e) {
throw new RuntimeException("token验证失败");
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o, Exception e) throws Exception {
}
}
简单登陆测试
登陆获取JWT token
@GetMapping("/login")
public @ResponseBody String login(String name , String password ) {
//XXX 登陆处理
return JwtUtil.getToken(sysUser);
}
不带token发起请求。
修改token发起请求。
将正确的token放到下一次调用接口的header中。