使用 React Router 保护 React 应用中的路由
Protecting routes in React app with React Router
我用 Redux
、React Router
和 Auth0
创建了一个简单的 React
应用程序来处理用户身份验证。
我正在尝试创建此基本行为来控制访问:
- 所有未经身份验证的用户将自动发送到
/public
- 经过身份验证的用户可以访问应用程序的所有其他部分
- 一旦用户通过
Auth0
的身份验证,我想处理 access_token
并将用户发送到 /
,即 Home
组件
一切都"almost" 正常工作。我遇到的问题是 App.jsx
中的 render()
函数在 lock.on('authenticated')
侦听器甚至有机会处理 Auth0
返回的标记之前执行。因此,令牌永远不会被存储,用户似乎总是未经身份验证。如果我将用户发送到 /login
,一切正常,因为我没有在呈现 Login
组件之前检查用户是否已通过身份验证。
我认为我处理受保护路线的方式需要改变。关于如何处理受保护的路由有什么建议吗?
我在此处提供您需要的代码。如果您想查看整个应用程序,请转到 https://github.com/imsam67/react-redux-react-router-auth0-lock
以下是App.jsx
:
class App extends Component {
render() {
const isAuthed = isAuthenticated();
return (
<div>
<Switch>
<Route exact path="/" render={ props => isAuthed ? <Home {...props} /> : <Redirect to="/public" /> } />
<Route exact path="/login">
<Login />
</Route>
<Route path="/public">
<Public />
</Route>
</Switch>
</div>
);
}
}
这是我处理的 AuthWrapper
组件 Auth0
:
class AuthWrapper extends Component {
constructor(props) {
super(props);
this.onAuthenticated = this.onAuthenticated.bind(this);
this.lock = new Auth0Lock('my_auth0_client_id', 'my_domain.auth0.com', {
auth: {
audience: 'https://my_backend_api_url',
redirectUrl: 'http://localhost:3000/',
responseType: 'token id_token',
sso: false
}
});
this.onAuthenticated();
}
onAuthenticated() {
debugger; // After successful login, I hit this debugger
this.lock.on('authenticated', (authResult) => {
debugger; // But I never hit this debugger
let expiresAt = JSON.stringify((authResult.expiresIn * 1000) + new Date().getTime());
sessionStorage.setItem('access_token', authResult.accessToken);
sessionStorage.setItem('id_token', authResult.idToken);
sessionStorage.setItem('expires_at', expiresAt);
});
}
render() {
return(
<AuthContext.Provider value={{ lock: this.lock }}>
{this.props.children}
</AuthContext.Provider>
);
}
}
这是 index.js
以备不时之需:
import App from './components/App';
import AuthWrapper from './components/auth/AuthWrapper';
// Store
import appStore from './store/app-store';
const store = appStore();
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<AuthWrapper>
<App />
</AuthWrapper>
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
动态路由需要在 <Switch>
范围之外定义。这是一个例子,假设你的函数 isAuthenticated() 是一个状态(Redux 或 wathever)
import React from "react";
import ReactDOM from "react-dom";
import { createBrowserHistory } from "history";
import { Router, Route, Switch, Redirect } from "react-router-dom";
// core components
import Admin from "layouts/Admin.js";
import SignIn from "layouts/SignIn";
const hist = createBrowserHistory();
const loggedRoutes = () => (
<Switch>
<Route path="/" component={SignIn} />
<Route path="/admin" component={Admin} />
<Redirect from="/admin" to="/admin/aboutUs/whatWeDo" />
</Switch>
);
const routes = () => (
<Switch>
<Route path="/" component={SignIn} />
<Route path="/login" component={Admin} />
<Redirect from="/" to="/login" />
</Switch>
);
ReactDOM.render(
<Router history={hist}>
{checkIfAuth? loggedRoutes():routes()}
</Router>,
document.getElementById("root")
);
在这个例子中,如果您没有登录,您将被重定向到/login。
以下是我为完成这项工作所做的更改。
- 我现在在
index.js
中初始化 Auth0 Lock
使其成为全局
- 我将
onAuthenticated()
侦听器移到了 LoginCallback.jsx
组件,这是用户在成功登录后被发送到的地方。我相信将 onAuthenticated()
从 App.jsx
移动到 LoginCallback.jsx
产生了最大的影响,因为 LoginCallback.jsx
中的侦听器在 App.jsx
. 之前执行
- 身份验证成功后,我还使用
this.props.history.push('/');
将用户发送到 Home
组件
有关完整代码,请参阅位于 https://github.com/imsam67/react-redux-react-router-auth0-lock
的存储库
请记住,客户端根本没有任何保护。如果您担心路由到没有身份验证的组件,只需确保没有数据被暴露(假设他们没有令牌就无法获取任何数据)并且即使在您的路由器检查身份验证或显示错误后它们到达那里也会重定向。
请记住,客户端根本没有任何保护。如果您担心路由到未经身份验证的组件,只需确保没有数据被暴露,即使在您的路由器检查身份验证后它们到达那里,也可以重定向。我认为@Sam 是对的。路由可能不会像预期的那样响应异步调用改变它,或者可能有奇怪的行为。我从来没有以这种方式尝试过动态路由,但总是有条件地呈现内容组件。更好的方法可能是发送调用并在 then 块中重定向到路由器知道要处理的 url。只是不确定路由器是否能很好地处理这个问题。捕获加载时检查身份验证的组件,如果未授权则重定向回登录。抱歉,我在这里帮不了什么忙,但条件路由几乎看起来像是一种反模式,但我想如果我们知道路由器在更改后如何呈现其数据,或者它是否确实如此(路由本身),它可能会起作用。所以如果他们要为 url 添加书签并尝试 return 返回 404 对吗?也许像 401 未经授权的显示和重定向或 link 登录可能更好?
我用 Redux
、React Router
和 Auth0
创建了一个简单的 React
应用程序来处理用户身份验证。
我正在尝试创建此基本行为来控制访问:
- 所有未经身份验证的用户将自动发送到
/public
- 经过身份验证的用户可以访问应用程序的所有其他部分
- 一旦用户通过
Auth0
的身份验证,我想处理access_token
并将用户发送到/
,即Home
组件
一切都"almost" 正常工作。我遇到的问题是 App.jsx
中的 render()
函数在 lock.on('authenticated')
侦听器甚至有机会处理 Auth0
返回的标记之前执行。因此,令牌永远不会被存储,用户似乎总是未经身份验证。如果我将用户发送到 /login
,一切正常,因为我没有在呈现 Login
组件之前检查用户是否已通过身份验证。
我认为我处理受保护路线的方式需要改变。关于如何处理受保护的路由有什么建议吗?
我在此处提供您需要的代码。如果您想查看整个应用程序,请转到 https://github.com/imsam67/react-redux-react-router-auth0-lock
以下是App.jsx
:
class App extends Component {
render() {
const isAuthed = isAuthenticated();
return (
<div>
<Switch>
<Route exact path="/" render={ props => isAuthed ? <Home {...props} /> : <Redirect to="/public" /> } />
<Route exact path="/login">
<Login />
</Route>
<Route path="/public">
<Public />
</Route>
</Switch>
</div>
);
}
}
这是我处理的 AuthWrapper
组件 Auth0
:
class AuthWrapper extends Component {
constructor(props) {
super(props);
this.onAuthenticated = this.onAuthenticated.bind(this);
this.lock = new Auth0Lock('my_auth0_client_id', 'my_domain.auth0.com', {
auth: {
audience: 'https://my_backend_api_url',
redirectUrl: 'http://localhost:3000/',
responseType: 'token id_token',
sso: false
}
});
this.onAuthenticated();
}
onAuthenticated() {
debugger; // After successful login, I hit this debugger
this.lock.on('authenticated', (authResult) => {
debugger; // But I never hit this debugger
let expiresAt = JSON.stringify((authResult.expiresIn * 1000) + new Date().getTime());
sessionStorage.setItem('access_token', authResult.accessToken);
sessionStorage.setItem('id_token', authResult.idToken);
sessionStorage.setItem('expires_at', expiresAt);
});
}
render() {
return(
<AuthContext.Provider value={{ lock: this.lock }}>
{this.props.children}
</AuthContext.Provider>
);
}
}
这是 index.js
以备不时之需:
import App from './components/App';
import AuthWrapper from './components/auth/AuthWrapper';
// Store
import appStore from './store/app-store';
const store = appStore();
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<AuthWrapper>
<App />
</AuthWrapper>
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
动态路由需要在 <Switch>
范围之外定义。这是一个例子,假设你的函数 isAuthenticated() 是一个状态(Redux 或 wathever)
import React from "react";
import ReactDOM from "react-dom";
import { createBrowserHistory } from "history";
import { Router, Route, Switch, Redirect } from "react-router-dom";
// core components
import Admin from "layouts/Admin.js";
import SignIn from "layouts/SignIn";
const hist = createBrowserHistory();
const loggedRoutes = () => (
<Switch>
<Route path="/" component={SignIn} />
<Route path="/admin" component={Admin} />
<Redirect from="/admin" to="/admin/aboutUs/whatWeDo" />
</Switch>
);
const routes = () => (
<Switch>
<Route path="/" component={SignIn} />
<Route path="/login" component={Admin} />
<Redirect from="/" to="/login" />
</Switch>
);
ReactDOM.render(
<Router history={hist}>
{checkIfAuth? loggedRoutes():routes()}
</Router>,
document.getElementById("root")
);
在这个例子中,如果您没有登录,您将被重定向到/login。
以下是我为完成这项工作所做的更改。
- 我现在在
index.js
中初始化Auth0 Lock
使其成为全局 - 我将
onAuthenticated()
侦听器移到了LoginCallback.jsx
组件,这是用户在成功登录后被发送到的地方。我相信将onAuthenticated()
从App.jsx
移动到LoginCallback.jsx
产生了最大的影响,因为LoginCallback.jsx
中的侦听器在App.jsx
. 之前执行
- 身份验证成功后,我还使用
this.props.history.push('/');
将用户发送到Home
组件
有关完整代码,请参阅位于 https://github.com/imsam67/react-redux-react-router-auth0-lock
的存储库请记住,客户端根本没有任何保护。如果您担心路由到没有身份验证的组件,只需确保没有数据被暴露(假设他们没有令牌就无法获取任何数据)并且即使在您的路由器检查身份验证或显示错误后它们到达那里也会重定向。
请记住,客户端根本没有任何保护。如果您担心路由到未经身份验证的组件,只需确保没有数据被暴露,即使在您的路由器检查身份验证后它们到达那里,也可以重定向。我认为@Sam 是对的。路由可能不会像预期的那样响应异步调用改变它,或者可能有奇怪的行为。我从来没有以这种方式尝试过动态路由,但总是有条件地呈现内容组件。更好的方法可能是发送调用并在 then 块中重定向到路由器知道要处理的 url。只是不确定路由器是否能很好地处理这个问题。捕获加载时检查身份验证的组件,如果未授权则重定向回登录。抱歉,我在这里帮不了什么忙,但条件路由几乎看起来像是一种反模式,但我想如果我们知道路由器在更改后如何呈现其数据,或者它是否确实如此(路由本身),它可能会起作用。所以如果他们要为 url 添加书签并尝试 return 返回 404 对吗?也许像 401 未经授权的显示和重定向或 link 登录可能更好?