JWT总结

发布于:2024-07-11 ⋅ 阅读:(15) ⋅ 点赞:(0)

JWT(JSON Web Tokens)是一种用于在双方之间安全传输信息的简洁的、URL安全的令牌标准。以下是关于JWT的结构、作用、优点以及可能出现的问题的详细解答:

一、JWT的结构

JWT的结构由三个部分组成,它们通过.(点)分隔:

  1. 头部(Header):头部通常由两部分组成,第一部分是令牌的类型,即JWT;第二部分是使用的加密算法,比如HMAC SHA256或RSA等。头部信息经过Base64编码后形成JWT的第一部分。
  2. 载荷(Payload):载荷部分包含了要传输的声明(claims)。声明是关于实体(通常是用户)和其他数据的声明。声明分为三种类型:注册声明(如iss发行人、exp过期时间等)、公共声明(自定义的声明,如user_idusername等)和私有声明(不建议定义与注册声明和公共声明相同的名称)。载荷也使用Base64编码进行序列化。
  3. 签名(Signature):签名部分是对头部和载荷进行签名生成的,用于验证消息的完整性和来源。签名的生成需要使用密钥,只有拥有密钥的一方才能对消息进行签名和验证。签名部分同样经过Base64编码。

二、JWT的作用

JWT的主要作用是作为一种安全的令牌,在网络应用的各方之间传递信息,确保信息的完整性和真实性。具体来说,JWT的作用包括:

  1. 身份验证:JWT可以作为身份验证的凭证,用于验证用户的身份,确保用户有权访问受保护的资源或执行特定操作。
  2. 信息交换:JWT可以在各方之间安全地传输信息,无需在服务器端保存会话信息,简化了服务器的架构并提高了系统的可扩展性。
  3. 授权:JWT可以包含用户的角色、权限等信息,用于授权用户对特定资源或操作的访问权限。
  4. 单点登录(SSO):JWT可以用于实现单点登录系统,用户只需在一个系统上登录,就可以在其他所有相互信任的系统上获得授权而无需重新登录。

三、JWT的优点

JWT的优点主要包括:

  1. 简单轻量:JWT使用简单的JSON格式,易于理解和使用。它是一种轻量级的身份验证和授权方案,适用于不同类型的应用程序。
  2. 分布式和无状态:由于JWT包含了所有必要的信息,服务器不需要在数据库中存储会话信息,也无需在集群中共享会话状态。这使得应用程序可以轻松地进行水平扩展。
  3. 跨域支持:JWT可以通过HTTP头或URL参数发送,因此可以轻松地跨域传输,并且没有额外的设置或配置需要。
  4. 安全性:JWT使用数字签名或加密机制来验证数据的完整性和安全性。服务器可以使用密钥对令牌进行签名,并在每次请求中进行验证,确保令牌不被篡改或伪造。
  5. 可扩展性:JWT是基于标准的JSON格式,因此可以通过添加自定义字段来轻松地扩展令牌的功能,以满足特定的应用程序需求。

四、JWT可能出现的问题

尽管JWT具有许多优点,但在使用过程中也可能会出现一些问题,主要包括:

  1. 令牌体积大:如果JWT中包含了大量的声明信息,那么生成的令牌体积可能会相对较大,这可能会增加网络传输的负担。需要权衡Token大小和网络传输性能,并可能考虑使用压缩算法或分段传输等方式来减小Token的大小。
  2. 令牌安全性问题
    • 敏感信息泄露:由于JWT在客户端可以被解码(尽管签名部分无法伪造),因此不建议在JWT中存储敏感信息。如果必须存储敏感信息,应确保使用HTTPS等安全协议来保护JWT的传输。
    • 令牌篡改:如果JWT被中间人截获并篡改,那么服务器在验证JWT时可能会受到欺骗。因此,应确保使用安全的算法和密钥来生成和验证JWT。
    • 密钥管理:密钥的安全管理是JWT安全性的关键。如果密钥被泄露,那么攻击者可以伪造JWT进行攻击。因此,需要采取适当的密钥管理措施来保护密钥的安全。
  1. 令牌无法撤销:JWT一旦生成并发送给客户端,就无法被撤销。如果令牌被盗取或泄露,攻击者可以一直使用该令牌进行访问,直到令牌过期。为了解决这个问题,可以设置较短的过期时间,并在服务器端记录Token的黑名单或使用JWT撤销列表等方式来实现注销。
  2. 性能问题:在高负载系统中,验证和解析JWT可能会消耗大量内存和CPU资源。为了优化性能,可以考虑对JWT的验证和解析过程进行优化,例如使用缓存机制来存储已验证的JWT等。
  3. CSRF攻击:在某些情况下,JWT可能会被用于认证CSRF(跨站请求伪造)攻击。为了防止此类攻击,需要采取适当的CSRF保护措施

五、demo

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Calendar;
import java.util.Map;

/**
 * Jwt 工具类
 */
public class JwtUtils {

    //自定义密钥
    private static final String SIGN = "qwer";

    /**
     * 生成 Token
     * @param map 自定义的载荷数据
     * @return 返回 Token
     */
    public static String createToken(Map<String, String> map) {
        //1.设置过期时间(默认 1 天过期)
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.MINUTE, 1);

        //2.创建 jwt builder,添加自定义的载荷数据
        JWTCreator.Builder builder = JWT.create();
        for(Map.Entry<String, String> entry : map.entrySet()) {
            builder.withClaim(entry.getKey(), entry.getValue());
        }

        //3.生成 Token
        String token = builder.withExpiresAt(instance.getTime()) //过期时间
                .sign(Algorithm.HMAC256(SIGN));// sign
        return token;
    }

    /**
     * 验证 Token 合法性
     * @param token
     */
    public static boolean checkToken(String token) {
        try {
            JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }


    /**
     * 获取 Token 信息
     * @param token
     * @return
     */
    public static DecodedJWT getTokenInfo(String token) {
        DecodedJWT verify = JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
        return verify;
    }

}
    @GetMapping("/login")
    @ResponseBody
    public String login(String name) {
        //2.校验密码
        User dbUser = dbMap.get(name);
        if (dbUser == null) {
            return "not found user";
        }
        HashMap<String, String> map = new HashMap<>();
        map.put("name", name);
        String token = JwtUtils.createToken(map);
        cache.put(token, dbUser);
        return token;
    }

    @GetMapping("/checkToken")
    @ResponseBody
    public Boolean checkToken(String token) {
        System.out.println("checkToken---------------token" + token);
        return JwtUtils.checkToken(token);
    }


    @GetMapping("/logout")
    @ResponseBody
    public void logout(String token) {
        System.out.println("logout---------------token" + token);
        User user = cache.get(token);
        cache.remove(token);
    }