JWT原理

jwt原理和使用

JSON Web Tokens,是一种开发的行业标准RFC 7519,用于安全的表示双方之间的声明。目前,jwt广泛的用在系统的用户认证方面,特别是前后端分离项目。

一、JWT原理:

参考文章:https://www.jianshu.com/p/180a870a308a

1、传统的登录方式:

浏览器输入用户名密码,服务端校验通过,根据用户信息生成一个token,将token和user_id存到数据库或者session中,并将token返回给前端,存入cookie,后面浏览器每次请求都会带上cookie,服务端根据cookie查询用户,验证用户有效性。

弊端:

1)如果出现XSS(跨站脚本攻击)漏洞,由于cookie可以被js读取,XSS漏洞会导致用户token被泄露。

解决方案:

a)设置httpOnly,这样的话cookie将不会被js读取,浏览器会自动将它加到请求头信息中,但是带来了新的问题,很容易被XSRF(跨站请求伪造)攻击,因为只要当前浏览器开着,另一个界面可以很容易的跨站请求这个界面的内容,因为cookie会被默认发送出去。

b)设置secure,这样cookie就只能通过https传输,可以过滤掉一些使用http协议请求的XSS注入。

2)将验证信息存到数据库中,每次验证的时候,都需要去数据库中查询,增加了数据库的查询和存储开销。

3)如果将token存到session中,也会增加服务器的存储压力。

2、为什么使用 JWT

1)可以通过URL POST参数或者http header中发送,数据量小,传输速度快。

2)自包含:负载中包含了用户所需要的所有信息,避免多次查询数据库

3)JWT组成:

header+payload+signature 三者通过点【.】连接起来就是JWT了。

img

a)header头部:包含token类型和采用的加密算法

{“alg”:“HS256”, “typ”:“JWT”}

b)payload负载:存放信息的地方,可以把用户ID等信息存放在payload中。

常用的信息有:iss(签发者), exp(过期时间), sub(面向用户), aud(接收方), iat(签发时间)等。

c)signature签名:使用编码后(base64编码)的header和payload再加上我们提供的一个公钥,然后使用header中指定的签名算法进行签名。作用是保证JWT没有被窜改过。

JWT只适合向web端传递一些非敏感信息,因为base64编码是可逆的,很容易被破解。

JWT通常用来设计用户认证和授权系统,还有我们通常说的单点登录等。

二、JWT的使用过程

img
  1)前端通过表单将用户名和密码发送到后端接口。

2)后端核对用户名和密码后,将用户的id及其他非敏感信息作为JWT Payload,将其与头部分别进行base64编码后签名,生成JWT

3)后端将JWT字符串作为登录成功的结果返回给前端,前端可以将JWT存到localStorage或者sessionStorage中,退出登录时,前端删除保存的JWT信息即可。

4)前端每次在请求时,将JWT放到header中的Authorization

5)后端验证JWT的有效性

6)验证通过后,进行其他逻辑操作。

  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。

1. 摘要

JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案,本文介绍它的原理,用法和详细的数据结构。

2. JWT的定义

