jwt讲解

jwt官网:https://jwt.io/introduction/
jwt介绍及使用:https://blog.51cto.com/u_13538361/6383648
jwt优缺点、续签问题:https://blog.youkuaiyun.com/JingYuanhao/article/details/120941173

2.jwt相对普通token的好处?
    优点:
        1.确定发件人
        2.由于使用标头和有效负载计算签名,您还可以验证内容没有被篡改。
        3. 可扩展性好
        应用程序分布式部署的情况下,Session需要做多机数据共享,通常可以存在数据库或者Redis里面。而JWT不需要。
        4. 无状态(关于有状态和无状态:https://mp.weixin.qq.com/s/xEIWTduDqQuGL7lfiP735w)
        JWT不在服务端存储任何状态。RESTful API的原则之一是无状态,发出请求时,总会返回带有参数的响应,不会产生附加影响。用户的认证状态引入这种附加影响,这破坏了这一原则。另外JWT的载荷中可以存储一些常用信息,用于交换信息,有效地使用 JWT,可以降低服务器查询数据库的次数。
    缺点:
        1 安全性:由于JWT的payload是使用Base64编码的,并没有加密,因此JWT中不能存储敏感数据。而Session的信息是存在服务端的,相对来说更安全。
        2 性能:JWT太长。由于是无状态使用JWT,所有的数据都被放到JWT里,如果还要进行一些数据交换,那载荷会更大,经过编码之后导致JWT非常长,Cookie的限制大小一般是4k,cookie很可能放不下,所以JWT一般放在LocalStorage里面。并且用户在系统中的每一次Http请求都会把JWT携带在Header里面,Http请求的Header可能比Body还要大。而SessionId只是很短的一个字符串,因此使用JWT的Http请求比使用Session的开销大得多。
        3 一次性:无状态是JWT的特点,但也导致了这个问题,JWT是一次性的。想修改里面的内容,就必须签发一个新的JWT。即缺陷是一旦下发,服务后台无法拒绝携带该jwt的请求(如踢除用户)
          无法废弃:通过JWT的验证机制可以看出来,一旦签发一个JWT,在到期之前就会始终有效,无法中途废弃。例如你在payload中存储了一些信息,当信息需要更新时,则重新签发一个JWT,但是由于旧的jwt还没过期,拿着这个旧的JWT依旧可以登录,那登录后服务端从JWT中拿到的信息就是过时的。为了解决这个问题,我们就需要在服务端部署额外的逻辑,例如设置一个黑名单,一旦签发了新的JWT,那么旧的就加入黑名单(比如存到redis里面),避免被再次使用。
        5 续签:如果你使用jwt做会话管理,传统的Cookie续签方案一般都是框架自带的,Session有效期30分钟,30分钟内如果有访问,有效期被刷新至30分钟。一样的道理,要改变JWT的有效时间,就要签发新的JWT。最简单的一种方式是每次请求刷新JWT,即每个HTTP请求都返回一个新的JWT。这个方法不仅暴力不优雅,而且每次请求都要做JWT的加密解密,会带来性能问题。另一种方法是在Redis中单独为每个JWT设置过期时间,每次访问时刷新JWT的过期时间。
        redis续签:https://mp.weixin.qq.com/s/ilgI63tIsbqMktwB36knyQ
        可以看出想要破解JWT一次性的特性,就需要在服务端存储jwt的状态。但是引入 redis 之后,就把无状态的jwt硬生生变成了有状态了,违背了JWT的初衷。而且这个方案和Session都差不多了。
    说了这么多,JWT 也不是天衣无缝,由客户端维护登录状态带来的一些问题在这里依然存在,举例如下:
    续签问题,这是被很多人诟病的问题之一,传统的 cookie+session 的方案天然的支持续签,但是 jwt 由于服务端不保存用户状态,因此很难完美解决续签问题,如果引入 redis,虽然可以解决问题,但是 jwt 也变得不伦不类了。
    注销问题,由于服务端不再保存用户信息,所以一般可以通过修改 secret 来实现注销,服务端 secret 修改后,已经颁发的未过期的 token 就会认证失败,进而实现注销,不过毕竟没有传统的注销方便。
    密码重置,密码重置后,原本的 token 依然可以访问系统,这时候也需要强制修改 secret。
    基于第 2 点和第 3 点,一般建议不同用户取不同 secret。
    解决方案:redis可以解决续签、(重新登录、修改密码、单点登录)废弃旧token;如果对于重新登录和修改密码,直接重新获取toekn即可,但是续签和废弃旧token就能只能使用redis了。
    如果不是使用oauth2验证,无需处理废弃、续签jwt,那么也就无法支持单点登录;如果需要的话,让jwt永久有效,用redis存储jwt并且设置失效时间,过期就删除,续签就验签过期时间,支持单点登录

3.jwt续签退出登录解决方法(面试题)
    你们使用JWT做登录凭证,如何解决token注销问题
    答:jwt的缺陷是token生成后无法修改,因此无法让token失效。只能采用其它方案来弥补,基本思路如下: 
    方案一: 
    1)适当减短token有效期,让token尽快失效 
    2)删除客户端cookie 
    3)服务端对失效token进行标记,形成黑名单,虽然有违无状态特性,但是因为token有效期短,因此标记 时间也比较短。服务器压力会比较小 方案二: 1)用户登录后,生成JWT 2)把JWT的id存入redis,只有redis中有id的JWT,才是有效的JWT 3)退出登录时,把ID从Redis删除即可
    既然token有效期短,怎么解决token失效后的续签问题?
    
