jwt实现登录验证
jwt实现登录验证
一、什么是jwt
JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息。
简要说明:前段传验证信息到后端,后端验证通过,返回一个对象,只不过这个对象是加密的,每次请求的时候,请求头带上token,里面封装了对象的信息,我们只需要用拦截器进行拦截,解析token,后端就可以知道是谁登录。
二、为什么需要jwt
cookie和session的缺点:
- session都存储在服务端内存
- 集群环境中需要额外处理。ip_hash,分布式,session......
user -> u1,u2,u3 - csrf:Cross-site request forgery, cookie被截获后可能发生跨站点请求伪造
- cookie的跨域(前后端分离)读写不方便
三、jwt的组成
一个 JWT 实际上就是一个字符串,它由三部分组成,头部、载荷与签名
1.头部header
{
"alg": "HS256",
"typ": "JWT"
}
alg: 签名算法,常用HS256,具体支持可参考jwt官网: https://jwt.io/
typ: token令牌的类型,统一写成JWT
2.payload 负载
也是一个 json 的对象,下面是官方字段:
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
当然也可以定义一些其他字段:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
由于jwt默认不是加密的,所以不要保存敏感信息
3.Signature 签名
Signature 部分是对前两个部分的签名,防止数据篡改。
首先需要制定一个密匙(secret),这个密匙只有服务器才知道,不能泄露给用户。
然后使用Header里面指定的签名算法,按照下面的公式产生签名。
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
算出签名后,把header,payload,signature之间用 ( . )隔开,就可以返给用户
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
四、jwt的实现方式
常用方式:
java-jwt
引入Maven依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.1</version>
</dependency>
获取token:
final String key = "123abc";
/**
* @description: 测试java-jwt生成token字符串
* @author: gsz
* @date: 2022/3/12 10:36
*/
@Test
public void test1() {
// 设置过期时长
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND,120);
JWTCreator.Builder builder = JWT.create()
//payload内容,由多个Claim组成
.withClaim("userId", 1L)
.withClaim("userName", "公孙瓒")
.withExpiresAt(calendar.getTime());
String token = builder.sign(Algorithm.HMAC256(key));
System.out.println(token);
}
运行结果:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6IuWFrOWtmeeTkiIsImV4cCI6MTY0NzA1NDMwNSwidXNlcklkIjoxfQ.AZ0REjUE5ecDtfY9_876bDqcE07zwrodLhjPyvX1d9I
校验:
@Test
public void textVerify(){
String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6IuWFrOWtmeeTkiIsImV4cCI6MTY0NzA1NDMwNSwidXNlcklkIjoxfQ.AZ0REjUE5ecDtfY9_876bDqcE07zwrodLhjPyvX1d9I";
DecodedJWT verify = null;
try {
verify = JWT.require(Algorithm.HMAC256(key)).build().verify(token);
} catch (SignatureVerificationException e){
e.printStackTrace();
log.info("--> 签名不一致 <--");
} catch (TokenExpiredException e){
e.printStackTrace();
log.info("--> 令牌过期 <--");
} catch (AlgorithmMismatchException e){
e.printStackTrace();
log.info("--> 签名算法不匹配 <--");
} catch (InvalidClaimException e){
e.printStackTrace();
log.info("--> payload不可用 <--");
} catch (Exception e){
e.printStackTrace();
log.info("--> 校验失败 <--");
}
if (verify != null){
// 获取payload信息,类型不匹配获取不到
Long userId = verify.getClaim("userId").asLong();
String userName = verify.getClaim("userName").asString();
log.info("{},{}",userId,userName);
}
}
控制台打印结果:
1,公孙瓒
jjwt
引入Maven依赖
<!-- jwt实现方式之jjwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
获取token
final String key = "123abc";
@Test
public void testGenerateToken(){
//过期时间
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND,120);
//创建payload的私有声明
Map<String, Object> claims = new HashMap<>();
claims.put("userId", 2L);
claims.put("userName", "测试");
JwtBuilder jwtBuilder = Jwts.builder()
.setClaims(claims)
.setExpiration(calendar.getTime())
.signWith(SignatureAlgorithm.HS256, key);
String compact = jwtBuilder.compact();
System.out.println(compact);
}
运行结果:
eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6Iua1i-ivlSIsImV4cCI6MTY0NzA1NzU3NCwidXNlcklkIjoyfQ.YMXW80A8MK7MCVGSmzTY0kpTsxe3kCSOw6CwuTPrXuM
校验token
@Test
public void textVerify(){
//刚刚获得的token
String token = "ey...";
Claims claims = Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(token).getBody();
Long userId = claims.get("userId", Long.class);
String userName = claims.get("userName", String.class);
log.info("{},{}",userId,userName);
}
控制台打印结果:
2,测试
后端工具类
根据 jjwt 进行封装
package common.util;
import com.alibaba.fastjson.JSON;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import project.entity.LoginUser;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
/**
* Token工具类
* @author gsz
* @date 2022/03/11 15:27
*/
public class TokenUtil {
public static final Logger log = LoggerFactory.getLogger(TokenUtil.class);
// 私钥设置
public static final String TOKEN_SECRET = "6gahgadg80asd";
/**
* @description: 生成token
* @author: gsz
* @date: 2022/3/12 13:31
* @param loginUser
* @return
*/
public static String getToken(LoginUser loginUser){
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND,120);
//创建payload的私有声明
Map<String, Object> claims = new HashMap<>();
claims.put("userInfo", JSON.toJSONString(loginUser));
JwtBuilder jwtBuilder = Jwts.builder()
.setClaims(claims)
.setExpiration(calendar.getTime())
.signWith(SignatureAlgorithm.HS256, TOKEN_SECRET);
String token = jwtBuilder.compact();
return token;
}
/**
* @description: 校验token
* @author: gsz
* @date: 2022/3/12 13:36
* @param tokenToBeVerify
* @return
*/
public static Claims verify(String tokenToBeVerify){
Claims claims = null;
try {
claims = Jwts.parser()
.setSigningKey(TOKEN_SECRET)
.parseClaimsJws(tokenToBeVerify).getBody();
} catch (SignatureVerificationException e){
e.printStackTrace();
log.info("--> 签名不一致 <--");
} catch (TokenExpiredException e){
e.printStackTrace();
log.info("--> 令牌过期 <--");
} catch (AlgorithmMismatchException e){
e.printStackTrace();
log.info("--> 签名算法不匹配 <--");
} catch (Exception e){
e.printStackTrace();
log.info("--> 校验失败 <--");
}
return claims;
}
/**
* @description: 解析
* @author: gsz
* @date: 2022/3/12 13:39
* @param claims
* @return
*/
public static LoginUser parser(Claims claims){
return JSON.parseObject(claims.get("userInfo", String.class), LoginUser.class);
}
}
简单测试一下
@Test
public void test(){
LoginUser loginUser = new LoginUser(1L, "公孙瓒", "123456");
String token = TokenUtil.getToken(loginUser);
Claims verify = TokenUtil.verify(token);
LoginUser parser = TokenUtil.parser(verify);
System.out.println(token);
System.out.println(parser.toString());
}
控制台打印结果:
eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySW5mbyI6IntcInBhc3N3b3JkXCI6XCIxMjM0NTZcIixcInVzZXJJZFwiOjEsXCJ1c2VyTmFtZVwiOlwi5YWs5a2Z55OSXCJ9IiwiZXhwIjoxNjQ3MDY1NzM1fQ.Y9lLQhJlzqRkFNLDXZh0_VSePVd9YLrwsyv7k67364U
LoginUser{userId=1, userName='公孙瓒', password='123456'}