JWT总结

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);
    }
相关推荐
whysqwhw43 分钟前
js之Promise
前端
恋猫de小郭4 小时前
Flutter 3.35 发布,快来看看有什么更新吧
android·前端·flutter
从后端到QT5 小时前
RTCP详解
服务器·音视频·实时音视频·rctp
chinahcp20085 小时前
CSS保持元素宽高比,固定元素宽高比
前端·css·html·css3·html5
gnip6 小时前
浏览器跨标签页通信方案详解
前端·javascript
gnip7 小时前
运行时模块批量导入
前端·javascript
华纳云IDC服务商7 小时前
通过限制网络访问来降低服务器被攻击风险的方法
运维·服务器·网络
霸气十足+拼命+追梦少年7 小时前
vscode远程服务器出现一直卡在正在打开远程和连接超时解决办法
服务器·ide·vscode
hyy27952276847 小时前
企业级WEB应用服务器TOMCAT
java·前端·tomcat
逆风优雅7 小时前
vue实现模拟 ai 对话功能
前端·javascript·html