4.续签
    使用 Refresh Token
    还有另一种方案,使用 Refresh Token,它可以避免频繁的读写操作。这种方案中,服务端不需要刷新 Token 的过期时间,一旦 Token 过期,就反馈给前端,前端使用 Refresh Token 申请一个全新 Token 继续使用。
    这种方案中,服务端只需要在客户端请求更新 Token 的时候对 Refresh Token 的有效性进行一次检查,大大减少了更新有效期的操作,也就避免了频繁读写。
    当然 Refresh Token 也是有有效期的,但是这个有效期就可以长一点了,比如,以天为单位的时间。
    服务端保存token状态
    在服务器端保存 Token 状态,用户每次操作都会自动刷新(推迟) Token 的过期时间
    但每秒种可能发起很多次请求,每次都去刷新过期时间会产生非常大的代价。
    如果 Token 的过期时间被持久化到数据库或文件,代价就更大了。所以通常为了提升效率,减少消耗,会把 Token 的过期时间保存在缓存或者内存中
4.实例
    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, IllegalStateException, UnsupportedEncodingException {
		// 密钥
		byte[] key = "your-256-bit-secret".getBytes();

		String token1 = JWT.create()
								.setPayload("sub", "1234567890")
								.setPayload("name", "looly")
								.setPayload("admin", true)
								.setKey(key)
								.sign();
		System.out.println(token1);
		
		// Header 头部信息,主要声明了JWT的签名算法等信息
		String header = "{\"typ\":\"JWT\",\"alg\":\"HS256\"}"; 
		String headerEncode = cn.hutool.core.codec.Base64.encode(header.getBytes());
		String headerUrl = URLEncoder.encode(headerEncode);
		System.out.println(headerUrl);
		
		// Payload 载荷信息,主要承载了各种声明并传递明文数据
		String payload  ="{\"sub\":\"1234567890\",\"admin\":true,\"name\":\"looly\"}";
		String payloadode = cn.hutool.core.codec.Base64.encode(payload.getBytes());
		String payloadUrl = URLEncoder.encode(payloadode);
		System.out.println(payloadUrl);
		
		// Signature 签名,拥有该部分的JWT被称为JWS,也就是签了名的JWS,用于校验数据
        String message=headerUrl+"."+payloadUrl;
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKey = new SecretKeySpec(key, "HmacSHA256");
        sha256_HMAC.init(secretKey);
        byte[] hash = sha256_HMAC.doFinal(message.getBytes("UTF-8"));

        String signature =  cn.hutool.core.codec.Base64.encode(hash);
		System.out.println(signature); // 需要将其Base64URL编码:=被省略、+替换成-,/替换成_ 
		
		// 整体结构是:header.payload.signature
		System.out.println(headerUrl+"."+payloadUrl+"."+signature); // 然后将其复制到官网验证:https://jwt.io/

		
	}

实例一:SpringBoot集成JWT实现token验证:https://www.jianshu.com/p/e88d3f8151db
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>4.0.0</version>
</dependency>
使用:java-jwt.jar生成和解析token:https://blog.youkuaiyun.com/weixin_45070175/article/details/118559272
实例二:使用微服务网关过滤拦截:https://zhuanlan.zhihu.com/p/185735351