Json web token(JWT)是为了网络应用环境间传递声明而执行的一种基于JSON的开发标准(RFC 7519),该token被设计为紧凑且安全的,特别适用于分布式站点的单点登陆(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

什么情况下使用JWT比较适合? **授权:**这是最常见的使用场景,解决单点登录问题。因为JWT使用起来轻便,开销小,服务端不用记录用户状态信息(无状态),所以使用比较广泛; **信息交换:**JWT是在各个服务之间安全传输信息的好方法。因为JWT可以签名,例如,使用公钥/私钥对儿 - 可以确定请求方是合法的。此外,由于使用标头和有效负载计算签名,还可以验证内容是否未被篡改。

3. JWT的原理和流程

3.1 跨域认证的问题

互联网服务离不开用户认证。一般流程是下面这样:

1、用户向服务器发送用户名和密码。

2、服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等等。

3、服务器向用户返回一个 session_id,写入用户的 Cookie。

4、用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。

5、服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。

这种模式的问题在于,扩展性(scaling)不好。单机当然没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session。

举例来说,A 网站和 B 网站是同一家公司的关联服务。现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,请问怎么实现?

一种解决方案是 session 数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。 另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。

3.2 JWT 的原理

JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样。 { “姓名”: “张三”, “角色”: “管理员”, “到期时间”: “2018年7月1日0点0分” } 以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名。

服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。

区别

(1) session 存储在服务端占用服务器资源,而 JWT 存储在客户端

(2) session 存储在 Cookie 中,存在伪造跨站请求伪造攻击的风险

(3) session 只存在一台服务器上,那么下次请求就必须请求这台服务器,不利于分布式应用

(4) 存储在客户端的 JWT 比存储在服务端的 session 更具有扩展性

3.3 JWT的认证流程图

流程说明:

1,浏览器发起请求登陆,携带用户名和密码;

2,服务端验证身份,根据算法,将用户标识符打包生成 token,

3,服务器返回JWT信息给浏览器,JWT不包含敏感信息;

4,浏览器发起请求获取用户资料,把刚刚拿到的 token一起发送给服务器;

5,服务器发现数据中有 token,验明正身;

6,服务器返回该用户的用户资料;

3.4 JWT的6个优缺点

1、JWT默认不加密,但可以加密。生成原始令牌后,可以使用改令牌再次对其进行加密。

2、当JWT未加密方法是,一些私密数据无法通过JWT传输。

3、JWT不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数。

4、JWT的最大缺点是服务器不保存会话状态,所以在使用期间不可能取消令牌或更改令牌的权限。也就是说,一旦JWT签发,在有效期内将会一直有效。

5、JWT本身包含认证信息,因此一旦信息泄露,任何人都可以获得令牌的所有权限。为了减少盗用,JWT的有效期不宜设置太长。对于某些重要操作,用户在使用时应该每次都进行进行身份验证。

6、为了减少盗用和窃取,JWT不建议使用HTTP协议来传输代码,而是使用加密的HTTPS协议进行传输。

4. JWT 的数据结构

4.1 JWT消息构成

一个token分3部分,按顺序:

  • 头部(header)
  • 载荷(payload)
  • 签证(signature) 对象为一个很长的字符串,字符之间通过"."分隔符分为三个子串。注意JWT对象为一个长字串,各字串之间也没有换行符,一般格式为:xxxxx.yyyyy.zzzzz 。 例如 yJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

4.2 头部(header)

JWT的头部承载两部分信息:

  • 声明类型,这里是jwt
  • 声明加密的算法 通常直接使用 HMAC SHA256

JWT里验证和签名使用的算法,可选择下面的:

JWS算法名称描述
HS256HMAC256HMAC with SHA-256
HS384HMAC384HMAC with SHA-384
HS512HMAC512HMAC with SHA-512
RS256RSA256RSASSA-PKCS1-v1_5 with SHA-256
RS384RSA384RSASSA-PKCS1-v1_5 with SHA-384
RS512RSA512RSASSA-PKCS1-v1_5 with SHA-512
ES256ECDSA256ECDSA with curve P-256 and SHA-256
ES384ECDSA384ECDSA with curve P-384 and SHA-384
ES512ECDSA512ECDSA with curve P-521 and SHA-512

JWT的头部描述JWT元数据的JSON对象参考: { “alg”: “HS256”, “typ”: “JWT” }

代码样例如下:

// header Map
Map<String, Object> map = new HashMap<>();
map.put("alg", "HS256");
map.put("typ", "JWT");

4.3 载荷(payload)

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。

iss (issuer):签发人 exp (expiration time):过期时间 sub (subject):主题 aud (audience):受众 nbf (Not Before):生效时间 iat (Issued At):签发时间 jti (JWT ID):编号

除以上默认字段外,我们还可以自定义私有字段,如下例: { “sub”: “1234567890”, “name”: “chongchong”, “admin”: true } 注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。 这个 JSON 对象也要使用 Base64URL 算法转成字符串。 代码样例如下:

 JWT.create().withHeader(map) // header
                .withClaim("iss", "Service") // payload
                .withClaim("aud", "APP")
                .withIssuedAt(iatDate) // sign time
                .withExpiresAt(expiresDate) // expire time
                .withClaim("name", "cy") // payload
                .withClaim("user_id", "11222");

4.4 签名(signature)

Signature 部分是对前两部分的签名,防止数据篡改。 首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

HMACSHA256( base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就构成整个JWT对象TOKEN, 就可以返回给用户。

4.4.1 Base64URL算法

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

4.5 JWT的用法

客户端接收服务器返回的JWT,将其存储在Cookie或localStorage中。 此后,客户端将在与服务器交互中都会带JWT。如果将它存储在Cookie中,就可以自动发送,但是不会跨域,因此一般是将它放入HTTP请求的Header Authorization字段中。

Authorization: Bearer

当跨域时,也可以将JWT被放置于POST请求的数据主体中。

5. JWT、JWS、JWE的区别

1)JWT(JSON Web Tokens),jwt长度较小,且可以使用URL传输(URL safe)。不想cookies只能在web环境起作用。 JWT可以同时使用在web环境和RESTfull的接口。

2)对于开发者来说,JWT与另外两种相近的标准:JWS(JSON Web Signature)、JWE(JSON Web Encryption),容易引起混乱。

