登录凭证
  • 为什么需要登录凭证
    • http是无状态协议
    • 登录成功后,已用户身份访问其他数据和资源,还是通过http访问,服务器不知道上一步做了什么,必须有个方法证明我们登录过

cookie

  • 总是保存在客户端中,按在客户端的存储位置,cookie可以分为内存cookie和硬盘cookie
    • 没有设置过期时间,默认是内存cookie关闭浏览器自动删除
    • 由设置过期时间,并且过期时间不为0或者负数的cookie是硬盘cookie,需要手动清理或到期
    • 内存cookie由浏览器维护,保存在内存中,浏览器关闭时cookie就会消失,其存在时间是短暂的
    • 硬盘cookie保存在硬盘中,有一个过期时间,用户手动清理或者过期时间到时,才会被清理
  • 常见属性
    客户端设置document.cookie = 'name=lp;max-age=5;'服务端设置
    (maxAge对应毫秒)
    ctx.cookies.set('name','lp',{  maxAge:5*1000})
    ctx.cookies.get('name')复制代码
    • Domain:指定哪些主机可以接受cookie
    • Path:指定主机下哪些路径可以接受cookie(例如Path=/docs,则以下地址都会匹配/docs,/docs/Web,/docs/Web/HTTP)
    • 如果不指定,默认origin,不包括子域名
    • 指定Domain,包含子域名(例如Domain=mozilla.org,则cookie也包含在子域名中)
    • 默认是内存cookie,也称之为会话cookie,浏览器关闭时自动删除
    • 通过设置expires或者max-age来设置过期时间
    • expires:设置的是Date.toUTCString(),设置格式是expires=date-in-GMTString-format
    • max-age:设置过期的秒钟,max-age=max-age-in-seconds(例如606024*365)
    • 生效周期
    • 作用域(允许cookie发送给哪些URL)

session

  • 依赖cookie

  • 不能在客户端设置

    安装koa-sessionconst Session = require('koa-session')
    创建session配置const session = Session({  key:'sessionid',  maxAge:5*1000,  signed:false,// 是否使用加密签名},app)
    app.keys=['aaa']
    app.use(session)
    
    在接口使用
    ctx.session.user={id,name}复制代码

cookie和session缺点

  • cookie会被附加到每个HTTP请求,无形增加了流量
  • cookie是明文传递,存在安全性问题
  • cookie大小限制是4KB,对复杂需求是不够的
  • 对于浏览器的其他客户端(ios\Android),每次请求必须手动的设置cookie和session
  • 对分布式系统和服务器集群中如何可以保证其他系统也可以正确的解析共享session?
  • 后两点最大的缺点

Token

  • 可以翻译为令牌
  • 在验证了用户名和密码正确的情况下,给用户颁发一个凭证
  • 这个令牌作为后续用户访问一些接口或者资源的凭证
  • 根据这个凭证来判断用户是否有权限来访问
  • 因此token使用分为两个步骤:
    • 生成token:登录的时候,颁发token
    • 验证token:访问某些资源或接口时,验证token

JWT(json web token)

  • header
    • alg:采用加密算法,默认是HMAC SHA256(HS256),采用同一个密钥进行加密和解密(对称加密)
    • typ:JWT,固定值
    • 会通过base64Url算法进行编码
  • payload
    • 携带的数据,比如name、id
    • 默认携带iat(issued at),令牌签发时间
    • 设置过期时间,exp(expiration time)
    • 会通过base64Url算法进行编码
  • signature
    //安装jsonwebtokenconst jwt = require('jsonwebtoken')//在接口中使用//返回tokenconst user = {id:1,name:'lp'}const SCRECT_KEY = 'anbfcfakn1345'const token = jwt.sign(user,SCRECT_KEY,{ // PRIVATE_KEY
      expiresIn:10, //单位是秒
      // algorithm:'RS256',// 非对称加密时,指定算法})
    ctx.body = token//验证tokenconst authorization = ctx.headers.authorizationconst token = authorization.replace('Bearer ','')try {  const result = jwt.verify(token,SCRECT_KEY,{// PUBLIC_KEYalgorithm:['RS256']
      })
      ctx.body = result
    }catch(error){
      ctx.body = 'token是无效的'}  
    复制代码
    • 设置一个secretKey,通过将前两个的结果合并后进行HMACSHA256的算法
    • HMACSHA256(base64Url(header)+.+base64Url(payload),secretKey)
    • 但如果secretKey暴露是非常危险的,因为之后可以模拟颁发token,也可以解密token

非对称加密RS256

  • 私钥 private key:用于发布令牌
  • 公钥 public key :用于验证令牌
  • openssl
    • 生成私钥:genrsa -out private.key 1024
    • 生成公钥:rsa -in private.key -pubout -out public.key
  • 替换SCRECT_KEY