问题:
cookie、session、token、jwt、OAuth2的区别:https://blog.youkuaiyun.com/wcc27857285/article/details/87901559 (有讲到Base64URL)
    1.cookie不能跨域

知识点:
1.Base64URL算法:
前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。
JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 http://api.example.com/?token=xxx)。Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。这就是 Base64URL 算法。

2.Base64和Base64Url:https://www.jianshu.com/p/51589f4cf384

3.java实现HmacSHA256算法进行加密
    3.1使用HmacSHA256进行数据加密(需要使用秘钥secret)
    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
        String secret="2131231@#42";
        String message="我加密一下";
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes("utf-8"), "HmacSHA256");
        sha256_HMAC.init(secretKey);
        byte[] hash = sha256_HMAC.doFinal(message.getBytes("utf-8"));
        String encodeStr = Base64.encodeBase64String(hash);
		String encodeStr16=byte2Hex(hash);
    }
    3.2加密后的字节也可以进行转换成16位进制的字符串
    /**
     * 将byte转为16进制
     *
     * @param bytes
     * @return
     */
    private static String byte2Hex(byte[] bytes) {
        StringBuffer stringBuffer = new StringBuffer();
        String temp = null;
        for (int i = 0; i < bytes.length; i++) {
            temp = Integer.toHexString(bytes[i] & 0xFF);
            if (temp.length() == 1) {
                //1得到一位的进行补0操作
                stringBuffer.append("0");
            }
            stringBuffer.append(temp);
        }
        return stringBuffer.toString();
    }
    注释:MD5算法进行加密的属于比较弱的加密算法,所以要想强一点加密的话可以使用sha256加密算法
    版权声明:本文为优快云博主「MikeTeas」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.youkuaiyun.com/sfb749277979/article/details/107508389



# 常见问题汇总
1)令牌过期了怎么办?
为了保证安全性,授权的令牌时间不可过长,但是一些游戏服务器需要长时间保持微信用户信息访问状态,
为此,OAuth 2.0 提供了更新令牌机制,一方面使令牌授权时间不会过长,另一方面保证了游戏服务器访问微信用户信息的连贯性。
2)为什么要先返回认证码(code)而不是直接返回令牌?
浏览器是一个不安全的环境,所以在用户给予授权后,微信认证服务器不是直接返回访问令牌(access_token),而是返回认证码(code),并通过浏览器重定向给游戏服务器。
游戏服务器需要先用认证码(code)换取访问令牌(access_token),而换取步骤需要微信认证服务器认证游戏服务器的身份(而不是验证浏览器身份),以此保证授权过程的安全性。
3)state 参数有什么用?
获取认证码(code)请求中,加入state参数可有效防止CSRF攻击。
CSRF攻击者要想诱导用户授权并插入自己的认证码(code),且被游戏服务器接收,就需要猜出该state参数,这大大增加了CSRF攻击的难度。


问题:
1.关于token值前面的Bearer token是什么意思?还有Digest token和Basic token,又代表什么含义?
Basic token:是指Basic base64(账号 密码),如果使用http clint,如Basic 账号 密码,它会自动将账号密码base64
Bearer token:是指Bearer jwt格式
Digest token:摘要认证


疑问:
1.认证和授权在网关还是在服务(security)?

认证、授权、权限管理、单点登录
用户认证知识总结
1.cookie、session、token、jwt的区别:https://blog.youkuaiyun.com/qq_35067322/article/details/119814236
    1.token:sha-256算法签名。
        1.token 是验证用户身份的凭证,我们通常叫它:令牌。
            最简单的token组成: uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,以哈希算法压缩成一定长的十六进制字符串)
        2.sha-256hash 算法原理:https://zhuanlan.zhihu.com/p/94619052
        3.什么是签名
        4.关于加密还得再研究一下
    2.jwt相对其他方式的优点、详解:https://blog.youkuaiyun.com/weixin_45070175/article/details/118559272
2.认证的方式
    2.1 自己写
    2.2 shiro
    2.3 spring security+Spring Authorization Server


3.sso单点登录
这其实就是redis是要存一个username-token还是多个的问题,如果存一个,另一个登录就会替换掉上一个登录的token
你可以定时让前端去请求接口判断登陆状态
如果觉得这样服务器压力太大,也可以等用户下一次请求再返回重新登陆的响应
是不是简简单单,这是没必要去用什么框架的,一大堆配置,很复制,很头疼

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值