Session和JWT有什么区别?_服务器端

目录

  1. 引言
  2. 什么是JWT?
  3. JWT优点分析
  4. JWT基本使用
  • 3.1 添加HuTool框架依赖
  • 3.2 生成Token
  • 3.3 验证和解析Token
  • 3.4 代码实战
  1. JWT实现原理
  2. Session VS JWT
  3. 总结

引言

在用户和服务器之间建立认证状态是Web开发中常见的需求。SessionJWT(JSON Web Token)是两种常用的机制,但它们在工作原理、存储方式和安全性等方面存在差异。

什么是JWT?

JWT是一种开放标准(RFC 7519),用于在网络上安全传输信息的简洁、自包含的方式。它通常被用于身份验证和授权机制。JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。

  • 头部(Header):包含了关于生成该JWT的信息以及所使用的算法类型。
  • 载荷(Payload):包含了要传递的数据,例如身份信息和其他附属数据。JWT官方规定了7个字段,可供使用:
  • iss (Issuer):签发者。
  • sub (Subject):主题。
  • aud (Audience):接收者。
  • exp (Expiration time):过期时间。
  • nbf (Not Before):生效时间。
  • iat (Issued At):签发时间。
  • jti (JWT ID):编号。
  • 签名(Signature):使用密钥对头部和载荷进行签名,以验证其完整性。

JWT优点分析

相较于传统的基于会话(Session)的认证机制,JWT具有以下优势:

  • 无需服务器存储状态:服务器无需存储任何会话状态信息,所有的认证和授权信息都包含在JWT中。
  • 跨域支持JWT可以轻松地在多个域之间进行传递和使用,实现跨域授权。
  • 适应微服务架构:在微服务架构中,很多服务是独立部署并且可以横向扩展的,这就需要保证认证和授权的无状态性。
  • 自包含JWT包含了认证和授权信息,以及其他自定义的声明,这些信息都被编码在JWT中。
  • 扩展性JWT可以被扩展和定制,可以按照需求添加自定义的声明和数据。

JWT基本使用

在Java开发中,可以借助JWT工具类来方便的操作JWT,例如HuTool框架中的JWTUtil

3.1 添加HuTool框架依赖

pom.xml中添加以下依赖:

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.16</version>
</dependency>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
3.2 生成Token
Map<String, Object> map = new HashMap<String, Object>() {
    private static final long serialVersionUID = 1L;
    {
        put("uid", Integer.parseInt("123")); // 用户ID
        put("expire_time", System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 15); // 过期时间15天
    }
};
String token = JWTUtil.createToken(map, "服务器端秘钥".getBytes());
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
3.3 验证和解析Token

验证Token的示例代码如下:

String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2MjQwMDQ4MjIsInVzZXJJZCI6MSwiYXV0aG9yaXRpZXMiOlsiUk9MRV_op5LoibLkuozlj7ciLCJzeXNfbWVudV8xIwiUk9MRV_op5LoibLkuIDlj7ciLCJzeXNfbWVudV8yIl0sImp0aSI6ImQ0YzVlYjgwLTA5ZTctNGU0ZC1hZTg3LTVkNGI5M2FhNmFiNiIsImNsaWVudF9pZCI6ImhhbmR5LXNob3AifQ.aixF1eKlAKS_k3ynFnStE7-IRGiD5YaqznvK2xEjBew";
JWTUtil.verify(token, "123456".getBytes());
  • 1.
  • 2.

解析Token的示例代码如下:

String rightToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9.U2aQkC2THYV9L0fTN-yBBI7gmo5xhmvMhATtu8v0zEA";
final JWT jwt = JWTUtil.parseToken(rightToken);
jwt.getHeader(JWTHeader.TYPE);
jwt.getPayload("sub");
  • 1.
  • 2.
  • 3.
  • 4.
3.4 代码实战

在登录成功之后,生成Token的示例代码如下:

// 登录成功,使用 JWT 生成 Token
Map<String, Object> payload = new HashMap<String, Object>() {
    private static final long serialVersionUID = 1L;
    {
        put("uid", userinfo.getUid());
        put("manager", userinfo.getManager());
        // JWT 过期时间为 15 天
        put("exp", System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 15);
    }
};
String token = JWTUtil.createToken(payload, AppVariable.JWT_KEY.getBytes());
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

例如在Spring Cloud Gateway网关中验证Token的实现代码如下:

import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.example.common.AppVariable;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * 登录过滤器(登录判断)
 */
@Component
public class AuthFilter implements GlobalFilter, Ordered {
    // 排除登录验证的 URL 地址
    private String[] skipAuthUrls = {"/user/add", "/user/login"};

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 当前请求的 URL
        String url = exchange.getRequest().getURI().getPath();
        for (String item : skipAuthUrls) {
            if (item.equals(url)) {
                // 继续往下走
                return chain.filter(exchange);
            }
        }
        ServerHttpResponse response = exchange.getResponse();
        // 登录判断
        List<String> tokens =
                exchange.getRequest().getHeaders().get(AppVariable.TOKEN_KEY);
        if (tokens == null || tokens.size() == 0) {
            // 当前未登录
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        // token 有值
        String token = tokens.get(0);
        // JWT 效验 token 是否有效
        boolean result = false;
        try {
            result = JWTUtil.verify(token, AppVariable.JWT_KEY.getBytes());
        } catch (Exception e) {
            result = false;
        }
        if (!result) {
            // 无效 token
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        } else { // 判断 token 是否过期
            final JWT jwt = JWTUtil.parseToken(token);
            // 得到过期时间
            Object expObj = jwt.getPayload("exp");
            if (expObj == null) {
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                return response.setComplete();
            }
            long exp = Long.parseLong(expObj.toString());
            if (System.currentTimeMillis() > exp) {
                // token 过期
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                return response.setComplete();
                        }
    }
    return chain.filter(exchange);
}

@Override
public int getOrder() {
    // 值越小越早执行
    return 1;
}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.

JWT实现原理

JWT本质上是将秘钥存放在服务器端,并通过某种加密手段进行加密和验证的机制。加密签名=某加密算法(header+payload+服务器端私钥),因为服务端私钥别人不能获取,所以JWT能保证自身其安全性。

Session VS JWT

SessionJWT的区别主要有以下几点:

  • 工作原理不同Session机制依赖于服务器端的存储,而JWT是一种无状态的认证机制,它通过在客户端存储令牌(Token)来实现认证。
  • 存储方式不同Session信息存储在服务器端,而JWT信息存储在客户端。
  • 有效期和灵活性不同Session的有效期通常由服务器控制,而JWT的有效期可以在令牌生成时设置,并且可以在客户端进行缓存和重复使用。

总结

SessionJWT各有优势和适用场景。Session适合于需要频繁更改用户状态和服务器资源充足的场景,而JWT适合于分布式系统、微服务架构以及需要跨域认证的场景。选择合适的认证机制,可以提高系统的安全性、可扩展性和维护性。