前端面试-网络-加密、Token、JWT

188 阅读6分钟

一、加密算法的分类

1.1 对称加密

  • 使用相同的密钥
    • 在对称加密中,公钥和私钥的一样的,即使用相同的密钥进行加密解密
  • 加密速度快
    • 对称加密算法通常比非对称加密算法快,因为使用了相同的密钥
  • 安全传输问题
    • 因为公钥和私钥是一样的,在网络通信过程中传递密钥,如果被拦截,通信的安全将受到威胁
  • 常见的算法
    • DES、3DES、TDEA、Blowfish、RC5等

image.png

1.2 非对称加密

  • 使用一对公钥和私钥进行加密解密
    • 公钥和私钥的不一样的
    • 一般在客户端使用公钥对账号密码进行加密,然后传递给后端,后端通过私钥进行解密
  • 公钥可以分发,安全性有保障
    • 使用公钥加密的数据,只能通过对应的私钥才能解密,即在网络通信过程中被拦截,也无法解密获取数据
  • 加密速度相对于对称算法来说比较慢
  • 常见的算法
    • RSA、DSA、Rabin、ECC、D-H等

image.png

1.3 摘要算法

摘要算法又称哈希/散列算法,它是通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常采用16进制的字符串表示),算法不可逆。
虽然算法不可逆,但存在碰撞的可能,即相同的数据加密后产生的数据串是一样的,即网络中采用的暴力破解法来破解摘要算法。

二、Token

token(令牌)是一种用于身份认证和授权的字符串,它在客户端和服务器之间传递,用于标识身份和访问权限,token本身一般会包含用户的一些身份信息、token的有效期等数据,用于校验token的合法性和有效性

  • 认证
    • 当用户输入账号密码后,后端会生成一个token,并将其发给客户端,用于表明身份
  • 授权
    • token还可以用于授权,即某些资源的访问的权限,在某些情况下,token可能会包含某些权限/角色的信息
  • 无状态
    • token认证是无状态的,即服务器不会在自身存储用户的登录状态
    • 用户的每个请求都包含token,服务器通过校验token来进行身份验证

三、JWT

3.1 JWT组成

JWT(JSON WEB TOKEN)是一种常见的token格式,它是一种轻量级的、自包含的方式来表示信息。

  • JWT通常包含一些标准的声明
    • 令牌的颁发者
    • 过期时间
    • 自定义的声明
      • 用户的ID、角色等
  • JWT由三部分组成
    • 头部(header)
      • 令牌头部,记录了整个令牌的类型和签名算法
    • 载荷(payload)
      • 令牌载荷,记录了保存的用户主体信息,如你要保存的用户信息可以放在这里
    • 签名(signature)
      • 令牌签名,按照头部固定的签名算法对整个令牌进行签名,该签名的作用是:保证令牌不被伪造和篡改

它们组合而成的完整格式:header.payload.signature

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE1ODc1NDgyMTV9.BCwUy3jnUQ_E6TqCayc7rCHkx-vxxdagUwPOWqwYCFc

3.2 header

header是令牌头部,记录了整个令牌的类型和签名算法,它的格式为一个json对象

  • alg:signature部分使用的签名算法,通常可以取两个值
    • HS256:对称算法
    • RS256:非对称算法
  • typ:整个令牌的类型,固定写JWT即可
{
    "alg":"HS256",
    "typ":"JWT"
}

设置好header后,就可以生成header部分,具体的生成方式,就是把header部分使用base64 url编码即可

base64 url不是一个加密算法,而是一种编码方式,它是在base64算法的基础上对+=/三个字符做出特殊处理的算法

浏览器提供了btoa函数

const header = window.btoa(JSON.stringify({
    "alg":"HS256",
    "typ":"JWT"
}))
// 得到:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

同理通过atob函数解码

window.atob(header)
// 得到:{"alg":"HS256","typ":"JWT"}

3.3 payload

这部分是JWT的主体信息,它为json对象

  • ss:发行该jwt的是谁,可以写公司名字,也可以写服务名称
  • iat:该jwt的发放时间,通常写当前时间的时间戳
  • exp:该jwt的到期时间,通常写时间戳
  • sub:该jwt是用于干嘛的
  • aud:该jwt是发放给哪个终端的,可以是终端类型,也可以是用户名称,随意一点
  • nbf:一个时间点,在该时间点到达之前,这个令牌是不可用的
  • jti:jwt的唯一编号,设置此项的目的,主要是为了防止重放攻击(重放攻击是在某些场景下,用户使用之前的令牌发送到服务器,被服务器正确的识别,从而导致不可预期的行为发生)
{
  "ss""发行者",
  "iat""发布时间",
  "exp""到期时间",
  "sub""主题",
  "aud""听众",
  "nbf""在此之前不可用",
  "jti""JWT ID"
}

与header一样,payload也是通过base64 url 进行编码转换的

3.4 signature

这一部分是jwt的签名,这部分的生成,是对前面两个部分的编码结果,按照头部指定的方式进行加密 比如:头部指定的加密方法是HS256,前面两部分的编码结果是eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE1ODc1NDgyMTV9

则第三部分就是用对称加密算法HS256对字符串eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE1ODc1NDgyMTV9进行加密,当然你得指定一个秘钥,比如shhhhh

HS256(`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE1ODc1NDgyMTV9`, "shhhhh")
// 得到:BCwUy3jnUQ_E6TqCayc7rCHkx-vxxdagUwPOWqwYCFc

最终,将三部分组合在一起,就得到了完整的jwt

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE1ODc1NDgyMTV9.BCwUy3jnUQ_E6TqCayc7rCHkx-vxxdagUwPOWqwYCFc

由于签名使用的秘钥保存在服务器,这样一来,客户端就无法伪造出签名,因为它拿不到秘钥。

换句话说,之所以说无法伪造jwt,就是因为第三部分的存在。

而前面两部分并没有加密,只是一个编码结果而已,可以认为几乎是明文传输

这不会造成太大的问题,因为既然用户登陆成功了,它当然有权力查看自己的用户信息

甚至在某些网站,用户的基本信息可以被任何人查看

你要保证的,是不要把敏感的信息存放到jwt中,比如密码

3.5 面试 阐述JWT的令牌格式

token分为三个部分,分别为header、payload、signature 其中header为头部令牌,记录了token的类型和签名算法;payload为主体信息,包含令牌的过期时间、发布时间、发行者、主体内容等;signature为令牌签名,是根据前面两部分进行加密后得到的结果 token有防篡改的特点,如果攻击者改动了前面两个部分,会导致和第三个部分对应不上,使得token失效,而攻击者不知道第三部分的加密密钥,因此又无法修改第三部分的值 所以在密钥不泄露的前提下,一个验证通过的token是值得被信任的