reactjs - 如何在客户端 PrivateRoute 上授权 jwt 令牌?
问题描述
我正在尝试向后端的授权端点发送一个简单的 GET 请求,以验证成功验证(登录)后保存在 localStorage 中的用户 jwt 令牌。我已经设置了一个辅助函数(见下文),它将使用标头中的 jwt 令牌向服务器发出 GET 请求,如果收到状态 200,则我返回 true(已授权)。
这个辅助函数在我的 PrivateRoute HOC 中调用,如果为 true 将返回私有组件,如果为 false 将重定向到 Login。
基本上,如果我只是解码令牌并验证令牌是否存在并且因此用户已登录,则基本上我可以使受保护的路由正常工作。但是,这不是根据服务器 jwt verify 检查令牌,因此不安全。我不确定是否需要在任何地方实现异步代码?我知道验证步骤必须存在于后端,并且前端应该简单地发送带有标头中令牌的 API 请求,但我似乎无法使用 Private Routes 完成这项工作。
我还尝试在主 App 组件上运行辅助函数作为 useEffect 挂钩,并根据辅助函数是否返回授权更新“loggedIn”状态变量,然后可以将其作为道具传递给其他组件。我已经为我的导航栏组件执行此操作,以根据“登录”状态是真还是假来显示“登录/注册”或“注销”。
服务器端验证令牌功能:
const jwt = require('jsonwebtoken');
module.exports = function (req, res, next) {
const token = req.header('auth-token');
if(!token) return res.status(401).send('Access Denied');
try {
const decoded = jwt.verify(token, process.env.TOKEN_SECRET);
req.user = decoded;
next();
} catch (err) {
res.status(400).send('Invalid Token');
}
}
辅助功能:
import React from 'react';
import { Redirect } from 'react-router-dom';
import axios from 'axios';
const checkAuth = {
isAuthorised: false,
userID: void (0),
authorise: function () {
const token = window.localStorage.getItem("access_token");
if (!token) {
console.log('Invalid Token');
window.localStorage.setItem("access_token", null);
}
const response = axios({
method: 'get',
url: 'http://localhost:3001/api/verify',
headers: { 'auth-token': token }
})
const data = response.data;
if (response.status === 200) {
this.isAuthorised = true;
this.userID = data._id;
return this.isAuthorised;
}
return this.isAuthorised;
},
logout: function () {
window.localStorage.setItem("access_token", null);
this.isAuthorised = false;
return <Redirect to="/" />
},
}
export default checkAuth;
私人路线 HOC:
import React from 'react';
import { Redirect, Route } from 'react-router-dom';
import checkAuth from './helper';
const PrivateRoute = ({ component: Component, path, ...rest }) => {
return (
<Route
path={path}
{...rest}
render={props =>
checkAuth.authorise()
? <Component {...props} />
: <Redirect to={{ pathname: "/login" }} />
}
/>
);
}
export default PrivateRoute;
应用组件(隐藏导入):
const App = () => {
const [loggedIn, setLoggedIn] = useState(false);
const [user, setUser] = useState();
const verify = () => {
if (checkAuth.authorise()) {
setLoggedIn(true);
setUser(checkAuth.userID);
console.log('logged in');
return true;
} else {
console.log('not logged in');
return false;
}
}
useEffect(() => {
verify()
}, [loggedIn]);
return (
<div>
<NavBar loggedIn={loggedIn} user={user} />
<Router>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/story" component={Story} />
<Route path="/prologue" component={Prologue} />
<PrivateRoute path="/chapters" component={Chapters} />
<PrivateRoute path="/forum" component={Forum} />
<PrivateRoute path="/characters" component={Characters} />
<PrivateRoute path="/characters/:id" component={CharacterSheet} />
<Route path="/login" component={Login} />
<Route path="/register" component={Register} />
<Route component={NoMatch} />
</Switch>
</Router>
</div>
);
}
export default App;
NavBar 组件(仅相关部分):
const NavBar = ({ loggedIn, user }) => {
return (
...
{loggedIn ? (
<Nav>
<h1 style={{ paddingRight: '30px', color: 'black', fontSize: '32pt'}}>Welcome, {user}</h1>
<Nav.Link href="/" onClick={() => checkAuth.logout()}>Sign Out</Nav.Link>
</Nav>
) : (
<Nav>
<Nav.Link href="/login">Login</Nav.Link>
<Nav.Link href="/register">Register</Nav.Link>
</Nav>
)}
...
)
}
export default NavBar;
我希望当用户登录时,NavBar 会根据 useEffect 钩子调用的授权帮助函数更新以显示用户登录。
我还希望当用户路由到受保护的路由之一(PrivateRoute)时,辅助函数将运行并返回 true(登录后)并随后呈现。
当使用以下代码代替辅助函数中的 API 调用时,我得到了预期的结果(但据我了解,这不是正确的授权):
authorise: function () {
const token = window.localStorage.getItem("access_token");
if (!token) {
console.log('Invalid Token');
window.localStorage.setItem("access_token", "");
}
try {
const decoded = decode(token);
console.log(decoded)
if (decoded) {
this.isAuthorised = true;
this.userID = decoded._id;
return this.isAuthorised;
}
}
catch {
console.log('invalid token')
}
return this.isAuthorised;
},
目前,登录似乎什么也没做——尽管用户令牌被接收并存储在 LocalStorage 中。
解决方案
axios 返回一个承诺,因此,基本上,您需要将验证代码更改为以下内容:
return axios({
method: 'get',
url: 'http://localhost:3001/api/verify',
headers: { 'auth-token': token }
})
.then(response => {
const data = response.data;
if (response.status === 200) {
this.isAuthorised = true;
this.userID = data._id;
}
return this.isAuthorised;
});
然后也将checkAuth.authorise()
呼叫作为承诺处理
推荐阅读
- arrays - 如何将模拟结果的多个变量导出到 csv?
- shell - 重命名命令不评估表达式
- flutter - Flutter 模拟器 APK 包含所有 ABI
- php - 如何打开 csv 文件,逐行读取文件,base64_decode() 并将解码后的数据写入新文件?
- excel - Excel公式从文本中提取字符串
- angular - Zone.js 检测到 ZoneAwarePromise `(window|global).Promise` 已在自定义元素中被覆盖
- android - 如何强制现有小部件将尺寸更改为新尺寸?
- ruby-on-rails - 身份验证后无法在 Ruby on Rails 中呈现 React 应用程序
- amazon-web-services - AWS Cognito 授权方不使用适用于 Android 的 AWS 开发工具包
- python - SQLAlchemy过滤一对多关系中的孩子