¿Alguien sabe cómo restringir el acceso a rutas particulares en react-router? Quiero comprobar si el usuario está conectado antes de permitir el acceso a una ruta en particular. Pensé que sería simple, pero los documentos no tienen claro cómo hacerlo.
¿Es esto algo que debería configurar donde defino mis <Route>componentes, o debería manejarlo dentro de mis controladores de componentes?
<Route handler={App} path="/">
  <NotFoundRoute handler={NotFound} name="not-found"/>
  <DefaultRoute handler={Login} name="login"/>
  <Route handler={Todos} name="todos"/> {/* I want this to be restricted */}
</Route>
                    
                        javascript
                                reactjs
                                react-router
                                
                    
                    
                        Tanner Semerad
fuente
                
                fuente

Respuestas:
Actualización (16 de agosto de 2019)
En react-router v4 y usando React Hooks, esto se ve un poco diferente. Empecemos con tu
App.js.export default function App() { const [isAuthenticated, userHasAuthenticated] = useState(false); useEffect(() => { onLoad(); }, []); async function onLoad() { try { await Auth.currentSession(); userHasAuthenticated(true); } catch (e) { alert(e); } } return ( <div className="App container"> <h1>Welcome to my app</h1> <Switch> <UnauthenticatedRoute path="/login" component={Login} appProps={{ isAuthenticated }} /> <AuthenticatedRoute path="/todos" component={Todos} appProps={{ isAuthenticated }} /> <Route component={NotFound} /> </Switch> </div> ); }Estamos usando una
Authbiblioteca para verificar si el usuario está autenticado actualmente. Reemplace esto con su función de verificación de autenticación. Si es así, establecemos laisAuthenticatedbandera entrue. Hacemos esto cuando nuestra aplicación se carga por primera vez. También vale la pena mencionar que es posible que desee agregar un signo de carga en su aplicación mientras se ejecuta la verificación de autenticación, para que no muestre la página de inicio de sesión cada vez que actualice la página.Luego pasamos la bandera a nuestras rutas. Creamos dos tipos de rutas
AuthenticatedRouteyUnauthenticatedRoute.Se
AuthenticatedRoute.jsve así.export default function AuthenticatedRoute({ component: C, appProps, ...rest }) { return ( <Route {...rest} render={props => appProps.isAuthenticated ? <C {...props} {...appProps} /> : <Redirect to={`/login?redirect=${props.location.pathname}${props.location.search}`} />} /> ); }Comprueba si
isAuthenticatedestá configurado entrue. Si es así, renderizará el componente deseado. De lo contrario, redirigirá a la página de inicio de sesión.El
UnauthenticatedRoute.jspor otro lado se ve así.export default ({ component: C, appProps, ...rest }) => <Route {...rest} render={props => !appProps.isAuthenticated ? <C {...props} {...appProps} /> : <Redirect to="/" />} />;En este caso, si
isAuthenticatedestá configurado enfalse, renderizará el componente deseado. Y si está configurado como verdadero, lo enviará a la página de inicio.Puede encontrar versiones detalladas de esto en nuestra guía: https://serverless-stack.com/chapters/create-a-route-that-redirects.html .
Versión antigua
La respuesta aceptada es correcta, pero el equipo de React considera que los Mixins son dañinos ( https://facebook.github.io/react/blog/2016/07/13/mixins-considered-harmful.html ).
Si alguien se encuentra con esta pregunta y está buscando la forma recomendada de hacerlo, le sugiero que use Componentes de orden superior en lugar de Mixins.
Aquí hay un ejemplo de un HOC que verificará si el usuario inició sesión antes de continuar. Y si el usuario no ha iniciado sesión, lo redireccionará a la página de inicio de sesión. Este componente toma un accesorio llamado
isLoggedIn, que es básicamente una bandera que su aplicación puede almacenar para indicar si el usuario está conectado.import React from 'react'; import { withRouter } from 'react-router'; export default function requireAuth(Component) { class AuthenticatedComponent extends React.Component { componentWillMount() { this.checkAuth(); } checkAuth() { if ( ! this.props.isLoggedIn) { const location = this.props.location; const redirect = location.pathname + location.search; this.props.router.push(`/login?redirect=${redirect}`); } } render() { return this.props.isLoggedIn ? <Component { ...this.props } /> : null; } } return withRouter(AuthenticatedComponent); }Y para usar este HOC, simplemente envuélvalo en sus rutas. En el caso de su ejemplo, sería:
<Route handler={requireAuth(Todos)} name="todos"/>Cubro este y algunos otros temas en un tutorial detallado paso a paso aquí: https://serverless-stack.com/chapters/create-a-hoc-that-checks-auth.html
fuente
this.props.isLoggedIncontruey entrada de derivación?<Route handler={}/>está en desuso en v1.0, debe usar<Route component={} />.componentWillMountpronto quedarán obsoletos. Léalo en la publicación del blog en reactjs.org . En su lugar, iría con la respuesta que @jacob proporcionó.Hay (¿ahora?) Un ejemplo de esto en los documentos de React Router 4 para
Redirectimport { Route, Redirect } from 'react-router' <Route exact path="/" render={() => ( loggedIn ? ( <Redirect to="/dashboard"/> ) : ( <PublicHomePage/> ) )}/>fuente
Si desea utilizar la autenticación en toda su aplicación, debe almacenar algunos datos en toda la aplicación (por ejemplo, token). Puede configurar dos mixins de React que sean responsables de administrar el
$authobjeto. Este objeto no debería estar disponible fuera de esos dos mixins. Aquí hay un ejemplo de eso:define('userManagement', function() { 'use strict'; var $auth = { isLoggedIn: function () { // return something, e.g. using server-stored data } }; return { Authenticator: { login: function(username, password) { // modify $auth object, or call server, or both } }, NeedsAuthenticatedUser: { statics: { willTransitionTo: function (transition) { if (!$auth.isLoggedIn()) { transition.abort(); } } } } }; });Luego, puede simplemente
Authenticatormezclar la mezcla con sus componentes de inicio de sesión (pantalla de inicio de sesión, ventana emergente de inicio de sesión, etc.) y llamar a lathis.loginfunción cuando tenga todos los datos necesarios.Lo más importante es proteger sus componentes mezclándolos con
NeedsAuthenticatedUsermixin. Cada componente que necesita un usuario autenticado tendrá que verse así:var um = require('userManagement'); var ProtectedComponent = React.createClass({ mixins: [um.NeedsAuthenticatedUser] // ... }Tenga en cuenta que
NeedsAuthenticatedUserutiliza la API react-router (willTransitionToytransition.abort()).fuente
react-routerfomenta un enfoque declarativo para su enrutador, debe hacer que su enrutador sea lo más tonto posible y evitar poner su lógica de enrutamiento en sus componentes.Así es como puede hacerlo (suponiendo que le pase el
loggedInaccesorio):const DumbRouter = ({ loggedIn }) => ( <Router history={history}> <Switch> {[ !loggedIn && LoggedOutRoutes, loggedIn && LoggedInRouter, <Route component={404Route} /> ]} </Switch> </Router> ); const LoggedInRoutes = [ <Route path="/" component={Profile} /> ]; const LoggedOutRoutes = [ <Route path="/" component={Login} /> ];fuente
Puede usar HOC y auth es una variable que puede cambiar el valor verdadero o falso significa (autorización)
<Route path="/login" component={SignIn} /> <Route path="/posts" render = {() => (auth ? (<Post />) : (<Redirect to="/login" />))}/>fuente
ruta-privada.tsx
import {Redirect, Route, RouteProps} from 'react-router'; import * as React from 'react'; interface PrivateRouteProps extends RouteProps { /** * '/login' for example. */ redirectTo: string; /** * If true, won't redirect. * We are using a function instead of a bool, a bool does not seem to be updated * after having successfully authenticated. */ isLogged: () => boolean; } export function PrivateRoute(props: PrivateRouteProps) { // `component: Component` is not typing, it assign the value to a new variable. let { isLogged, redirectTo, component: Component, ...rest }: any = props; // error: JSX type element Component does not have call signature or ... AVOIDED BY ADDING ANY, still work, // and did not find a proper way to fix it. return <Route {...rest} render={(props) => ( isLogged() ? <Component {...props}/> : <Redirect to={{ pathname: redirectTo, state: { from: props.location } }} /> )} />; }Uso:
<PrivateRoute exact={true} path="/admin/" redirectTo={'/admin/login'} isLogged={this.loginService.isLogged} component={AdminDashboardPage}/> <Route path="/admin/login/" component={AdminLoginPage}/>Basado en https://tylermcginnis.com/react-router-protected-routes-authentication/ .
fuente
Por lo general, a un usuario que haya iniciado sesión se le otorgará un token y utilizará este token para cualquier comunicación con el servidor. Lo que solemos hacer es definir una página raíz y las cosas se construyen sobre esa página. esta página raíz realiza la localización, autenticación y otras configuraciones por usted.
aquí hay un ejemplo
Routes = ( <Route path="/" handler={Root}> <Route name="login" handler={Login} /> <Route name="forget" handler={ForgetPassword} /> <Route handler={Main} > <Route name="overview" handler={Overview} /> <Route name="profile" handler={Profile} /> <DefaultRoute handler={Overview} /> </Route> <DefaultRoute handler={Login} /> <NotFoundRoute handler={NotFound} /> </Route> );en su página raíz, verifique que el token sea nulo o autentique el token con el servidor para ver si el usuario tiene un inicio de sesión válido.
espero que esto ayude :)
fuente