很多开发者习惯把JWT令牌存在localStorage里,但这样做其实有安全风险。如果网站存在XSS漏洞,攻击者可以轻易偷走令牌。换个方法,把令牌放在HttpOnly Cookie里,能解决这个问题。
HttpOnly Cookie有三个好处:
JavaScript无法读取,XSS攻击偷不走令牌
浏览器自动在每次请求中携带,不需要手动设置请求头
可以使用安全标志,降低CSRF和窃听风险
只需要一些配置,就能大幅提升安全性。
先搭建一个简单的Express服务器:
// server/index.mjs
import express from 'express';
import cookieParser from 'cookie-parser';
import csurf from 'csurf';
const app = express();
app.use(express.json());
app.use(cookieParser());
// 设置CSRF保护
app.use(csurf({ cookie: true }));
// 获取CSRF令牌的接口
app.get('/api/csrf-token', (req, res) => {
res.setHeader('x-csrf-token', req.csrfToken());
res.sendStatus(204);
});
这段代码设置了JSON解析、Cookie解析和CSRF保护。之后所有POST、PUT、DELETE请求都需要提供CSRF令牌。
import jwt from 'jsonwebtoken';
const isProd = process.env.NODE_ENV === 'production';
app.post('/api/token', (req, res) => {
const { username, password } = req.body;
// 这里应该验证用户名和密码
if (!isValidUser(username, password)) {
return res.status(401).json({ error: '用户名或密码错误' });
}
const accessToken = jwt.sign({ sub: username }, process.env.ACCESS_SECRET, {
expiresIn: '15m'
});
const refreshToken = jwt.sign({ sub: username }, process.env.REFRESH_SECRET, {
expiresIn: '7d'
});
res
.cookie('access_token', accessToken, {
httpOnly: true,
sameSite: 'strict',
secure: isProd,
maxAge: 15 * 60 * 1000
})
.cookie('refresh_token', refreshToken, {
httpOnly: true,
sameSite: 'strict',
secure: isProd,
maxAge: 7 * 24 * 60 * 60 * 1000
})
.json({ user: username, status: 'active' });
});
验证通过后生成两个令牌,都设置为HttpOnly Cookie,JavaScript无法读取,但浏览器会自动发送。
app.post('/api/token/refresh', (req, res) => {
const refreshToken = req.cookies.refresh_token;
if (!refreshToken) return res.sendStatus(401);
jwt.verify(refreshToken, process.env.REFRESH_SECRET, (err, decoded) => {
if (err) return res.sendStatus(403);
const newAccess = jwt.sign({ sub: decoded.sub }, process.env.ACCESS_SECRET, {
expiresIn: '15m'
});
const newRefresh = jwt.sign({ sub: decoded.sub }, process.env.REFRESH_SECRET, {
expiresIn: '7d'
});
res
.cookie('access_token', newAccess, {
httpOnly: true,
sameSite: 'strict',
secure: isProd,
maxAge: 15 * 60 * 1000
})
.cookie('refresh_token', newRefresh, {
httpOnly: true,
sameSite: 'strict',
secure: isProd,
maxAge: 7 * 24 * 60 * 60 * 1000
})
.json({ ok: true });
});
});
这个接口用刷新令牌来获取新的访问令牌,保持用户登录状态。
获取CSRF令牌:
async function getCSRF() {
const res = await fetch('/api/csrf-token', {
credentials: 'include'
});
return res.headers.get('x-csrf-token');
}
登录函数:
const loginUser = async (formData) => {
const csrf = await getCSRF();
const res = await fetch('/api/token', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrf
},
body: JSON.stringify(formData)
});
if (res.ok) {
const data = await res.json();
// 只存储非敏感信息
localStorage.setItem('username', data.user);
} else {
throw new Error('登录失败');
}
};
自动刷新令牌:
useEffect(() => {
const refreshInterval = setInterval(async () => {
try {
const csrf = await getCSRF();
await fetch('/api/token/refresh', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrf
}
});
} catch (error) {
console.log('令牌刷新失败');
}
}, 14 * 60 * 1000); // 每14分钟刷新一次
return () => clearInterval(refreshInterval);
}, []);
文件上传
const uploadFile = async (file) => {
const formData = new FormData();
formData.append('file', file);
await fetch('/api/upload', {
method: 'POST',
body: formData,
credentials: 'include'
});
};
Next.js服务端渲染
export async function getServerSideProps({ req }) {
const token = req.cookies.access_token;
// 用令牌获取用户数据
return { props: { userData } };
}
WebSocket连接
const socket = new WebSocket(
`wss://example.com/chat?token=${getTokenFromCookie()}`
);
移动端React Native
// 使用Keychain或SecureStore存储令牌
import Keychain from 'react-native-keychain';
const storeToken = async (token) => {
await Keychain.setGenericPassword('token', token);
};
设置合适的令牌过期时间
生产环境一定要启用Secure标志
使用SameSite防止CSRF攻击
定期轮换密钥
监控异常登录行为
从localStorage切换到HttpOnly Cookie管理JWT令牌,虽然需要一些额外配置,但能显著提高应用安全性。这种方法适合大多数Web应用,特别是需要较高安全要求的项目。
现代浏览器都支持这些特性,现在就开始升级你的认证系统吧。记住,安全是一个持续的过程,需要定期审查和更新安全措施。
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!
angular的4200端口 向 nodejs的8080端口 发送一个post请求, 这会造成跨域问题,跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制
实际开发中有要求用户一些行为每天一次,次日开始重新回复功能,一般前端都是通过cookie来记住用户的操作,然后进行判断当日是否还有机会,这时候需要给存储的cookie值一个有效期,让次日自动失效,重新计数,代码如下:
我在做面试官的时候,曾经问过很多朋友这个问题: Cookie 和 Session 有什么区别呢?大部分的面试者应该都可以说上一两句,比如:什么是 Cookie?什么是 Session?两者的区别等。
浏览器已经接收指令,之前在一级域名下存储了相关的信息。这里为了简化问题,假设我们有两个应用A和B,域名分别为:a.b.com和c.a.b.com。(显然B是A的一个子域)。
定义:让网站服务器把少量数据储存到客户端的硬盘或内存,从客户端的硬盘读取数据的一种技术;下载与引入:jquery.cookie.js基于jquery;先引入jquery,再引入:jquery.cookie.js
Cookie 是在您的计算机上存储在小的文本文件中的数据。当 web 服务器向浏览器发送网页后,连接被关闭,服务器会忘记用户的一切。Cookie 是为了解决“如何记住用户信息”而发明的:若要想修改某个cookie,只需要用此种方法
创建:this.$配置时候设置的名称.set(\\\'cookies的key\\\',value),获取指定的key:this.$配置时候设置的名称.get(\\\'cookies的key`),获取所有keys返回为数组的形式:this.$配置时候设置的名称.keys (\\\'cookies的key`)
存储cookie(key为test;value为testValue);而在cookie的名中我们可以控制,但要保存的值是不确定的,怎么办呢?这时就需要用escape()函数进行编码,它能将一些特殊符号使用十六进制表示
Web 服务器和 HTTP 服务器是无状态的,因此当 Web 服务器将网页发送到浏览器时,连接会被断开,服务器会忘记与用户相关的所有内容。那么浏览器和 Web 服务器是怎样记住用户信息的?cookie 被发明出来解决这个问题。
CookieUtil.get() 方法根据 cookie 的名字获取相应的值。它会在 document.cookie 字符串中查找 cookie 名加上等于号的位置。如果找到了,那么使用 indexOf() 查找该位置之后的第一个分号(表示了该 cookie 的结束位置)。如果没有找到分号
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!