java小技能:JWT(json web token)认证实现
2023-06-13 09:16:00 时间
引言
应用场景:
登录授权
,它相比原先的session、cookie来说,更快更安全,跨域也不再是问题。- 传递数据
I. 预备知识
1.1 关键字去空格处理
错误代码 (keyword+"").trim();
会将空转为字符串“null” 正确代码: return StringUtils.isBlank(keyword)?keyword:keyword.trim();
1.2 JWT认证流程
JWT不是一个具体的技术实现,而更像是一种标准。JWT规定了数据传输的结构,一串完整的JWT由三段落组成,每个段落用英文句号连接(.)连接,他们分别是:Header
、Payload
、Signature
,所以,常规的JWT内容格式是这样的:Header.Payload.Signature。并且这一串内容会进行加密;解码就可以看到实际传输的内容。
- Header:加密的方式、type
{
“typ”: “JWT”,
“alg”: “HS256”
}
- Payload:实际传递的参数内容,推荐对payload内容进行加密。
- Signature:签名,用于判断
Header
、Payload
有没有被人篡改;如果被篡改,那么这条JWT将会被视为无效。
内容前面的“Bearer”是固定的,并且还得多加一个空格做分割。
II token组成
生成jwt:sign(Header+Playload+Signature+expiresAt
)
return create(header, claims, JWT_ISSUER, TOKEN_TIMEOUT);
2.1 头部(Header)
头部存储认证类型和加密算法
{
“typ”: “JWT”,
“alg”: “HS256”
}
2.2 有效载荷(Playload)
有效载荷中存放了token的签发者(iss)、签发时间(iat)、过期时间(exp)等以及一些我们需要写进token中的信息
{
"iss": "ios逆向",
"exp": 1638841050,
"iat": 1638840690,
"userId": "1",
"account": "admin"
}
2.3 签名(Signature)
将Header和Playload拼接生成一个字符串,使用HS256算法和我们提供的密钥(secret,服务器自己提供的一个字符串)对str进行加密生成最终的JWT,即我们需要的令牌(token)。
2.4 代码实现:生成token
生成jwt:sign(Header+Playload+Signature+expiresAt
)
Algorithm algorithm = Algorithm.HMAC256(TOKENKEY);//使用HS256算法加密密钥
Date date = new Date(System.currentTimeMillis() + timeout);
JWTCreator.Builder builder = JWT.create()
.withHeader(header)
.withIssuer(issuer)
.withExpiresAt(date);
for (String key : claims.keySet()) {//Playload
builder.withClaim(key, claims.get(key));
}
token = builder.sign(algorithm);
builder.sign
public String sign(Algorithm algorithm) throws IllegalArgumentException, JWTCreationException {
if (algorithm == null) {
throw new IllegalArgumentException("The Algorithm cannot be null.");
} else {
this.headerClaims.put("alg", algorithm.getName());
if (!this.headerClaims.containsKey("typ")) {
this.headerClaims.put("typ", "JWT");
}
String signingKeyId = algorithm.getSigningKeyId();
if (signingKeyId != null) {
this.withKeyId(signingKeyId);
}
return (new JWTCreator(algorithm, this.headerClaims, this.payloadClaims)).sign();
}
}
完整例子
public static final long TOKEN_EXPIRE_TIME = 6 * 60 * 1000;
/**
* 生成token
*
* @param userId 用户ID
* @param account 登录名
* @param userName 用户名称
* @param role 角色ID集合
* @param department 部门ID集合
* @param jwtSecret 生成jwt的秘钥,由网关传入
* @return token
*/
public static String generateToken(Long userId, String account, String userName, List<Long> role, List<Long> department, String jwtSecret) {
Date now = new Date();
// 加密算法
Algorithm algorithm = Algorithm.HMAC256(jwtSecret);
return JWT.create()
//签发人
.withIssuer(ISSUER)
//签发时间
.withIssuedAt(now)
// .withSubject()
//过期时间
.withExpiresAt(new Date(now.getTime() + TOKEN_EXPIRE_TIME))
.withClaim("userId", userId)
.withClaim("userName", userName)
.withClaim("account", account)
.withClaim("role", role)
.withClaim("department", department)
.sign(algorithm);
}
III 验证token
3.1 网关验证token
/**
* 签发人
*/
private static final String ISSUER = "iOS逆向";
/**
* 验证token是否合法
*
* @param token
* @return
* JwtSecret为密钥,随机生成
*/
public static boolean verify(String token) {
try {
// 去掉token前缀
Algorithm algorithm = Algorithm.HMAC256("JwtSecret1");
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer(ISSUER)
.build();
DecodedJWT jwtDecode= verifier.verify(token);
// 存储token信息
Map<String, Claim> claims=jwtDecode.getClaims();
LoginHelper.set(LoginHelper.USER_ID,claims.get(LoginHelper.USER_ID).asString());
return true;
} catch (Exception e) {
log.error("验证token失败 {}", e.getMessage());
}
return false;
}
3.2 使用拦截器验证token
/**
* 拦截器
*/
@Component
@Slf4j
public class JwtHandler implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws IOException {
Map<String,Object> map=new HashMap<>();
String token = Request.getHeaderParam(request, "token");
try {
Algorithm algorithm = Algorithm.HMAC256("JwtSecret");
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer("baidu")
.build();
verifier.verify(token);
return true;
}catch (Exception e){
e.printStackTrace();
map.put("status",false);
map.put("msg","认证失败");
}
//jackson 将map转换为json
String json=new ObjectMapper().writeValueAsString(map);
response.getWriter().println(json);
return false;
}
}
拦截器注册
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
/**
* 注册拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new UserHandlerAdapter())
.addPathPatterns("/user/verify") //拦截请求路径
.excludePathPatterns("/user/login"); //不拦截请求路径
}
}
相关文章
- java用什么软件_Java编程什么软件最好用?
- java使用md5_Java_MD5的使用「建议收藏」
- java后端开发需要什么_从事Java后端开发,要学习哪些知识和技能?[通俗易懂]
- java启动器_JAVA基础:Java 启动器如何查找类
- 说一下java的运行机制_Java运行机制是什么?「建议收藏」
- java工作流_Java 实现简单工作流
- 第一个Java程序—HelloWorld[通俗易懂]
- java uuid 随机数_Java随机数和UUID[通俗易懂]
- java获取服务器路径_JAVA获取服务器路径的方法「建议收藏」
- Java编程开发的过程回顾
- 不会还有程序员不知道跳槽季靠这1700道java面试题就能平淌大厂吧
- 进程调度时间片轮转例题_进程调度算法java
- Java设计模式之外观模式
- JAVA判断当前日期是否是工作日,还是节假日
- java常用字符串操作函数详解编程语言
- Java Web的web.xml文件作用及基本配置详解编程语言
- Java在方法作用域内创建的内部类详解编程语言
- java 线程之concurrent中的常用工具 CyclicBarrier详解编程语言
- Java并发编程之AQS详解编程语言
- Java对象表示方式1:序列化、反序列化和transient关键字的作用详解编程语言
- Java远程登录Linux服务器入门指南(java远程linux)
- 时间 Java中处理Redis过期时间的简易方法(redisjava过期)
- 掌握必备技能:Linux下Java命令的使用(java命令linux)
- 使用Java监听MySQL数据库变化(java监听mysql)
- 实现Java认证让你离Oracle更近一步(java认证oracle)
- Java革命Oracle旗下的程序设计利器(java简介oracle)
- Java模拟Oracle实现稳定数据库性能(java模仿oracle)
- 进行操作学会使用Java玩转Redis(在java中队redis)
- JAVA算法起步之快速排序实例