文章目录
一、JWT
点击可链接到JWT官网
1.1 概念
JWT(JSON Web Token):又叫JWT令牌,是经过加密处理于校验处理的字符串。微服务建构中用于身份认证,目前最流行的跨域认证的解决方案。
官网描述 :JSON Web Token (JWT) 是一个开放标准 ( RFC 7519 ),它定义了一种紧凑且自包含的方式,用于在各方之间以 JSON 对象的形式安全传输信息。此信息可以验证和信任,因为它是数字签名的。JWT 可以使用密钥(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。
Token :其实就是访问资源对凭证。一般是用户通过用户名和密码登录成功之后,服务器将登录凭证做数字签名,加密之后得到的字符串作为token。
单点登录SSO(Single sign on):就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后,即可获得访问单点登录系统中其他关联系统和应用软件的权限,同时这种实现是不需要管理员对用户的登录状态或其他信息进行修改的,这意味着在多个应用系统中,用户只需一次登录就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,辅助了用户管理,是比较流行的。
1.2 相关知识
HTTP请求协议:超文本传输协议(HyperText Transfer Protocol),是指从客户端到服务器端的请求消息。包括:消息首行中,对资源的请求方法、资源的标识符及使用的协议。
超文本(HyperText):超文本就是指 含有指向其他资源连接内容的文本。超文本不仅仅是文字,包括图片、音频、视频等流媒体,以及超链接,点击超链接可以跳转到其他页面,每个页面都可以这样跳来跳去,形成一个网络。
协议:就是一种约定,规定好一种信息的格式后,发送方和接收方都需要按照这种格式来 发送信息 和 接收信息。
Cookie的产生:由于HTTP是无状态协议,一旦数据交换完毕,服务器和客户端之间的连接就会关闭。并且每一次请求都是相互独立的,当前请求不会记录上一次请求的记录。随着Web网站的发展,每次页面跳转的重新登录的痛点需要得到解决,在此需求下Cookie产生,使得客户端和服务器之间可以进行多次请求和响应,在此期间所产生的一些数据就保存在Cookie中。
Cookie:客户端会话管理技术,有时也用其复数形式 Cookies。类型为“小型文本文件”,是一段不超过4KB的小型文本数据,由一个名称(Name)、一个值(Value)和其它几个用于控制Cookie有效期、安全性、使用范围的可选属性组成。某些网站为了辨别用户身份,进行Session跟踪而储存在用户本地终端上的数据(通常经过加密),由用户客户端计算机暂时或永久保存的信息 。
Session的产生:由于cookie是保存在客户端上的,所以有被篡改的风险,不安全。要保存重要的数据一定要保证不会被篡改才是安全的,如果文件存储在服务器端,用户无法私自进行修改,那么安全性就会大大提高。为解决cookie不安全的问题,来提高网站安全性,便产生了Session。
Session:session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。session负责存储数据,cookie负责告诉后台,是哪一个数据。当建立一个新session的时候,向客户端下发一个session-id,让客户端保存session-id在cookie中,下一次发起请求的时候,将session-id发回后台,后台通过session-id,在很多session中查找到相应session-id对应的session进行操作。
总结:
HTTP是一种无状态的协议,所以用户在登录用户名密码进行登录后,HTTP不会记录下认证后的状态,下次登录下便还需进行认证登录。为了让我们应用能识别是那个用户发送的请求,我们只能在用户首次登录成功后,在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这是传统的基于session认证的过程。
JWT的产生:
由于传统的session认证存在一些问题:
- 随着用户的增多,每个用户的登录信息都会保存到服务器的Session中,服务器的开销会逐渐变大。
- 在分布式系统中,多个服务器下,无法正确拿到Session信息。
- Session依赖Cookie,Cookie无法防止CSRF(Cross Site Request Forgery):跨站请求伪造。Session认证中,Cookie是通过浏览器发送到服务端的,借助这个特性,攻击者可以通过让用户误点攻击连接(攻击者通过cookie拿到你的session_id就可以代替你的身份访问系统)达到攻击效果。
1.3 JWT特点
- 支持跨域请求访问。
- Token机制在服务端不需要储存session信息,因为Token自身包含了所有登录用户的信息。
- 可扩展性好 应用程序分布式部署的情况下,session需要做多机数据共享,通常可以存在数据库或者redis里面。而jwt不需要。
- JWT生成的token是无状态的,服务端不存储,即一旦生成在有效期之前一直可用,无法销毁,只能等它过期。
- 由于json的通用性,所以JWT是可以进行跨语言支持的。
- JWT中可附带允许的权限/操作,业务无需实现复杂的权限控制逻辑,只需要判断Token中是否有对应权限即可。
1.4 Token常见问题和解决
Token无状态产生的退出登录注销登录时产生的问题,Token一旦生成,后端不再添加其他逻辑,该Token在失效前都是有效的,也就是说即使注销登录退出Token也是可以使用的,那么如何解决这个问题呢?
- 黑名单机制:使用内存性数据库去维护一个黑名单,如果想让某个Token失效,可直接将这个Token加入到黑名单即可。当每次使用Token进行请求时对Token进行判断是否在黑名单中。
- 保持令牌的有效期限短并经常轮换 :很简单的一种方式。但是,会导致用户登录状态不会被持久记录,而且需要用户经常登录。
1.5 Token续签问题
添加一段逻辑:判断cookie即将到期时,重新生成一个token。比如token有效期为30分钟,当用户请求我们时,我们可以判断如果用户的token有效期还剩下10分钟,那么就重新生成token。因此用户只要在操作我们的网站,就会续签token
1.6 如何加强 JWT 的安全性?
- 使用安全系数高的加密算法
- 缩短 Token 有效时间
- token 不要放在 Cookie 中,有 CSRF 风险
- 使用 HTTPS 加密协议
- 对标准字段 iss、sub、aud、nbf、exp 进行校验
- 使用成熟的开源库,不要手贱造轮子
- 特殊场景下可以把用户的 UA、IP 放进 payload 进行校验(不推荐)
1.7 JWT鉴权流程
- 客户端发送登录请求到服务端。
- 服务端通过登录验证成功后,创建JWT生成的Token令牌。
- 服务端将创建好的Token响应给客户端。
- 客户端将Token进行保存(token 在客户端一般存放于 localStorage、cookie 或 sessionStorage 中,且有所区别)。
- 客户端在访问其他资源操作时,在请求头中携带Token发送到服务端。
- 服务端接受请求,进行Token解析(校验Token合法性,是否被篡改,通过Token获取信息)。
- 没有问题,则返回响应给客户端。
二、JWT内部结构
最终令牌效果如图:
如图所示,它是一串很长的字符串,中间由“。”分隔为三部分,并且无需换行(此处为方便展示遂换行)。
简而言之:JWT是由三部分组成 A+B+C ,其中A是由JWT头部信息(Header)组成,B为 Payload 负载,C为 Signature 签名。
故:最终令牌为
Header.Payload.Signature
2.1 Header 头部
标头通常由两部分组成:令牌的类型,即 JWT,以及正在使用的签名算法,例如 HMAC SHA256 或 RSA。
示例:
{
"alg": "RS256", 声明加密的算法
"typ": "JWT" 声明类型
}
然后,这个 JSON 被Base64Url编码以形成 JWT 的第一部分。
2.2 Payload 负载
令牌的第二部分是有效负载,其中包含声明。声明是关于实体(通常是用户)和附加数据的陈述。索赔分为三种类型:注册索赔、公开索赔和私人索赔。
- 注册声明 / 标准声明:这些是一组预定义的声明,它们不是强制性的,但建议使用,以提供一组有用的、可互操作的声明。标准声明的字段:标准中建议使用这些字段,但不强制(声明名称只有三个字符,只要 JWT 是紧凑的)。
iss?: string; // JWT的签发者 sub?: string; // JWT所面向的用户 aud?: string; // 接收JWT的一方 exp?: number; // JWT的过期时间 nbf?: number; // 在xxx日期之间,该JWT都是可用的 iat?: number; // 该JWT签发的时间 jti?: number; //JWT的唯一身份标识
- 公共声明:这些可以由使用 JWT 的人随意定义。但是为了避免冲突,它们应该在IANA JSON Web Token Registry中定义,或者定义为包含抗冲突命名空间的 URI。因为可以被解密出来,所以不要存放敏感信息
[key: string]: any;
- 私人声明:这些是为在同意使用它们的各方之间共享信息而创建的自定义声明,既不是注册声明也不是公共声明。私有声明是 JWT 提供者添加的字段,一样可以被解密,所以也不能存放敏感信息。
[key: string]: any;
示例:
然后对有效负载进行Base64Url编码以形成 JSON Web 令牌的第二部分。
2.3 Signature 签名
要创建签名部分,您必须获取编码的标头、编码的有效负载、秘密、标头中指定的算法,并对其进行签名。
公式: signature = 加密算法(header + “.” + payload, 密钥);
例如,如果您想使用 HMAC SHA256 算法,签名将通过以下方式创建:
签名用于验证消息在此过程中没有被更改,并且在使用私钥签名的令牌的情况下,它还可以验证 JWT 的发送者就是它所说的那个人。
2.4 综上
把所有的放在一起
输出是三个用点分隔的 Base64-URL 字符串,可以在 HTML 和 HTTP 环境中轻松传递,同时与基于 XML 的标准(如 SAML)相比更紧凑。
下面显示了一个 JWT,该 JWT 具有先前的标头和有效负载编码,并使用秘密签名。
三、Api操作JWT
3.1 Token数据生成
@Test
public void testCreateToken() {
//生成token
//1、准备数据
Map map = new HashMap();
map.put("id",1);
map.put("phone","13333333333");
//2、使用JWT的工具类生成token
long now = System.currentTimeMillis();
String token = Jwts.builder()
.signWith(SignatureAlgorithm.HS512, "test") //指定加密算法
.setClaims(map) //写入数据
.setExpiration(new Date(now + 50000)) //失效时间,单位毫秒值。
.compact();
System.out.println(token);
}
3.2 Token数据解析
/**
* token不合法报错:SignatureException
* token已过期报错:ExpiredJwtException
*/
@Test
public void testParseToken() {
String token = "eyJhbGciOiJIUzUxMiJ9.eyJwaG9uZSI6IjEzMzMzMzMzMzMzIiwiaWQiOjEsImV4cCI6MTY2MzIyNjE4MX0.Vhb6ZlKDBWwtPOU1oNhjw4Fso0S0juTXFNE0yhrJHDPCtkmSyJmfWKQW7E_mo_tXpcSLqjBtie4k4Xl4yih2VQ";
try {
Claims claims = Jwts.parser() //claims中的数据为写入时的Map对象
.setSigningKey("test")
.parseClaimsJws(token)
.getBody();
Object id = claims.get("id");
Object phone = claims.get("phone");
System.out.println(id + "+" + phone);
}catch (ExpiredJwtException e) {
System.out.println("token已过期");
}catch (SignatureException e) {
System.out.println("token不合法");
}
}
3.3 导入依赖
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>
四、常见面试题
以下面试题收集自:CSDN博主「MingjuLab」的原创文章
原文链接:https://blog.csdn.net/MINGJU2020/article/details/103039418
1. 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删除即可
2. 既然token有效期短,怎么解决token失效后的续签问题?
答:在验证用户登录状态的代码中,添加一段逻辑:判断cookie即将到期时,重新生成一个token。比如token有效期为30分钟,当用户请求我们时,我 们可以判断如果用户的token有效期还剩下10分钟,那么就重新生成token。因此用户只要在操作我们的网站,就会续签token
3. 如何解决异地登录问题?
答:因此如果有类似需求, 就不应选择JWT作为登录方案,而是使用传统session登录方案。
但是,如果一定要用JWT实现类似要过,就需要在Redis中记录登录用户的JWT的token信息,这样就成了有状态的登录,还不如一开始就选择Session 方案。
4. 如何解决cookie被盗用问题?
答:cookie被盗用的可能性主要包括下面几种:
- XSS攻击:这个可以再前端页面渲染时对 数据做安全处理即可,而且我们的cookie使用了Httponly为true,可以防止JS脚本的攻击。 - CSRF攻击:- 利用Referer头,防盗链- 请求头中加随机码
- 抓包,获取用户cookie:我们采用了HTTPS协议通信,无法获取请求的任何数据
- 请求重放攻击:对于普通用户的请求没有对请求重放做防御,而是对部分业务做好了幂等处理。运行管理系统中会对token添加随机码,认证token一次有效,来预防请求重放攻击。
- 用户电脑中毒:这个无法防范。
5. 用户的cookie被禁用怎么办?
答:cookie一般情况下,是不会被禁用,因为普通人根本不知道是什么是cookie,一般不用管,为了友好,我们可以给用户一个提示:你的cookie已经被禁用了,请启用cookie。
把jwt作为响应头返回,浏览器中JS把token写到本地存储(sessionStorage),要求前端每次发ajax,都必须自己携带token。而且有被xss攻击的风险
6. 如何解决cookie被篡改问题?
答:cookie可以篡改,但是签名无法篡改,否则服务端认证根本不会通过
7. 如何完成权限校验的?
答:首先我们有权限管理的服务,管理用户的各种权限,及可访问路径等。其次在网关zuul中利用Pre过滤器,拦截一切请求,在过滤器中,解析jwt获 取用户身份,查询用户权限,判断用户身份可以访问当前路径
8. 服务端微服务地址不小心暴露了,用户就可以绕过网关,直接访问微服务,怎么办?
答:首先,我们的微服务隐藏在网关的后面,而且整个服务被Nginx反向代理,用户只能看到nginx的地址,微服务暴露的可能性很低。
然后,即便真的暴露了,我们的微服务都做了严格的服务间鉴权处理,任何对微服务的访问都会被验证是否有授权,如果没有则会被拦截。
具体实现:
· 会有一张表记录每个微服务的id,和密钥信息
· 服务启动时,需要去授权中心,认证身份,携带id和secret
· 授权中心认证通过,会颁发一个JWT给微服务
· 微服务访问其它服务时,需要携带JWT
· 被访问的服务,需要验证JWT,如果没有携带,或token时伪造的拦截请求即可
版权归原作者 小杨同学_丶 所有, 如有侵权,请联系我们删除。