这是我在这里发布的第一期,所以请耐心等待:D 我正在创建一个社交应用程序,目前我正在数据库关闭时实施回退。在我的
Login.jsx
组件中,我正在获取数据,后端如下所示:
fetch(getApiUrl('/auth/login'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(values),
credentials: 'include',
})
.then(res => {
const jsn = res.json();
if (res.ok) {
handleAuthLogin(navigate, revalidate)(jsn);
} else if (jsn.message) {
toast.error(<div className='text-lg'>{jsn.message}</div>);
}
})
.catch((err) => {
console.error(err);
if (err.message === 'Failed to fetch') {
navigate('/service-unavailable');
} else if (err.message === 'Invalid token' || err.message === 'Unauthorized') {
navigate('/login');
} else {
toast.error(<div className='text-lg'>Failed to connect to the server. Please check your connection.</div>);
}
});
然后在我的后端,我使用 JWT 来哈希密码。这只是我的
index.js
文件中的代码片段:
/**
* Middleware for handling authentication routes.
* All routes starting with '/auth' will be handled by the authRouter.
*/
app.use(checkToken);
app.use('/auth', authRouter);
app.use(userRouter);
// Set up the /users endpoint
app.get('/users', async (req, res) => {
const { name } = req.query.name;
// Query the database using Prisma
const users = await prisma.user.findMany({
where: {
name: {
contains: name,
},
},
});
// Send the result back to the client
res.json(users);
});
和 checkToken 组件:
const checkToken = (req, res, next) => {
const token = req.headers['authorization'];
console.log('token: ', token);
if (!token) {
return res.status(403).send({ message: 'No token provided.' });
}
try {
const decoded = decodeJwt(token);
req.userId = decoded.id;
next();
} catch (error) {
return res.status(401).send({ message: 'Unauthorized! Session expired.' });
}
};
以及表单验证:
const formVerification = (req, res, action) => {
// Extract the data from the request body
const data = req.body;
console.log(data);
// Validate the data against the schema
schema
.validate(data)
.catch(err => {
// If validation fails, send a 422 status code and the validation error messages
res.status(422).json({ status: 422, message: err.errors });
})
.then(valid => {
if (valid) {
const cred = {
usr: data.username,
pwd: data.password
};
if (action === "login") {
// Login handle
handleLogin(cred, req, res);
} else if (action === "signup") {
// Signup handle
handleSignup(cred, req, res);
} else {
throw new Error("Invalid action");
}
} else {
res.status(400).json({ status: 400, message: "Invalid request" });
}
});
}
使用 jwt 编码的handleLogin组件:
const hashPwd = (pwd) => {
return createHash("md5").update(pwd).digest("hex");
};
const encodeJwt = ({ id, name }) => {
return sign({ id, name }, secret, { expiresIn: "1h" });
};
const decodeJwt = (jwt) => {
try {
return verify(jwt, secret);
} catch (e) {
throw new Error('Failed to decode JWT: ' + e.message);
}
};
const handleLogin = ({ usr, pwd }, req, res) => {
console.log('usr: ', usr, 'pwd: ', pwd);
getUser(usr, (err, user) => {
if (err) {
console.error(err);
return res.status(500).json({ status: 500, message: "Database is offline" });
}
console.log('user: ', user);
if (!user) {
return res.status(404).json({ status: 404, message: "User not found" });
} else if (user.credentials.hash !== hashPwd(pwd)) {
return res.status(401).json({ status: 401, message: "Invalid password" });
} else {
return res.status(200).json({ status: 200, jwt: encodeJwt(user), user: { id: user.id, name: user.name } });
}
}, true);
};
您现在可能会意识到,很难确定错误到底在哪里,而且代码有点大,所以我将链接整个代码的存储库:
https://github.com/Patri22k/social-app
。
最后,错误是什么?在安装上我遇到错误
user.js:29 GET http://localhost:5000/user/@me 401 (Unauthorized)
。哦,顺便说一句,这是 user.js 文件:
// No token stored in local store
const NO_TOKEN_ERR = "no_token";
const UserContext = createContext(null);
const UserProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [fetching, setFetching] = useState(false);
const [error, setError] = useState(null);
// Obtain new data
const revalidate = () => {
// Obtain local JWT val
const jwt = localStorage.getItem("jwt");
if (!jwt) {
setError(NO_TOKEN_ERR);
return;
}
const init = {
method: "GET",
headers: {
'Authorization': `Bearer ${jwt}`
}
}
// Fetch that thing
fetch(getApiUrl("/user/@me"), init) // line 29
.then(res => res.json())
.then(({ user }) => setUser(user))
.catch((e) => setError(e.message))
.finally(() => setFetching(false))
}
// rest of the code
}
在我将任何值传递给表单后,令牌未定义,并且出现错误
POST http://localhost:5000/auth/login 403 (Forbidden)
。但在挂载点上有一个哈希令牌。
我想实现什么目标?
我想在数据库关闭时进行后备,它将重定向到 /service-unavailable
url。就是这样。当它打开时,它应该像任何具有表单验证的社交应用程序一样工作。
如果您需要使用
const token = req.headers['authorization'];
访问令牌,您需要在 POST 请求标头中手动传递令牌,如下所示:
fetch(getApiUrl('/auth/login'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${your_token}`
},
body: JSON.stringify(values),
credentials: 'include',
})
并且在你的后端你需要删除'Bearer',不要忘记它!
const token = req.headers['authorization'].split(' ')[1]
仅设置:
credentials: 'include'
不会将令牌放在正确的位置,这就是为什么你的令牌是undefined
并且http状态是401,就像你编程的那样。
不要担心错误如此简单;我花了很多天的时间才在我的代码中意识到这种事情......答案总是在我们从未想过会出错的事情中。
希望有帮助;)