1、基于session的用户认证
在传统的用户登录认证中,因为http是无状态的,所以都是采用session方式。用户登录成功,服务端会保证一个session,当然会给客户端一个sessionId,客户端会把sessionId保存在cookie中,每次请求都会携带这个sessionId。
cookie+session这种模式通常是保存在内存中,而且服务从单服务到多服务会面临的session共享问题,随着用户量的增多,开销就会越大。而JWT不是这样的,只需要服务端生成token,客户端保存这个token,每次请求携带这个token,服务端认证解析就可。
2、JWT
JWT(json web token)是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。
JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。比如用在用户登录上。
3、JWT生成Token后的样子
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzQwNjEzOTUsInVzZXJJZCI6IjEwMjI2NDk3IiwiaWF0IjoxNTc0MDYxMzM1fQ.VRh4e0JbCemc6xlJ3bs6lG-4s2zXrXlQDZ24sCMiIWI
该token放在response Headers或者返回的json里面均可。同样的,前端放在request 或者请求参数里面均可。
4、JWT的优点
1、因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。
2、payload部分,JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
3、便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。它不需要在服务端保存会话信息, 所以它易于应用的扩展
5、JWT的构成
这一部分自行百度。第一部分我们称它为头部(header),第二部分我们称其为载荷(payload),第三部分是签证(signature)。
6、JWT的实现
1.编写一个工具类,用户生成、验证token等
package cn.boe.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.io.UnsupportedEncodingException;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JWTUtils {
// 过期时间5分钟5*60*1000
private static final long EXPIRE_TIME = 24*60*60*1000;
public static String SECRET = "lvyyTestToken";
/**
* 校验token是否正确
* @param token 密钥
* //@param pwd 用户的密码
* @return 是否正确
*/
public static boolean verify(String token, String userId,String pwd) {
try {
Algorithm algorithm = Algorithm.HMAC256(pwd+SECRET);
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("userId", userId)
.build();
DecodedJWT jwt = verifier.verify(token);
return true;
// 验证 token
// JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
} catch (Exception exception) {
return false;
}
}
/**
* 获得token中的信息无需secret解密也能获得
* @return token中包含的用户名
*/
public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username").asString();
} catch (JWTDecodeException e) {
return null;
}
}
public static String getUserId(String token) {
try {
DecodedJWT jwt = JWT.decode(token);//先解码在从里面取值
return jwt.getClaim("userId").asString();
} catch (JWTDecodeException e) {
return null;
}
}
// 是否已过期
public static boolean isExpiration(String token) {
try {
DecodedJWT jwt = JWT.decode(token);//先解码在从里面取值
return jwt.getExpiresAt().before(new Date());
} catch (Exception e) {
return true;
}
}
/**
* 生成签名,5min后过期
* @param userId 用户id
* @param pwd 用户的密码
* @return 加密的token
*/
public static String sign(String userId,String pwd) {
try {
Date nowTime = new Date();
Date lastTime = new Date(System.currentTimeMillis()+EXPIRE_TIME);
//过期时间一分钟
/* Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE,1);
Date lastTime = calendar.getTime();*/
Map<String,Object> map = new HashMap<String,Object>();
map.put("alg","HS256");
map.put("typ","JWT");
Algorithm algorithm = Algorithm.HMAC256(pwd+SECRET);//以用户的密码作为token密钥,或者是自定义的字符串作为密钥
// 附带userId信息
return JWT.create()
.withHeader(map)//header
.withClaim("userId", userId)//将用户名保存到token里面,pyload
.withExpiresAt(lastTime)//将过期时间保存到token里面,过期时间要大于签发时间
.withIssuedAt(nowTime)//设置签发时间
.sign(algorithm);//加密
//String token= JWT.create().withAudience(user.getId())// 将 user id 保存到 token 里面
// .sign(Algorithm.HMAC256(user.getPassword()));// 以 password 作为 token 的密钥
} catch (UnsupportedEncodingException e) {
return null;
}
}
}
2、新建两个注解类,在你需要验证token的方法上或者不需要验证token的方法上添加想要的注解
package cn.boe.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({
ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
boolean required() default true;
}
和
package cn.boe.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({
ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiredToken {
boolean required() default true;
}
3、在controller方法中加入该注解
@RequestMapping(value