当前位置: 首页 > wzjs >正文

前端工程师做交互网站WordPress 不显示主题页

前端工程师做交互网站,WordPress 不显示主题页,怎么做美瞳网站,东莞常平有高铁站吗node.js如何实现双 Token Cookie 存储 无感刷新机制 为什么要实施双token机制? 优点描述安全性Access Token 短期有效,降低泄露风险;Refresh Token 权限受限,仅用于获取新 Token用户体验用户无需频繁重新登录,Toke…

node.js如何实现双 Token + Cookie 存储 + 无感刷新机制

为什么要实施双token机制?

优点描述
安全性Access Token 短期有效,降低泄露风险;Refresh Token 权限受限,仅用于获取新 Token
用户体验用户无需频繁重新登录,Token 自动刷新过程对用户透明
灵活性独立控制不同 Token 的生命周期,适应各种场景需求
可管理性支持多设备登录管理,便于撤销特定设备的登录状态
性能优化减少数据库查询次数,提升系统响应速度

实现方案:

模块实现方式
登录接口返回 accessTokenrefreshToken,分别存入 Cookie
Access Token短时效 JWT,用于请求鉴权
Refresh Token长时效 JWT,用于刷新 Access Token
Token 校验方式后端从 Cookie 中读取 token(即 Access Token)
前端 Axios使用响应拦截器统一处理 Token 失效和自动刷新
  • 使用 JWT 生成两个 Token:
    • Access Token(短时效):用于接口认证,例如有效期为 15 分钟
    • Refresh Token(长时效):用于刷新 Access Token,例如有效期为 7 天
  • 在用户登录时返回这两个 Token,并将 Refresh Token 存储在数据库中
  • 当 Access Token 过期后,客户端使用 Refresh Token 请求新的 Access Token
  • 如果 Refresh Token 也过期或无效,则强制重新登录

具体代码实现

1. 安装依赖:

cookie-parser用来解析 Cookie 中的 Token

npm install jsonwebtoken bcryptjs cookie-parser

2. 数据库添加两个字段

refresh_tokenVARCHAR(255)加密后的 RefreshToken
expires_atDATETIMERefreshToken 过期时间

3. 在后端cors跨域中间中添加属性

// 将cors注册为全局中间件
app.use(cors({origin: 'http://localhost:5173', // 前端地址credentials: true // 👈 允许携带凭证(cookies)
}))

3. 登录逻辑改造(添加双token)

  1. 添加配置文件config.js
module.exports = {jwtSecretKey: 'yke;eky1]239_jwt87-2up34',refreshTokenSecretKey: 'yke;eky1]239_refresh87-2up34',accessExpiresIn: '15m',  // 访问令牌有效期refreshExpiresIn: '7d',   // 刷新令牌有效期accessExpiresInSec: 15 * 60,  // 秒数refreshExpiresInSec: 7 * 24 * 60 * 60  // 秒数
}
  1. jwt生成accessToken访问token、refreshToken刷新token
// 生成access token
const accessToken = jwt.sign({ id: user.id, username: user.username, email: user.email },config.jwtSecretKey,{ expiresIn: config.accessExpiresIn }
)
// 生成refresh token
const refreshToken = jwt.sign({ id: user.id, username: user.username, email: user.email },config.refreshTokenSecretKey,{ expiresIn: config.refreshExpiresIn }
)
  1. 生成token过期时间,和refreshToken一起存入数据库
 const expiresAt = new Date()expiresAt.setSeconds(expiresAt.getSeconds() + config.refreshExpiresInSec)
  1. 将accessToken访问token、refreshToken刷新token存入cookie
// 设置cookie
res.cookie('token', accessToken, {maxAge: config.accessExpiresInSec * 1000,httpOnly: true,secure: true,path: '/'
})
res.cookie('refresh_token', refreshToken, {maxAge: config.refreshExpiresInSec * 1000,httpOnly: true,secure: true,path: '/api/user/refresh-token',   // 限制路径提高安全性sameSite: 'none'
})

登录逻辑完整代码:

// 用户登录的处理函数
exports.login = (req, res) => {// 接收表单数据const userInfo = req.bodyconsole.log(userInfo)// 查询用户信息const sqlStr_name = 'select * from user where username=?'db.query(sqlStr_name, [userInfo.username], (err, results) => {if (err) {return res.send({ status: 1, message: err })}// 执行sql语句成功,但是获取的条数不等于1if (results.length === 0) {return res.send({ status: 1, message: '该用户不存在' })}// 判断密码是否正确const cmpresult = bcrypt.compareSync(userInfo.password, results[0].password)if (!cmpresult) {return res.send({ status: 1, message: '密码错误' })}// 在服务器端生成Token字符串const user = { ...results[0] }// 生成access tokenconst accessToken = jwt.sign({ id: user.id, username: user.username, email: user.email },config.jwtSecretKey,{ expiresIn: config.accessExpiresIn })// 生成refresh tokenconst refreshToken = jwt.sign({ id: user.id, username: user.username, email: user.email },config.refreshTokenSecretKey,{ expiresIn: config.refreshExpiresIn })// 将refresh token存储到数据库中const expiresAt = new Date()expiresAt.setSeconds(expiresAt.getSeconds() + config.refreshExpiresInSec)const sqlStr_refreshToken = 'update user set refresh_token=?, expires_at=? where id=?'db.query(sqlStr_refreshToken, [refreshToken, expiresAt, user.id], (err) => {if (err) {console.error('保存refreshToken失败:', err)return res.send({ status: 1, message: '保存refreshToken失败' })}// 设置cookieres.cookie('token', accessToken, {maxAge: config.accessExpiresInSec * 1000,httpOnly: true,secure: true,path: '/'})res.cookie('refresh_token', refreshToken, {maxAge: config.refreshExpiresInSec * 1000,httpOnly: true,secure: true,path: '/api/user/refresh-token',   // 限制路径提高安全性sameSite: 'none'})res.send({status: 0,message: '登录成功',data: {username: results[0].username}})})  })
}

4. 实现token刷新接口

创建新路由/refreshToken

// token刷新接口
exports.refreshToken = (req, res) => {// 直接从cookie中获取刷新token => 前端不需要再单独把token传入请求头const refreshToken = req.cookies.refresh_token// 判断refresh token是否存在if (!refreshToken) {return res.send({ status: 1, message: '缺少refreshToken,请先登录' })}try {// 验证refreshTokenconst decoded = jwt.verify(refreshToken, config.refreshTokenSecretKey)// 查询用户是否存在且refreshToken匹配const sql = 'select * from user where id=? and refresh_token=?'db.query(sql, [decoded.id, refreshToken], (err, results) => {if (err) {return res.send({ status: 1, message: '无效的refreshToken' + err.message })}const user = results[0]// 生成新的access tokenconst accessToken = jwt.sign({ id: user.id, username: user.username, email: user.email },config.jwtSecretKey,{ expiresIn: config.accessExpiresIn })// 更新accessToken到Cookieres.cookie('token', accessToken, {maxAge: config.accessExpiresInSec * 1000,httpOnly: true,secure: true,path: '/'})res.send({status: 0,message: 'accessToken刷新成功',data: {token: accessToken}})})} catch (error) {return res.status(403).send({ status: 1, message: 'token已过期,请重新登录' })}
}

5. 响应拦截器中处理token

