基于JWT的用户token验证

简介

1、JWT介绍


JWT(Json Web Token)是一种可以跨域的认证方案

构成:

  1. 头部Header:头部包含了两部分,token 类型和采用的加密算法(可为none,后端应限制加密算法,不以这里为准)。
  2. 载荷Payload:这部分才是重要的,可以自定义信息保存在此。
  3. 签名Signature:使用编码后的header和payload以及我们提供的一个密钥,然后使用header中指定的签名算法进行签名,签名的作用是保证JWT没有被篡改过,如果有人对头部以及负载的内容解码之后进行修改,再进行编码,最后加上之前的签名组合形成新的JWT的话,那么服务器端会判断出新的头部和负载形成的签名和JWT附带上的签名是不一样的。如果要对新的头部和负载进行签名,在不知道服务器加密时用的密钥的话,得出来的签名也是不一样的。

这三部分均用base64进行编码,并使用 . 进行分隔。一个典型的 JWT 格式的 token 类似xxxxx.yyyyy.zzzzz。

JWT对三个部分都使用的是Base64进行编码,但是Base64是可逆的,所以注意在Payload部分不要携带敏感信息。

特点:

  • 简洁(Compact) : 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快。
  • 自包含(Self-contained) :负载中包含了所有用户所需要的信息,避免了多次查询数据库。

核心优势:无状态

  • 省事,解析完token把信息放在处理链路的上下文,不用去redis或者database去取了。
  • 省时,用每个请求验证token慢一点点的代价,换来不用去存储取的优势。

2、JWT和Session对比


存储id的差异:
        Session方式存储用户id的最大弊病在于Session是存储在服务器端的,所以需要占用大量服务器内存,对于较大型应用而言可能还要保存许多的状态。一般而言,大型应用还需要借助一些KV数据库和一系列缓存机制来实现Session的存储。

        而JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。除了用户id之外,还可以存储其他的和用户相关的信息,例如该用户是否是管理员、用户所在的分组等。虽说JWT方式让服务器有一些计算压力(例如加密、编码和解码),但是这些压力相比磁盘存储而言可能就不算什么了。具体是否采用,需要在不同场景下用数据说话。

        Session方式来存储用户id,一开始用户的Session只会存储在一台服务器上。对于有多个子域名的站点,每个子域名至少会对应一台不同的服务器,例如:www.taobao.com,nv.taobao.com,nz.taobao.com,login.taobao.com。所以如果要实现在login.taobao.com登录后,在其他的子域名下依然可以取到Session,这要求我们在多台服务器上同步Session。使用JWT的方式则没有这个问题的存在,因为用户的状态已经被传送到了客户端。

使用Session的缺点:

  1. 占用服务端内存;
  2. 扩展性差(跨域难解决,不利于分布式);
  3. 基于Cookie,容易受到CSRF(跨站请求伪造)攻击。

JWT使用流程

  1. 前端通过Web表单将用户名和密码发送到后端的接口。这一过程一般是一个HTTP POST请求。建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探。
  2. 后端核对用户名和密码成功后,将用户的id等其他信息作为 JWT-Payload(负载),将其与头部分别进行Base64编码拼接后签名形成一个JWT。形成的JWT就是一个形同111.zzz.xxx的字符串。
  3. 后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStorage或sessionStorage上,退出登录时前端删除保存的JWT即可。
  4. 前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决XSS和XSRF问题)
  5. 后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确;检查Token是否过期;检查Token的接收方是否是自己(可选)。
  6. 验证通过后后端使用JWT中包含的用户信息进行其他逻辑操作,返回相应结果。

代码实现

1、添加依赖

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

2、生成token

private  static  final String ISSUER = "签发者";

    /**
     * 创建JWT令牌
     * @param userId
     * @return
     * @throws Exception
     */
    public static String generateToken(Long userId) throws Exception {
        //使用Algorithm的RSA256的加密算法
        Algorithm algorithm = Algorithm.RSA256(RSAUtil.getPublicKey(),RSAUtil.getPrivateKey());
        //日历类
        Calendar claendar = Calendar.getInstance();
        //设置日历为当前时间
        claendar.setTime(new Date());
        //设置过期时间及单位
        claendar.add(Calendar.SECOND,30);
        //创建JWT token令牌,设置KeyId、签发者、过期时间、加密算法
        return JWT.create().withKeyId(String.valueOf(userId))
                .withIssuer(ISSUER)
                .withExpiresAt(claendar.getTime())
                .sign(algorithm);
    }

3、token认证

/**
     * token验证方法
     * @param token
     * @return
     */
    public static Long verifyToken(String token) {
        try {
            //此处也是使用RSA加解密算法,因为获取token使用了RSA的加密算法,这里也需要使用对应的解密算法
            Algorithm algorithm = Algorithm.RSA256(RSAUtil.getPublicKey(),RSAUtil.getPrivateKey());
            //创建JWT认证者
            JWTVerifier verifier = JWT.require(algorithm).build();
            //获取解密后的JWT
            DecodedJWT verify = verifier.verify(token);
            //获取JWT的keyId,也就是这里的userId
            String userId = verify.getKeyId();
            return Long.valueOf(userId);
        }catch (TokenExpiredException e){
            throw new ConditionException("555", "token过期!");
        }catch (Exception e) {
            throw new ConditionException("非法用户token!");
        }
    }

  • 37
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值