这是一个老问题,但选择的答案并不完全安全。
对于网络而言,使用网络存储来存储敏感信息并不安全。
您应该使用仅限 http 的 cookie。仅 Http cookie 无法通过 Javascript 读取,但浏览器会自动将其发送到后端。
这里是 Nodejs-Express-TypeScript 代码示例;
在这个例子中,有两个 cookie 一个是 http-only,另一个不是。
非 http-only cookie 用于检查登录情况,如果它是有效的客户端假定用户已登录。但实际控制由后端完成。
PS:不需要存储或发送任何令牌,因为浏览器会自动处理它(cookies)。
const cookieConfig = {
httpOnly: true,
secure,
maxAge: 30 * 24 * 60 * 60 * 1000,
signed: secure,
}
const cookieConfigReadable = {
httpOnly: false,
secure,
maxAge: 30 * 24 * 60 * 60 * 1000,
signed: secure,
}
function signToken(unsignedToken: any) {
const token = jwt.sign(unsignedToken, privatekey, {
algorithm: 'HS256',
expiresIn: jwtExpirySeconds,
})
return token
}
const tokenObj = {
UID: userId,
SID: sessionId,
}
const token = signToken(tokenObj)
// sets session info to the http-only cookie
res.cookie('HSINF', token, cookieConfig)
// sets a cookie with expires=false value for client side check.
res.cookie('expired', false, cookieConfigReadable)
但是这种方法在调试过程中存在挑战,因为 NodeJS 和 Flutter Web 服务于不同的端口,你应该允许 CORS 用于开发环境。
if (process.env.NODE_ENV === 'dev') {
app.use(
cors({
origin: [
'http://localhost:8080',
'http://127.0.0.1:8080',
],
credentials: true,
}),
)
}
而且在 Flutter Web 中,你不能在调试时直接使用 http.get 或 http.post,因为 Flutter 默认禁用 CORS cookie。
这是一个解决方法。
// withCredentials = true is the magic
var client = BrowserClient()..withCredentials = true;
http.Response response;
try {
response = await client.get(
Uri.parse(url),
headers: allHeaders,
);
} finally {
client.close();
}
在调试方面还有另一个挑战;许多浏览器不允许 localhost 使用 cookie,您应该使用 127.0.0.1 代替。但是 Flutter Web 只针对特定的 url 和特定的端口运行,所以这是我的 VsCode 配置,让 Flutter 在 127.0.0.1 上运行
{
"name": "project",
"request": "launch",
"type": "dart",
"program": "lib/main.dart",
"args": [
"-d",
"chrome",
"--web-port",
"8080",
"--web-hostname",
"127.0.0.1"
]
}
这些用于设置和传输 cookie,您可以在下面找到我的后端 cookie 检查
const httpOnlyCookie = req.signedCookies.HSINF
const normalCookie = req.signedCookies.expired
if (httpOnlyCookie && normalCookie === 'false') {
token = httpOnlyCookie
}
if (token) {
let decoded: any = null
try {
decoded = jwt.verify(token, privatekey)
} catch (ex) {
Logger.error(null, ex.message)
}
if (decoded) {
//Cookie is valid, get user with the session id
}
}