JWT认证原理
JWT认证原理
JWT(Json Web Token)是一种服务端向客户端发放令牌的认证方式。客户端用户名密码登录时,服务端会生成一个令牌返回给客户端;客户端随后在向服务端请求时只需携带这个令牌,服务端通过校验令牌来验证是否是来自合法的客户端,进而决定是否向客户端返回应答。从机制可以看到,这种基于请求中携带令牌来维护认证的客户端连接的方式解决了早期服务端存储会话的各种有状态问题。
在Istio使用中,JWT令牌生成由特定的认证服务提供,令牌验证由网格执行,彻底解耦用户业务中的认证逻辑,使应用程序专注于自身业务。基于Istio的JWT完整机制如图1所示。
① 客户端连接认证服务,提供用户名和密码;
② 认证服务验证用户名和密码,生成JWT令牌,包括用户标识和过期时间等信息,并使用认证服务的私钥签名;
③ 认证服务向客户端返回生成的JWT令牌;
④ 客户端将收到的JWT令牌存储在本端,供后续请求时使用;
⑤ 客户端在向其他服务发起请求时携带JWT令牌,无需再提供用户名、密码等信息;
⑥ 网格数据面代理拦截到流量,使用配置的公钥验证JWT令牌;
⑦ 验证通过后,网格代理将请求转发给服务端;
⑧ 服务端处理请求;
⑨ 服务端返回应答数据给网格代理;
⑩ 网格数据面代理转发应答数据给调用方。
在这个过程中,重点是第六步,原来服务端的JWT认证功能迁移到了网格代理上。网格数据面从控制面配置的认证策略中获取验证JWT令牌的公钥,可以是jwks(JSON Web Key Set)上配置的公钥,也可以是从jwksUri配置的公钥地址获取到的公钥。获得公钥后,网格代理使用该公钥对认证服务私钥签名的令牌进行验证,并解开令牌中的iss,验证是否匹配认证策略中的签发者信息。验证通过的请求发送给应用程序,验证不通过则直接拒绝,不会发送给应用程序。
JWT结构
JWT是一个包含了特定声明的Json结构。从前面介绍的JWT认证流程第六步知道,只要验证这个Json结构本身,即可以确认请求身份,不需要查询后端服务。下面解析JWT结构从而了解如何携带这些认证信息。
JWT包含三部分:头部Header、负载Payload和签名Signature。
- 头部Header
头部描述JWT的元数据,包括算法alg和类别typ等信息。alg描述签名算法,这样接收者可以根据对应的算法来验证签名,默认是如下所示的HS256,表示 HMAC-SHA256;typ表示令牌类型,设置为JWT,表示这是一个JWT类型的令牌。
{ "alg": "HS256", "typ": "JWT" }
- 负载Payload
存放令牌的主体内容,由认证服务AuthN生成相关信息并放到令牌的负载中。重要属性包括:
- iss:令牌发行者 issuer
- aud:令牌受众 audience
在JWT验证时,会校验发行者、受众信息和令牌负载中的发行者iss、受众audience是否匹配。JWT的内容本身不是加密的,所有拿到令牌的服务都可以看到令牌负载Payload中的内容,因此建议Payload里不要存放私密的信息。
- 签名Signature
签名字段是对头部和负载的签名,确保只有特定合法的认证服务才可以发行令牌。实际使用中一般是把头部和负载分别执行Base64转换成字符串,然后使用认证服务的密钥对拼接的字符串进行签名,签名算法正是前面介绍的头域中定义的算法。
# Header: { "alg": "RS512", "typ": "JWT" } # Payload { "iss": "weather@cloudnative-istio", "audience": "weather@cloudnative-istio" } # Signature RSASHA512( base64UrlEncode(header) + "." + base64UrlEncode(payload) )
以上结构最终输出的令牌如下,可以看到“.”分割的三个字符串分别对应JWT结构的头部、负载和签名三部分。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjQ2ODU5ODk3MDAsInZlciI6IjIuMCIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoid2VhdGhlckBjbG91ZG5hdGl2ZS1pc3Rpby5ib29rIiwic3ViIjoid2VhdGhlckBjbG91ZG5hdGl2ZS1pc3Rpby5ib29rIn0.SEp-8qiMwI45BuBgQPH-wTHvOYxcE_jPI0wqOxEpauw