什么是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的單點登錄功能,這在分布式的項目中也同樣適用。