以前單體應(yīng)用控制登錄session就夠了,后來(lái)對(duì)于多客戶端乘客,可以用cas做統(tǒng)一認(rèn)證狐血,之前也成功配置過(guò)cas5.3.9實(shí)現(xiàn)多客戶端單點(diǎn)登錄單點(diǎn)登出案例,但是也很麻煩易核,最大的缺點(diǎn)是cas還要另起一個(gè)用來(lái)做統(tǒng)一認(rèn)證的服務(wù)匈织,占用一個(gè)端口,而且也很吃內(nèi)存牡直,相似原理的還可以用redis等緩存數(shù)據(jù)庫(kù)弄一個(gè)共享緩存缀匕,也可以實(shí)現(xiàn)多客戶端單點(diǎn)登錄的功能,但是這兩個(gè)的方法都要額外占用端口和服務(wù)碰逸,直到了解到JWT乡小。
JWT是用java寫(xiě)的,可以生成一個(gè)獨(dú)一無(wú)二的token字符串饵史。
包括Header,Claim,ExpiresAt,sign,Header通常由兩部分組成:令牌的類(lèi)型满钟,即JWT。和常用的散列算法约急,如HMAC SHA256或RSA零远。
例如:
{
"alg": "HS256",
"typ": "JWT"
}
Header部分的JSON被Base64Url編碼,形成JWT的第一部分厌蔽。
Clainm是要加密的StringId,ExpiresAt可以設(shè)置過(guò)期時(shí)間牵辣,sign可以自己私加密匙,如此:
JWT.create()
.withHeader(header)
.withClaim("userId", userId)
.withExpiresAt(date)
.sign(algorithm);
便可以創(chuàng)建出一個(gè)加密的token字符串奴饮,通過(guò)把這個(gè)字符串分發(fā)給其他客戶端就可以識(shí)別是不是同一個(gè)用戶纬向,是哪個(gè)用戶,從而很簡(jiǎn)單的實(shí)現(xiàn)單點(diǎn)登錄戴卜。
maven下載jar包(截止發(fā)文最新版是3.10.2):
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.2</version>
</dependency>
創(chuàng)建JWTUtil工廠工具類(lèi)
package com.zhaohy.app.utils;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
public class JWTUtil {//過(guò)期時(shí)間
private static final long EXPIRE_TIME = 15 * 60 * 1000;//默認(rèn)15分鐘
//私鑰
private static final String TOKEN_SECRET = "privateKey";
/**
* 生成簽名逾条,15分鐘過(guò)期
* @param **username**
* @param **password**
* @return
*/
public static String createToken(String userId) {
try {
// 設(shè)置過(guò)期時(shí)間
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
// 私鑰和加密算法
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
// 設(shè)置頭部信息
Map<String, Object> header = new HashMap<>(2);
header.put("Type", "Jwt");
header.put("alg", "HS256");
// 返回token字符串
return JWT.create()
.withHeader(header)
.withClaim("userId", userId)
.withExpiresAt(date)
.sign(algorithm);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 生成token,自定義過(guò)期時(shí)間 毫秒
* @param **username**
* @param **password**
* @return
*/
public static String createToken(String userId,long expireDate) {
try {
// 設(shè)置過(guò)期時(shí)間
Date date = new Date(System.currentTimeMillis() + expireDate);
// 私鑰和加密算法
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
// 設(shè)置頭部信息
Map<String, Object> header = new HashMap<>(2);
header.put("Type", "Jwt");
header.put("alg", "HS256");
// 返回token字符串
return JWT.create()
.withHeader(header)
.withClaim("userId", userId)
.withExpiresAt(date)
.sign(algorithm);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 檢驗(yàn)token是否正確
* @param **token**
* @return
*/
public static String verifyToken(String token){
try {
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT jwt = verifier.verify(token);
String userId = jwt.getClaim("userId").asString();
return userId;
} catch (Exception e){
return null;
}
}
public static void main(String[] args) {
String token = JWTUtil.createToken("zhaohy", 3000);
System.out.println("token == " + token);
String userId = JWTUtil.verifyToken(token);
System.out.println("userId == " + userId);
//新建定時(shí)任務(wù)
Runnable runnable = new Runnable() {
//run方法中是定時(shí)執(zhí)行的操作
public void run() {
System.out.println(new Date());
String userId = JWTUtil.verifyToken(token);
System.out.println("userId == " + userId);
}
};
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
/*
* 參數(shù)一:command:執(zhí)行線程
* 參數(shù)二:initialDelay:初始化延時(shí)
* 參數(shù)三:period:兩次開(kāi)始執(zhí)行最小間隔時(shí)間
* 參數(shù)四:unit:計(jì)時(shí)單位
*/
service.scheduleAtFixedRate(runnable, 0, 4, TimeUnit.SECONDS);
}
}
運(yùn)行里面的main方法:
通過(guò)截圖中控制臺(tái)打印出的效果表明投剥,token可以被創(chuàng)建师脂,也可以被解析,過(guò)期時(shí)間之后就不見(jiàn)了江锨,過(guò)期時(shí)間是生效的吃警,至此,就可以在項(xiàng)目中愉快的使用啦啄育!