shiro安全控制目錄
shiro有狀態(tài)認(rèn)證是利用session保存登錄狀態(tài)的授權(quán)認(rèn)證方式锹杈。但是當(dāng)前后端分離迈着,web服務(wù)無狀態(tài)化之后,那么JWT身份認(rèn)證便是趨勢咬清。
1. JWT簡介
1.1 什么叫做JWT
JWT(json web token)是為了在網(wǎng)絡(luò)應(yīng)用環(huán)境之間傳遞聲明而執(zhí)行的一種基于JSON的開放標(biāo)準(zhǔn)奴潘。
JWT的聲明一般被用來在身份提供者和服務(wù)提供者間傳遞被認(rèn)證的用戶身份信息,以便從資源服務(wù)器獲取資源粪滤。比如用于登錄雀扶。
shiro(9)-有狀態(tài)身份認(rèn)證和無狀態(tài)身份認(rèn)證的區(qū)別
1.2 JWT的構(gòu)成
JWT由三部分組成:頭部(header)、載荷(payload)予权、簽名(signature)。頭部定義類型和加密方式岗照;載荷部分放的不是很重要的數(shù)據(jù)笆环;簽名使用定義的加密方式加密base64后的header和payload和一段自己加密key。最后的token由base64(header).base64(payload).base64(signature)組成迫吐。
JWT生成Token后是這個樣子的:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJvcmciOiLku4rml6XlpLTmnaEiLCJuYW1lIjoiRnJlZeeggeWGnCIsImV4cCI6MTUxNDM1NjEwMywiaWF0IjoxNTE0MzU2MDQzLCJhZ2UiOiIyOCJ9.49UF72vSkj-sA4aHHiYN5eoZ9Nb4w5Vb45PsLF7x_NY
1.2.1. header
JWT頭部分是一個描述JWT元數(shù)據(jù)的JSON對象志膀。
- 聲明類型鳖擒,這里是jwt。
- 聲明加密的算法戳稽,通常直接使用HMAC SHA256期升。
完整的頭部就像下面這樣的json。
{"typ": "JWT","alg": "HS256"}
然后將頭部進(jìn)行base64加密赊时,構(gòu)成第一部分:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
1.2.2. payload
載荷是存放有效信息的地方祖秒,這些有效部分包含三個部分舟奠。
- 標(biāo)準(zhǔn)中注冊的聲明;
- 公共的聲明抬纸;
- 私有的聲明耿戚;
標(biāo)準(zhǔn)中注冊的聲明:
類型 | 作用 |
---|---|
iss | 發(fā)行人 |
sub | 主題 |
aud | 接收Token的用戶 |
exp | Token過期時間 |
iat | Token簽發(fā)時間 |
nbf | 在該時間之前Token不可用 |
jti | Token的唯一標(biāo)識 |
公共的聲明:
公共的聲明可以添加任何的信息,一般添加用戶的相關(guān)信息或其他業(yè)務(wù)需要的必要信息坛猪,但不建議添加敏感的信息,因為這部分在客戶端可解密命黔。
私有的聲明:
私有聲明是提供者和消費者所共同定義的聲明就斤,一般不建議存放敏感信息,因為base64是對稱解密的搜立,意味著該部分信息可以歸類為明文信息槐秧。
定義一個payload:
{"name":"Free碼農(nóng)","age":"28","org":"今日頭條"}
然后將其進(jìn)行base64加密刁标,得到第二部分
eyJvcmciOiLku4rml6XlpLTmnaEiLCJuYW1lIjoiRnJlZeeggeWGnCIsImV4cCI6MTUxNDM1NjEwMywiaWF0IjoxNTE0MzU2MDQzLCJhZ2UiOiIyOCJ9
1.2.3. signuature
jwt的第三部分是一個簽證信息址晕,這個簽證信息由三部分組成:
- header(base64后的)
- payload(base64后的)
- secret(密鑰)
這個部分需要base64加密后的header和base64加密后的payload使用.
連接組成的字符串谨垃,然后通過header中聲明的加密方式進(jìn)行加鹽secret組合加密,就構(gòu)成了jwt的第三部分:
49UF72vSkj-sA4aHHiYN5eoZ9Nb4w5Vb45PsLF7x_NY
注:密鑰secret是保存在服務(wù)端的胳赌,服務(wù)端會根據(jù)這個密鑰進(jìn)行生成token和驗證匙隔,所以要保護(hù)好。
1.3 JWT優(yōu)點
- 因為json的通用性捍掺,所以JWT是可以跨語言支持的再膳。
- payload部分,JWT可以在自身存儲一些其他業(yè)務(wù)邏輯所必要的非敏感信息不瓶。
- 便于傳輸,JWT構(gòu)成簡單湃番,字節(jié)占用很小,所以它是非常便于傳輸?shù)淖鸲瑁恍枰诜?wù)端保存會話信息泥兰,所以利于應(yīng)用的擴(kuò)展。
2. JAVA代碼生成JWT
1.maven依賴
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
2. 生成jwt
public class JwtUtils {
private static final String secret = "66diangezanbageixioapang12oc";
/**
* (默認(rèn)的超時時間)token存活時間膀捷,2小時
*/
private static final long liveMills = 3600 * 2 * 1000;
/**
* 獲取secret
*/
public static SecretKey obtainKey() {
//對key進(jìn)行解碼
byte[] secretBytes = secret.getBytes();
return new SecretKeySpec(secretBytes, 0, secretBytes.length, "AES");
}
/**
* 獲取token全庸,使用默認(rèn)的超時時間融痛,即2個小時。
*
* @param sub 主題(需要加密的字符串)
* @return token字符串
*/
public static String createJWT(String sub) {
return createJWT(sub, liveMills);
}
/**
* 會自動對主題對象序列化(使用fastJson進(jìn)行序列化)得到字符串
*
* @param subObj 主題對象
* @return token字符串
*/
public static <T> String createJWT(T subObj) {
return createJWT(JSONObject.toJSONString(subObj), liveMills);
}
/**
* 會自動對主題對象序列化(使用fastJson進(jìn)行序列化)得到字符串
*
* @param subObj 主題對象
* @param liveMills 失效時間覆劈,單位毫秒
* @return token字符串
*/
public static <T> String createJWT(T subObj, Long liveMills) {
return createJWT(JSONObject.toJSONString(subObj), liveMills);
}
/**
* @param sub 需要加密的主題
* @param liveMills 失效時間责语,單位毫秒
* @return 經(jīng)過jwt加密的token字符串目派,失效時間即當(dāng)前時間+liveMills毫秒數(shù)
*/
public static String createJWT(String sub, Long liveMills) {
//加密模式
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long currentTimeMillis = System.currentTimeMillis();
Date now = new Date(currentTimeMillis); //iat token簽發(fā)時間
SecretKey secretKey = obtainKey();
//jti表示該token的唯一id,不推薦使用相同值|isa 下發(fā)時間
JwtBuilder jwtBuilder = Jwts.builder().setId("jti-xp").
setIssuedAt(now).
setSubject(sub).
signWith(signatureAlgorithm, secretKey);
if (liveMills > 0) {
long expMills = currentTimeMillis + liveMills;
Date expDate = new Date(expMills); //失效時間
jwtBuilder.setExpiration(expDate);
}
return jwtBuilder.compact();
}
/**
* 解密token,返回Claims對象
* <p>
* 注铐拐,若token失效练对,會拋出{@link ExpiredJwtException}的異常
**/
public static Claims parseJWT(String token) {
SecretKey key = obtainKey();
return Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody(); // Claims [kle?mz] 聲明
}
/**
* 解析token獲取到sub(主題)螟凭。
* <p>
* 注,若token失效螺男,會拋出{@link ExpiredJwtException}的異常
*/
public static String parseJWT2Sub(String token) {
SecretKey key = obtainKey();
Claims body = Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody();
return body.getSubject();
}
/**
* 解密token獲取sub纵穿,并反序列化為對象谓媒。
*
* @param token 需要解密的token字符串
* @param clazz sub反序列化的對象類型
*/
public static <T> T parseJWT2Sub(String token, Class<T> clazz) {
SecretKey key = obtainKey();
Claims body = Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody();
return JSON.parseObject(body.getSubject(), clazz);
}
public static void main(String[] args) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("username", "xiaopang");
jsonObject.put("host", "127.0.0.1");
String token = JwtUtils.createJWT(jsonObject.toJSONString());
System.out.println(token);
//解析token
Claims claims = parseJWT(token);
String subject = claims.getSubject();
System.out.println(subject);
//base64 payload解析
String payload="eyJqdGkiOiJqdGkteHAiLCJpYXQiOjE1NjM5NDcxMTAsInN1YiI6IntcImhvc3RcIjpcIjEyNy4wLjAuMVwiLFwidXNlcm5hbWVcIjpcInhpYW9wYW5nXCJ9IiwiZXhwIjoxNTYzOTU0MzEwfQ";
//org.apache.shiro.codec.Base64
System.out.println("payload的base64解密:"+new String(Base64.decode(payload)));
//base64 header解析
String header="eyJhbGciOiJIUzI1NiJ9";
System.out.println("header的base64解密:"+new String(Base64.decode(header)));
}
}
解析結(jié)果
eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJqdGkteHAiLCJpYXQiOjE1NjM5NDc2OTQsInN1YiI6IntcImhvc3RcIjpcIjEyNy4wLjAuMVwiLFwidXNlcm5hbWVcIjpcInhpYW9wYW5nXCJ9IiwiZXhwIjoxNTYzOTU0ODk0fQ.QibTsAVmwHnnl5D-o6HuvPRhJPgTmgxivSZj36lyEnU
{"host":"127.0.0.1","username":"xiaopang"}
payload的base64解密:{"jti":"jti-xp","iat":1563947110,"sub":"{\"host\":\"127.0.0.1\",\"username\":\"xiaopang\"}","exp":1563954310
header的base64解密:{"alg":"HS256"}
附錄:
1. 重放攻擊
重放攻擊是攻擊者獲取客戶端發(fā)送給服務(wù)器端的包句惯,不做修改抢野,原封不動的發(fā)送給服務(wù)器用來實現(xiàn)某些功能各墨。比如說客戶端發(fā)送給服務(wù)器端一個包的功能是查詢某個信息,攻擊者攔截到這個包恃轩,然后想要查詢這個信息的時候黎做,把這個包發(fā)送給服務(wù)器,服務(wù)器就會做相應(yīng)的操作引几,返回查詢的信息伟桅。