JWT入门详解

JWT


如果可以的话,请点个小小的赞可以吗?
以下的代码都基于springboot开发

一、什么是JWT

  • WT(JSON WEBTOKEN):JSON网络令牌,JWT是一个轻便的安全跨平台传输格式,定义了一个紧凑的自包含的方式在不同实体之间安全传输信息(JSON格式)。它是在Web环境下两个实体之间传输数据的一项标准。实际上传输的就是一个字符串。广义上讲JWT是一个标准的名称;狭义上JWT指的就是用来传递的那个token字符串

JSON Web Token (JWT),它是目前最流行的跨域身份验证解决方案

二、为什么要使用JWT

  • 为了保护项目之中的数据资源,那么一定就需要采用认证检测机制,于是SpringCloud进行认证处理,就可以使用SpringSecurity 来实现了,但是如果你真的去使用了SpringSecurity进行开发,因为维护的成本实在是太高了。

  • 在后来的时候有很多的开发者开始尝试通过OAuth2统一认证来进行SpringCloud认证与授权服务,这种操作也属于较早期的实现了,这种实现最大的问题在于随着版本的更新会出现代码不稳定的情况,很多的开发者就开始尝试自己去独立的实现认证与授权的操作机制,于是就有了JWT开发技术。

  • 使用JWT最大的特点在于不需要维护所有的数据的状态,同时可以自己包含有过期的时间,以及通过附加数据的形式传送所需要的额外的数据内容((可以保存认证以及授权信息)。
    在WEB开发中需要维护Session的状态,所以如果用户访问量过大,那么必然会出现Session内容过多而导致服务器处理性能下降的惨剧,所以后面就需要引入WEB服务器的集群,但是为了便于服务器集群之中的Session管理,那么又需要进行分布式的Session存储,总之就一点:有状态的用户需要维护Session,Session维护成本很高。

三、JWT的工作原理

  1. 客户端通过Web表单将正确的用户名和密码发送到服务端的接口。这一过程一般是POST请求。建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探。

  2. 服务端核对用户名和密码成功后,将用户的id等其他信息作为JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个JWT。形成的JWT就是一个形lll.zzz.xxx的字符串,并设置有效时间。

  3. 服务端将JWT字符串作为登录成功的返回结果返回给客户端。

  4. 客户端将返回的JWT以cookie的形式保存在浏览器上,并设置cookie的有效时间(建议客户端cookie和服务端JWT的有效时间设置为一致),用户登出时客户端需删除cookie。

  5. 客户端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决XSS和XSRF问题)

  6. 服务端对收到的JWT进行解密和校验,如检查签名是否正确、Token是否过期、Token的接收方是否是自己等。

  7. 验证通过后服务端使用JWT中包含的用户信息进行其他逻辑操作,返回相应结果,否则返回401。

四、JWT的组成

  • Header(头部)
  • Payload(负载)
  • Signature(签名)

header.payload.signature

五、JWT的验证过程

  • 它验证的方法其实很简单,只要把header做base64url解码,就能知道JWT用的什么算法做的签名,然后用这个算法,再次用同样的逻辑对header和payload做一次签名,并比较这个签名是否与JWT本身包含的第三个部分的串是否完全相同,只要不同,就可以认为这个JWT是一个被篡改过的串,自然就属于验证失败了。接收方生成签名的时候必须使用跟JWT发送方相同的密钥。

  • 验证一个JWT的时候,签名认证是每个实现库都会自动做的,但是payload的认证是由使用者来决定的。因为JWT里面可能会包含一个自定义claim,所以它不会自动去验证这些claim

  • 如果签名认证失败会抛出如下的异常:

    ​ io.jsonwebtoken.SignatureException

    即签名错误,JWT的签名与本地计算机的签名不匹配

  • JWT过期异常

    ​ io.jsonwebtoken.ExpiredJwtException

    就是令牌超过了过期时间

六、使用JWT

创建一个工具类 - JwtUtil

  • import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.JwtBuilder;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import java.nio.charset.StandardCharsets;
    import java.util.Date;
    import java.util.Map;
    
    public class JwtUtil {
        /**
         * 生成jwt
         * 使用Hs256算法, 私匙使用固定秘钥
         *
         * @param secretKey jwt秘钥
         * @param ttlMillis jwt过期时间(毫秒)
         * @param claims    设置的信息
         * @return
         */
        public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
            // 指定签名的时候使用的签名算法,也就是header那部分
            SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
    
            // 生成JWT的时间
            long expMillis = System.currentTimeMillis() + ttlMillis;
            Date exp = new Date(expMillis);
    
            // 设置jwt的body
            JwtBuilder builder = Jwts.builder()
                    // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                    .setClaims(claims)
                    // 设置签名使用的签名算法和签名使用的秘钥
                    .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
                    // 设置过期时间
                    .setExpiration(exp);
    
            return builder.compact();
        }
    
        /**
         * Token解密
         *
         * @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
         * @param token     加密后的token
         * @return
         */
        public static Claims parseJWT(String secretKey, String token) {
            // 得到DefaultJwtParser
            Claims claims = Jwts.parser()
                    // 设置签名的秘钥
                    .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
                    // 设置需要解析的jwt
                    .parseClaimsJws(token).getBody();
            return claims;
        }
    
    }
    

创建一个JwtProperties类

  • import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    @Component
    @ConfigurationProperties(prefix = "com.jwt")
    @Data
    public class JwtProperties {
        private String adminSecretKey;//秘钥
        private long adminTtl;//过期时间
        private String adminTokenName;//有效负载
    
    }
    

在配置文件中加入

  • com:
      jwt:
        # 设置jwt签名加密时使用的秘钥
        admin-secret-key: itcast
        # 设置jwt过期时间
        admin-ttl: 7200000
        # 设置前端传递过来的令牌名称
        admin-token-name: token
    

使用

  • @Autowired
    private JwtProperties jwtProperties;
    
    public Class TestJWt{
        //生成jwt令牌
        HashMap<String, Object> claims = new HashMap<>();
                claims.put("1","1");
        String jwt = JwtUtil.createJWT(
                jwtProperties.getAdminSecretKey(),
                jwtProperties.getAdminTtl(),
                claims
        );
        //解析jwt令牌
        Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), jwt);
        claims.get("1");
    }
    
    
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值