前后端分离是基本是目前项目的趋势,客户端和服务端如何通讯,session,cookie,token等等,有太多方法了。以下是百度来的两者的区别。
1. 好处
那Token相对于Cookie的好处:
支持跨域访问: Cookie是不允许垮域访问的,token支持
无状态: token无状态,session有状态的
去耦: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在 你的API被调用的时候, 你可以进行Token生成调用即可.
更适用于移动应用: Cookie不支持手机端访问的
性能: 在网络传输的过程中,性能更好
基于标准化: 你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在 多个后端库(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如: Firebase,Google, Microsoft)
2 缺陷
说了那么多token认证的好处,但他其实并没有想象的那么神,token 也并不是没有问题。
1. 占带宽
正常情况下要比 session_id 更大,需要消耗更多流量,挤占更多带宽,假如你的网站每月有 10 万次的浏览器,就意味着要多开销几十兆的流量。听起来并不多,但日积月累也是不小一笔开销。实际上,许多人会在 JWT 中存储的信息会更多。
2. 无法在服务端注销,那么就很难解决劫持问题
3. 性能问题
JWT 的卖点之一就是加密签名,由于这个特性,接收方得以验证 JWT 是否有效且被信任。但是大多数 Web 身份认证应用中,JWT 都会被存储到 Cookie 中,这就是说你有了两个层面的签名。听着似乎很牛逼,但是没有任何优势,为此,你需要花费两倍的 CPU 开销来验证签名。对于有着严格性能要求的 Web 应用,这并不理想,尤其对于单线程环境。
其实咋看一下,都不是很清楚说啥。说说我实际开发中的经验,token就是服务端派发的一个通行证,只要有了这个通行证的访客,通行证可以复制,可以给任何人,有通行证的人都可以访问服务端给定的资源。服务端只对token这段字符进行验证,合法即为通过。
而session只保留在单一的客户端,访客关闭客户端即失效,但是session需要一个用户登录过程,不登入如何判断客户的唯一性?而token是无状态的,就是一串字符串,再怎么加密,对于客服来说就是一大串字符串,服务端在对这段字符串就行解密,判断是否合法,获取payload载荷。
在前后端分离的项目中,token是唯一的选择,至于如何实现token的安全唯一性,可以通过redis来解决,这篇文章不多阐述,这里仅仅记录一下,如下在node下实现token通讯。
一,安装apenssl,这里就不阐述安装过程了,安装成功后,进入bin目录,打开openssl.exe。
会出来命令行,然后输入:
kcs8 -topk8 -inform PEM -in 文件名.pem -outform PEM –nocrypt
然后在bin目录下就会生成此文件,打开一下就是如此界面:
二,继续在命令行输入以下指令:
rsa -in 刚才的文件名.pem -pubout -out rsa_public_key.pem
至此公钥密钥都生成了,把这两个文件复制到node项目中,如下图:
三,npm安装jsonwebtoken模块,然后新建一个JWT.js的文件,封装一下,我这里是吧密钥都放在全局变量中了,实际开发中可以根据自己情况来定夺,代码如下:
// 引入模块依赖
import jwt from 'jsonwebtoken'
// iss:Issuer,发行者
// sub:Subject,主题
// aud:Audience,观众
// exp:Expiration time,过期时间
// nbf:Not before
// iat:Issued at,发行时间
// jti:JWT ID
/**
* 根据载荷生成token,返回生成的token等信息
*
* @param {object} payload
* @return {string}
* @public
*/
export const generateToken = (payload, expiresIn) => {
let createtime = Math.floor(Date.now() / 1000)
//payload.iat = createtime
payload.iss = 'hzkjseat'
//payload.exp = createtime + expiresIn
// console.log(payload)
const cert = global.config.tokenPrivateKey //从全局变量里读取,
let access_token = jwt.sign(
{ payload },
cert,
{ expiresIn, algorithm: 'RS256' }
)
return {
access_token,//token
iat:createtime,//发行时间
exp:createtime + expiresIn//过期时间
}
}
/**
* 验证token,验证成功则返回载荷
* @param {string} token
*/
export const verifyToken = async (token) => {
try {
let cert = global.config.tokenPublicKey
let data = (jwt.verify(token, cert, { algorithm: 'RS256' })) || {}
return data
} catch (e) {
throw e
}
}
四,用户使用凭证,我项目中是通过id和apikey来获取凭证的,代码如下(仅仅是示例):
export default async (req, res, next) => {
const { id, key } = req.query
if (!ObjectID.test(id) || !ApiKey.test(key)) {
next(ApiKeyFail)
}
const tokenExpiresIn = 60 * 60 * 4 // 单位秒 设置token过期时间和redis本身一致
const result =generateToken({name:'shuanghei'},tokenExpiresIn)
next(result)
}
最后,然后设定一个路由守卫,所有请求必须先通过此中间件。
代码如下:
import {verifyToken } from '../utils/JWT.js'
export default async (req, res, next) => {
let reqtoken = req.headers.authorization
console.log(verifyToken(reqtoken))//判断token是否合法
next()
}
这次,初步完成node项目下,token的通讯了,具体业务逻辑,还需要根据项目需求来修改。