什么是JWT
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
使用方式
引入依赖
<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
1、生成token
//token生成密钥,由服务器持有,不可泄漏
private static String SING = "token!=k(2+68*¥GOInf";
/**
* 生成token
* @param map 要放入token的payload,里面可以包含一些信息,之后可以解析出来。
* @return token字符串
*/
public static String getToken(Map<String, String> map) {
JWTCreator.Builder builder = JWT.create();
map.forEach(builder::withClaim); //放入payload信息
Calendar instance = Calendar.getInstance();
instance.add(Calendar.DATE, 7); //默认七天过期
builder.withExpiresAt(instance.getTime());
return builder.sign(Algorithm.HMAC256(SING));//使用加密算法
}
示例:
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
map.put("username", "jinchengll");
String token = JWTUtils.getToken(map);
System.out.println(token);
}
结果:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MDE0NzA4OTYsInVzZXJuYW1lIjoiamluY2hlbmdsbCJ9.Xm-lCjUy2SV6NlIxB81FI3znr3pR4a2onKj3Ta0D-Ow
由三部分组成,作为token返回给用户。
2、验证token
浏览器以及客户端在获得token之后的请求都应该携带token来标示身份,最常见的是将token放在请求头里面。
服务器使用相同的算法以及密钥对token进行验证:
/**
* 验证和读取token中的payload
* 如果验证失败会报错,因此在外部可以进行捕获异常来处理结果。
* @param token token
* @return 包含payload的信息
*/
public static DecodedJWT verify(String token) {
return JWT.require(Algorithm.HMAC256(SING)).build().verify(token);
}
示例:
public static void main(String[] args) {
String token = "填入1中得到的token字符串";
// 根据不同的业务处理验证时候产生的不同错误
try {
DecodedJWT verify = JWTUtils.verify(token);
String username = verify.getClaim("username").asString();
System.out.println(username);
} catch (SignatureVerificationException e) {
System.out.println("token签名无效");
} catch (TokenExpiredException e) {
System.out.println("token过期");
} catch (AlgorithmMismatchException e) {
System.out.println("token算法不一致");
} catch (Exception e) {
System.out.println("token无效");
}
}
结果:
jinchengll
3、工具类
在这里贴出简单封装的工具类:
/**
* @Author jinchengll
* @Date 2020/9/6 6:51 下午
*/
public class JWTUtils {
//token生成密钥
private static String SING = "你的密钥";
/**
* 生成token
*
* @param map 要放入token的payload
* @return token字符串
*/
public static String getToken(Map<String, String> map) {
JWTCreator.Builder builder = JWT.create();
map.forEach(builder::withClaim); //放入payload信息
Calendar instance = Calendar.getInstance();
instance.add(Calendar.DATE, 7); //默认七天过期
builder.withExpiresAt(instance.getTime());
return builder.sign(Algorithm.HMAC256(SING));//使用加密算法
}
/**
* 验证和读取token中的payload
*
* @param token token
* @return 包含payload的信息
*/
public static DecodedJWT verify(String token) {
return JWT.require(Algorithm.HMAC256(SING)).build().verify(token);
}
}
JWT与SpringBoot的整合使用
通常在用户登陆成功后,服务器就会生成token返回给客户端,接下去客户端对服务器的请求都应该在请求头中携带token来标示其身份。
但是我们不可能对每个请求接口都手动编写token的验证代码,因此在SpringBoot项目中可以借助拦截器(AOP)来实现,方法如下:
1、编写校验拦截器
/**
* @Author jinchengll
* @Date 2020/9/6 7:54 下午
*/
public class JWTInterceptor implements HandlerInterceptor {
/**
* 在这里验证token的正确性,通过返回true,失败返回false
*
* @param request 请求
* @param response 响应
* @param handler handler
* @return 是否通行
* @throws Exception 异常
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//结果封装
ResultInfo<String> resultInfo = null;
//从请求头中获得token
String token = request.getHeader("token");
//开始验证
try {
JWTUtils.verify(token);
//没有异常,验证成功,放行
return true;
} catch (SignatureVerificationException e) {
resultInfo = new ResultInfo<>(ResponseEnum.CUSTOM_ERROR, "token签名无效");
} catch (TokenExpiredException e) {
resultInfo = new ResultInfo<>(ResponseEnum.CUSTOM_ERROR, "token过期");
} catch (AlgorithmMismatchException e) {
resultInfo = new ResultInfo<>(ResponseEnum.CUSTOM_ERROR, "token算法不一致");
} catch (Exception e) {
resultInfo = new ResultInfo<>(ResponseEnum.CUSTOM_ERROR, "token无效");
}
//将结果转成json,并返回给界面
String resultJson = new ObjectMapper().writeValueAsString(resultInfo);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(resultJson);
//拦截
return false;
}
}
2、配置拦截器
/**
* @Author jinchengll
* @Date 2020/9/6 8:04 下午
* 配置该应用的拦截器。
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//除了登陆接口,拦截其他所有接口
registry.addInterceptor(new JWTInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login/**");
}
}
到此项目就已经集成了JWT的单点登录功能,这在分布式的项目中也同样适用。