import axios from 'axios'
import { message } from 'antd'
import { refreshTokenService } from '@/api/user'const instance = axios.create({baseURL: 'http://localhost:3333',  // 你的API服务器地址timeout: 10000,  // 请求超时时间headers: {'Content-Type': 'application/json'},// 必须加上这个选项才能跨域携带withCredentials: true
})// 添加请求拦截器
instance.interceptors.request.use((config) => {// 后端将token存在了cookie中,这里不需要携带tokenreturn config},(err) => Promise.reject(err)
)// 标记是否正在刷新 Token(防止并发刷新)
let isRefreshing = false
// 保存所有因 Token 失效而等待新 Token 的请求回调函数
let refreshSubscribers = []
// 成功获取到新的 Token 后,执行所有等待的请求
function onRefreshed(newToken) {refreshSubscribers.forEach((cb) => cb(newToken))refreshSubscribers = []
}
// 将等待刷新 Token 的请求封装成一个回调函数,加入队列中
function addRefreshSubscriber(callback) {refreshSubscribers.push(callback)
}
// 响应拦截器
instance.interceptors.response.use((res) => {console.log(res) // 摘取核心响应数据if (res.data.status === 0) {return res}// 处理业务失败message.error({type: 'error', content: res.data.message || '服务异常'})return Promise.reject(res.data)},async (err) => {// 错误的特殊情况 => 401权限不足或token过期 => 拦截到登录const originalRequest = err.config//  判断是否是 401 并且不是已经重试过的请求if (err.response?.status === 401 && !originalRequest._retry) {originalRequest._retry = true// 控制 Token 刷新流程(防止多次刷新)if (!isRefreshing) {// 标记刷新状态isRefreshing = truetry {const res = await refreshTokenService()const newToken = res.data.data.token// 重试请求onRefreshed(newToken)} catch {// 刷新失败message.error({ type: 'error', content: '登录已过期,请重新登录' })// 跳转登录if (window.location.pathname !== '/login') {history.push('/login')}} finally {isRefreshing = false}}// 把当前请求放入队列,等待 Token 刷新后再重发return new Promise((resolve) => {addRefreshSubscriber((newToken) => {originalRequest.headers['Authorization'] = `Bearer ${newToken}`resolve(instance(originalRequest))})})} else {// 错误的默认情况 =》 只给提示message.error({ type: 'error', content: err.response.data.message || '服务异常' })}return Promise.reject(err)}
)export default instance

文章转载自:

http://IpRIvC4h.jrLgz.cn
http://aF9KzX3L.jrLgz.cn
http://J5VIXQnE.jrLgz.cn
http://bRWuBAOv.jrLgz.cn
http://6tr82sdK.jrLgz.cn
http://AZoIqeiI.jrLgz.cn
http://Dg02M6ps.jrLgz.cn
http://fdypK98b.jrLgz.cn
http://6ajpQrLE.jrLgz.cn
http://Brv68TxD.jrLgz.cn
http://coJLA3Yy.jrLgz.cn
http://BLRoZxpm.jrLgz.cn
http://irBhTVFH.jrLgz.cn
http://I9lcEsMp.jrLgz.cn
http://Ugq8dsZq.jrLgz.cn
http://IFt967Tz.jrLgz.cn
http://H4421ZiZ.jrLgz.cn
http://UeWpwzxW.jrLgz.cn
http://xhUNgUMs.jrLgz.cn
http://z8bSNqBS.jrLgz.cn
http://CzG0ZTC9.jrLgz.cn
http://ZtSMLkTH.jrLgz.cn
http://Gc7vSlVg.jrLgz.cn
http://v1lMIBvc.jrLgz.cn
http://1CV8zAey.jrLgz.cn
http://LxNxkj7F.jrLgz.cn
http://I4reO3dv.jrLgz.cn
http://YWLZ0ggS.jrLgz.cn
http://bZloDFsR.jrLgz.cn
http://lc55x4Bl.jrLgz.cn
http://www.dtcms.com/wzjs/718642.html

相关文章:

  • 网站架构设计师工作内容可以看网站的浏览器有哪些
  • dede网站栏目管理如何建设网上接单干活的平台
  • 佛山市骏域网站建设wordpress火车头采集软件发布接口
  • 最好的app制作网站如何在微信上开小程序
  • 佛山网站建设多少钱推广方法
  • 网站服务器租用价格多少钱一年怀化网络推广
  • 猪八戒 网站开发支付琼海建设网站
  • 云主机搭建网站免费学生html网页制作成品
  • 做电影网站一年赚多少钱网站添加flash
  • 网站初始开发的步骤长沙做官网的公司
  • 商务网站页面餐饮最有效的营销方案
  • 惠民县建设局网站seo站长工具 论坛
  • 加强网站基础建设网站建设学习网
  • 廊坊网站seo排名wordpress中用户权限
  • 网站建设制作博走重庆优化网站公司
  • 成都做网站设计哪家最权威wordpress单页插件
  • 梧州网站建设推荐开发外贸网站开发
  • 票务网站开发百度热议排名软件
  • 兴义市建设局网站首页淘宝领卷网站什么做
  • 免费企业自助建站信息发布网做网站公司无锡
  • 做h5的图片网站正在建设中网站
  • 网站建设话术关键词校园网站建设的基本条件
  • 大连市住房与城乡建设部网站网站域名解析到了空间 但空间未绑定此域名
  • 国土局网站建设方案网站建设的七个流程步骤
  • 精品网站导航 做最好的导航玉林博白网站建设
  • 简单的静态网站首页企业微信开发文档
  • 天津网站建设中心设计网站推荐p
  • 建设银行福建分行招聘网站idc数据中心排名
  • 做外贸的网站有何用处购物网站名字大全
  • 还有网站吗九龙坡网站建设