3)关于JWT的描述,可以参考RFC7519(https://tools.ietf.org/html/rfc7519)的描述: **JSON Web Token (JWT) **是一个间接地、URL安全的,表现为一组声明,可以在双方之间进行传输。一个JWT的声明,是指经过编码后的一个JSON对象,这个JSON对象可以是一个JSON Web Signature(JWS)结构的荷载(payload),或者是一个JSON Web Encryption(JWE)结构的明文。允许使用声明进行数字签名,或者通过一个Message Authentication Code(MAC)进行完整性保护可选择是否加密。

简单来说,JWT表现为一组被编码为JWS and/or JWE结构的JSON object的声明(Claim).

换言之,一组JWT声明(就是表现为JSON格式的Claims)被通过JWS结构或者JWE结构(或者同时使用两种)发送,决定于你如何去实现它。所以,当你发送JWT给别人时,它实际上是一个JWT荷载或者JWE荷载。JWS荷载更加常用。

4)关于JWS 顾名思义,JWS模式对这个内容进行了数字化签名。这个内容被用来存放JWT的声明.服务端签名出JWT并且发送到客户端,并在用户成功认证后进行应答。服务器期望客户端在下次请求的时候将JWS作为请求的一部分,发送回服务端。

如果我们处理的客户端是欺骗者怎么办呢?这就是签名(signature)需要出场的地方了。签名携带了完整的可验证的信息。换句话说,服务器可以确认,接收到的JWT声明里的JWS是没有经过欺骗客户端、中间者进行修改的。 服务端通过验证消息的签名来确保客户端没有修改声明。如果服务端检测到任何修改,可以采取适当的动作(拒绝这次请求或者锁定客户端之类的)。 客户端同样可以验证签名,为了做到这点,客户端也需要服务端的secret(密钥)(如果这个JWT签名是HMAC算法),或者需要服务端对公钥(如果这个WJT是数字化签名)。 特别注意:对于JWS,荷载(声明部分)没有进行加密,所以,不要发送任何敏感信息。

5)关于JWE JWE模式会对内容加密,而不是签名。JWT的声明会被加密。因此JWE带来了保密性。JWE可以被签名并附在JWS里。这样的话就可以同时加密和签名。因此得到了保密性(Confidentiality)、完整性(Integrity)、可认证(Authentication)。

6)那么对于客户端,如何分辨JWS或者JWE呢? JWS的Header与JWE的Header是不同的,可以通过检查“alg”Header参数的值来区分。如果这个值表现为一个数字签名或者MAC的算法,或者是”none“,则它是一个JWS。 如果它表现为一个 Key Encryption, Key Wrapping, Direct Key Agreement, Key Agreement with Key Wrapping, or Direct Encryption algorithm。则它是一个JWE。 还可以通过Header里的“enc”(encryption algorithm)是否存在来判断,如果"enc"这个成员存在的话说明是JWE,否则的话就是JWS.

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