说明
jwt使用 jose4j 实现
准备
maven依赖
<dependency>
<groupId>org.bitbucket.b_c</groupId>
<artifactId>jose4j</artifactId>
<version>0.6.3</version>
</dependency>
依赖jar
- jose4j-0.7.9.jar
- log4j-1.2.17.jar
- slf4j-api-1.7.25.jar
- slf4j-log4j12-1.7.25.jar
- jackson-annotations-2.3.1.jar
- jackson-core-2.3.1.jar
- jackson-databind-2.3.1.jar
java代码
跨域过滤器(重要)
详见:
https://www.malaoshi.top/show_1IX46QGBvoNw.html
CharsetFilter
处理中文乱码的过滤器,详见:
https://www.malaoshi.top/show_1IX40rYGj8Ud.html
JWT(重要)
该类封装了生成 JWT 和 校验 JWT
package top.malaoshi.util;
import java.security.PrivateKey;
import java.util.UUID;
import org.jose4j.json.JsonUtil;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jwk.RsaJwkGenerator;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.NumericDate;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.lang.JoseException;
public class JWT {
private static String keyId = "fa677d525c0e4ee485a61543937794af";
private static String privateKeyStr = "{\"kty\":\"RSA\",\"kid\":\"fa677d525c0e4ee485a61543937794af\",\"alg\":\"RS256\",\"n\":\"ki6NKW2ow53FBjWf21xNGF0v-Fzv9R4-vu5tz7LHz4vZ7TE2Lp7Xx0N2vFIH-HLZPLfAYW35W5iV29sW_MkbhVlh6f0q4AeCIeYrVjBGbcYTK5g-Sb8i9sO78DkGivryKTU4tUnOjqar2bfobwXScFhAgc4-BjIcvZ9V8LEzcAW76he500-sqekXqvYv7LbxIMlbadGEwbBqjscE83hiYjk1KSFrEeNWKP6E0X_cHVGEEGys8IKlBcfwOOCgaJ0sCFxvN3M54V33jSUknFzHAi1qJRsOI87-Fk1oYS-aniQOTfm5y5x1syTIgWEX9JvXCQgTxjp2kMItuoL2G2faoQ\",\"e\":\"AQAB\",\"d\":\"OmSUCN-AEZv9LwzOrW6CcWAQIHLne4-4WsadYOE2hcaEqAYHcboL0dI2JOXTv0AJXQK9u22VtSwPeMJcvV-MOclJnpF9xf3Z0rbByuz_xSvhToHDJ-xNCCuJ8FynK28wuptC6s_vzfXwIckf9PFrbWsjYXbEOe9cobZ7Ould9boHkEq-x0eKkDFfcQbevuQeM54FCk5FGl_D4wpbuqudZiT8FYCJPO7m-MTnND6xzdEVuApuQUPCjAin34ygp9QPk2VfQaM1z-ZHchTaXtsv5gyLwu3bqA91vugmrtGuSBG8pFaN6mYL_ivt1Q_7QDLqXZ5_WBhkoXO5DZ4-6ApLvQ\",\"p\":\"58fTzOMjU1zJ42ZdUkJjSpRlZXdlWx3ILStrvVw7kCHH1iBZ4Lckd7SdqJ9DUfdIBFTNPaAKfDqpoz7fWOUZyyUpy7_8qaDagFi53pAjuCGUuKnOV5hbSPBIOvGi9iTHwLNz6Om5eRV6lBXoTRJgT7g8u88HeSyl29RmYt-N7rs\",\"q\":\"oXTxPC0BFDxpFjk2Jpuj6hBkioD3Kiv7HEJjxRoOL3TEAAc3811f7V7bO5FWK2L_2g8I5YiZhsyh0SyxjPBfuIzcbAfGSTAsFqKiIOGZ7fqiFTOV319FJIkcMOrT2dbvGxNeGgTJpWB1vcX6C8i3ytzPFto1H9Bl3xAPVFflHFM\",\"dp\":\"RU-eaLCryav_u37K_WRY6N6Di9oudxbq24cWiuPf8_QGHGREPEzIHPvoAZrOuN4nrRPm5DzNpeStAeI1TBIGqpcMbp-U4Oz3KlZeDs4vwEpafPZafBtVgPRJxUapIs5Q5bFEQixSiIEBzPLYKuQJ5Q0FLGx2oafWWWykyYBsoy0\",\"dq\":\"SS2_yQ581rcqyi_UI1uXx5b2evBJFowonH5ayhMtKsU5sOmUqnE_8U50_2K4M6IDZMo7tg1byIUnMq-XKdIpEHSH008SyElVMk00PsMCCaL3o7Rl0YBUzmJ2rJVCwBFy_kqg9BoHazV1KDZ7RqwK4Z-DHVB5k5nZEmktCYVtCpE\",\"qi\":\"jDbVP3xGaYx2wgAGJT7KNjdoC4dYbl5ajp9saRCgAih3TYr1CjZSrLRm9L90UukgwajU0pHaffP74epVx0RBhnS5GtZhCoGitpLwYSDkZ9qTMTVnFPHrg6M1OYEEXjZ_UKlnYCrzrDe6tfHcO1UTzMzsuOgHjRAV7IS6ImfzwVw\"}";
private static String publicKeyStr = "{\"kty\":\"RSA\",\"kid\":\"fa677d525c0e4ee485a61543937794af\",\"alg\":\"RS256\",\"n\":\"ki6NKW2ow53FBjWf21xNGF0v-Fzv9R4-vu5tz7LHz4vZ7TE2Lp7Xx0N2vFIH-HLZPLfAYW35W5iV29sW_MkbhVlh6f0q4AeCIeYrVjBGbcYTK5g-Sb8i9sO78DkGivryKTU4tUnOjqar2bfobwXScFhAgc4-BjIcvZ9V8LEzcAW76he500-sqekXqvYv7LbxIMlbadGEwbBqjscE83hiYjk1KSFrEeNWKP6E0X_cHVGEEGys8IKlBcfwOOCgaJ0sCFxvN3M54V33jSUknFzHAi1qJRsOI87-Fk1oYS-aniQOTfm5y5x1syTIgWEX9JvXCQgTxjp2kMItuoL2G2faoQ\",\"e\":\"AQAB\"}";
public static long accessTokenExpirationTime = 60 * 60 * 24;
//jws创建token
public String createToken(String account) {
try {
//Payload
JwtClaims claims = new JwtClaims();
claims.setGeneratedJwtId();
claims.setIssuedAtToNow();
//expire time
NumericDate date = NumericDate.now();
date.addSeconds(accessTokenExpirationTime);
claims.setExpirationTime(date);
claims.setNotBeforeMinutesInThePast(1);
claims.setSubject("YOUR_SUBJECT");
claims.setAudience("YOUR_AUDIENCE");
//添加自定义参数,必须是字符串类型
claims.setClaim("account", account);
//jws
JsonWebSignature jws = new JsonWebSignature();
//签名算法RS256
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
jws.setKeyIdHeaderValue(keyId);
jws.setPayload(claims.toJson());
/*
RsaJsonWebKey jwk = null;
try {
jwk = RsaJwkGenerator.generateJwk(2048);
} catch (JoseException e) {
e.printStackTrace();
}
jwk.setKeyId(keyId); */
//PrivateKey privateKey = jwk.getPrivateKey();
PrivateKey privateKey = new RsaJsonWebKey(JsonUtil.parseJson(privateKeyStr)).getPrivateKey();
jws.setKey(privateKey);
//get token
String idToken = jws.getCompactSerialization();
return idToken;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* jws校验token
*
* @param token
* @return 返回 用户账号
* @throws JoseException
*/
public String verifyToken(String token) {
try {
JwtConsumer consumer = new JwtConsumerBuilder()
.setRequireExpirationTime()
.setMaxFutureValidityInMinutes(5256000)
.setAllowedClockSkewInSeconds(30)
.setRequireSubject()
//.setExpectedIssuer("")
.setExpectedAudience("YOUR_AUDIENCE")
/*
RsaJsonWebKey jwk = null;
try {
jwk = RsaJwkGenerator.generateJwk(2048);
} catch (JoseException e) {
e.printStackTrace();
}
jwk.setKeyId(keyId); */
//.setVerificationKey(jwk.getPublicKey())
.setVerificationKey(new RsaJsonWebKey(JsonUtil.parseJson(publicKeyStr)).getPublicKey())
.build();
JwtClaims claims = consumer.processToClaims(token);
if (claims != null) {
System.out.println("认证通过!");
String account = (String) claims.getClaimValue("account");
System.out.println("token payload携带的自定义内容:用户账号account=" + account);
return account;
}
} catch (JoseException e) {
e.printStackTrace();
} catch (InvalidJwtException e) {
e.printStackTrace();
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 创建jwk keyId , 公钥 ,秘钥
*/
public static void createKeyPair(){
String keyId = UUID.randomUUID().toString().replaceAll("-", "");
RsaJsonWebKey jwk = null;
try {
jwk = RsaJwkGenerator.generateJwk(2048);
} catch (JoseException e) {
e.printStackTrace();
}
jwk.setKeyId(keyId);
//采用的签名算法 RS256
jwk.setAlgorithm(AlgorithmIdentifiers.RSA_USING_SHA256);
String publicKey = jwk.toJson(RsaJsonWebKey.OutputControlLevel.PUBLIC_ONLY);
String privateKey = jwk.toJson(RsaJsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
System.out.println("keyId="+keyId);
System.out.println();
System.out.println("公钥 publicKeyStr="+publicKey);
System.out.println();
System.out.println("私钥 privateKeyStr="+privateKey);
}
public static void main(String[] args){
// createKeyPair();
JWT jwt=new JWT();
String token=jwt.createToken("李雷");
System.out.println(token);
String username=jwt.verifyToken(token);
System.out.println("username:"+username);
}
}
JsonResult
详见:
https://www.malaoshi.top/show_1IX2rn0kwlcB.html
UserInfo
package top.malaoshi.bean;
public class UserInfo {
private String id;
private String username;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
登录 servlet(重要)
登录成功后,将 用户id、用户名、转成 JWT,并返回给前端
package top.malaoshi.servlet;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.jose4j.json.JsonUtil;
import top.malaoshi.bean.JsonResult;
import top.malaoshi.bean.UserInfo;
import top.malaoshi.util.JWT;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
UserInfo ui=new UserInfo();
ui.setId("100");
ui.setUsername("李雷");
ObjectMapper om=new ObjectMapper();
String uiJSON=om.writeValueAsString(ui);
System.out.println(uiJSON);
JWT jwt=new JWT();
String token=jwt.createToken(uiJSON);
System.out.println(token);
String json=JsonResult.success("登录成功",token);
resp.getWriter().write(json);
// resp.setHeader("token",token);
}
}
测试通过header获取用户信息 TestJWT
前端发请求,从请求 header 中取出 auth 数据(JWT格式),并解析出数据
package top.malaoshi.servlet;
import com.fasterxml.jackson.databind.ObjectMapper;
import top.malaoshi.bean.UserInfo;
import top.malaoshi.util.JWT;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/testJWT")
public class TestJWT extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String auth=req.getHeader("auth");
System.out.println("auth:"+auth);
JWT jwt=new JWT();
String authText=jwt.verifyToken(auth);
System.out.println("authText:"+authText);
ObjectMapper om=new ObjectMapper();
//解析出UserInfo对象
UserInfo ui=om.readValue(authText, UserInfo.class);
System.out.println();
}
}
html代码
需要准备 axios.js
在另一个服务器中,实现跨域
注意:页面不要放在 tomcat 下,否则就不是跨域了
登录页面
跨域访问 登录Servlet,从返回数据中得到 JWT,然后存储到 localStorage
注意:现在是 多页面应用,需要用 localStorage
创建页面 login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="js/axios.js"></script>
<title>Document</title>
</head>
<body>
<script>
axios.post('http://localhost:8080/web_war_exploded/login', {
})
.then(function (res) {
console.log(res.data);
console.log(res.data.data)
localStorage.setItem('auth', res.data.data);
console.log(localStorage.getItem('auth'))
})
.catch(function (error) {// 请求失败处理
console.log(error);
});
</script>
</script>
</body>
</html>
测试页面
该页面跨域访问 testJWTServlet,axios 发请求时,从 localStorage 中取出 JWT,然后将 JWT 放入头信息中
创建页面 testJWT.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="js/axios.js"></script>
<title>Document</title>
</head>
<body>
<script>
console.log(localStorage.getItem('auth'))
//post方式
// axios.post('http://localhost:8080/web_war_exploded/testJWT', {
// },{
// headers: {
// 'auth': localStorage.getItem('auth')
// }
// })
// .then(res => {
// console.log(res);
// })
// .catch(erro => {// 请求失败处理
// console.log(error);
// });
//get方式
axios.get('http://localhost:8080/web_war_exploded/testJWT',{
headers: {
'auth': localStorage.getItem('auth')
}
})
.then(res => {
console.log(res);
})
.catch(error => {// 请求失败处理
console.log(error);
});
</script>
</script>
</body>
</html>