JWT| 青训营笔记

70 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 14 天

1.0.认证方式

1、基于Session的认证

基于session认证所显露的问题:

Session: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。

扩展性: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。

CSRF (Cross-Site Request Forgery): 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。

2、基于传统token的认证

传统的Token,例如:用户登录成功生成对应的令牌,key为令牌 value:userid,隐藏了数据真实性 ,同时将该token存放到redis中,返回对应的真实令牌给客户端存放。 客户端每次访问后端请求的时候,会传递该token在请求中,服务器端接收到该token之后,从redis中查询如果存在的情况下,则说明在有效期内,如果在Redis中不存在的情况下,则说明过期或者token错误。

1.1.JWT是什么

JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

是目前流行的跨域认证解决方案,一种基于JSON的、用于在网络上声明某种主张的令牌(token)。

该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。

JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

原理:jwt验证方式是将用户信息通过加密生成token,每次请求服务端只需要使用保存的密钥验证token的正确性,不用再保存任何session数据了,进而服务端变得无状态,容易实现拓展。

官网:jwt.io/

1.2. 什么时候你应该用JWT

下列场景中使用JSON Web Token是很有用的:

  • Authorization (授权) : 这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。
  • Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWTs可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。

Spring Boot使用

依赖引入

<dependency>
   <groupId>com.auth0</groupId>
   <artifactId>java-jwt</artifactId>
   <version>4.3.0</version>
</dependency>

具体使用

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 com.infinite.tikfake.entity.User;
import lombok.extern.slf4j.Slf4j;
​
import java.util.Date;
​
@Slf4j
public class JwtUtil {
​
​
    //指定一个token过期时间(毫秒)
    private static final long EXPIRE_TIME = 7 * 24 * 60 * 60 * 1000;  // 7天
    private static final String JWT_TOKEN_SECRET_KEY = "TikFake";//
​
    public static String createJwtTokenByUser(User user) {
​
        String secret = JWT_TOKEN_SECRET_KEY;
​
        Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
        Algorithm algorithm = Algorithm.HMAC256(secret);    //使用密钥进行哈希
        // 附带username信息的token
        return JWT.create()
                .withClaim("username", user.getName())
                .withExpiresAt(date)  //过期时间
                .sign(algorithm);     //签名算法
    }
​
​
    /**
     * 校验token是否正确
     */
    public static boolean verifyTokenOfUser(String token) {
        log.info("verifyTokenOfUser");
        String secret = JWT_TOKEN_SECRET_KEY;//
​
        //根据密钥生成JWT效验器
        Algorithm algorithm = Algorithm.HMAC256(secret);
        JWTVerifier verifier = JWT.require(algorithm)
                .withClaim("username", getUsername(token))//从不加密的消息体中取出username
                .build();
        try {
            DecodedJWT jwt = verifier.verify(token);
            return true;
        }
        catch (Exception exception){
            return false;
        }
    }
​
    /**
     * 在token中获取到username信息
     */
    public static String getUsername(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("username").asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }
​
    /**
     * 判断是否过期
     */
    public static boolean isExpire(String token) {
        DecodedJWT jwt = JWT.decode(token);
        return jwt.getExpiresAt().getTime() < System.currentTimeMillis();
    }
}