curtidor semerad
¿Alguien sabe cómo restringir el acceso a rutas particulares en react-router? Quiero verificar si el usuario ha iniciado sesión 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 mi <Route>
componentes, o debería manejarlo dentro de mis controladores de componentes?
<Route handler={App} path="https://stackoverflow.com/">
<NotFoundRoute handler={NotFound} name="not-found"/>
<DefaultRoute handler={Login} name="login"/>
<Route handler={Todos} name="todos"/> {/* I want this to be restricted */}
</Route>
jayair
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 un Auth
biblioteca para comprobar si el usuario está actualmente autenticado. Reemplace esto con su función de verificación de autenticación. Si es así, establecemos el isAuthenticated
bandera a true
. 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 AuthenticatedRoute
y UnauthenticatedRoute
.
los AuthenticatedRoute.js
Se ve como esto.
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 isAuthenticated
se establece en true
. Si es así, renderizará el componente deseado. De lo contrario, se redirigirá a la página de inicio de sesión.
los UnauthenticatedRoute.js
por otro lado se parece a esto.
export default ({ component: C, appProps, ...rest }) =>
<Route
{...rest}
render={props =>
!appProps.isAuthenticated
? <C {...props} {...appProps} />
: <Redirect to="https://stackoverflow.com/" />}
/>;
En este caso, si el isAuthenticated
se establece en false
, renderizará el componente deseado. Y si se establece en 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 los Mixins se consideran dañinos (https://facebook.github.io/react/blog/2016/07/13/mixins-considered-dañino.html) por el equipo de React.
Si alguien se encuentra con esta pregunta y está buscando la forma recomendada de hacerlo, sugeriría usar componentes de orden superior en lugar de Mixins.
Aquí hay un ejemplo de un HOC que verificará si el usuario ha iniciado sesión antes de continuar. Y si el usuario no ha iniciado sesión, lo redirigirá a la página de inicio de sesión. Este componente toma un accesorio llamado isLoggedIn
que es básicamente un indicador que su aplicación puede almacenar para indicar si el usuario ha iniciado sesión.
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 alrededor de sus rutas. En el caso de tu 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
-
Si mi código original usaba
, ¿cómo haría que funcionara con este ejemplo? – Salvado
12/07/2017 a las 18:45
-
Tengo un código muy similar, pero mi pregunta es, ¿es lo suficientemente seguro? Quiero decir que puede ser que un atacante pueda cambiar el código minificado de JS de tal manera que reemplace
this.props.isLoggedIn
contrue
y omitir el inicio de sesión?– karim elhelawy
25 de octubre de 2017 a las 7:29
-
@karimelhelawy Eso es cierto y, por eso, debe aplicar la autenticación en la API de su servidor.
– cbr
27 oct 2017 a las 15:07
-
<Route handler={}/>
está en desuso en v1.0, debe usar<Route component={} />
.– DrDirk
10/02/2018 a las 14:00
-
componentWillMount
pronto van a ser obsoletos. Léalo en la publicación del blog en reactjs.org. En cambio, iría con la respuesta proporcionada por @jacob.– Tomás
29 de marzo de 2018 a las 14:59
Hay (¿ahora?) un ejemplo de esto en los documentos de React Router 4 para Redirect
import { Route, Redirect } from 'react-router'
<Route exact path="https://stackoverflow.com/" render={() => (
loggedIn ? (
<Redirect to="/dashboard"/>
) : (
<PublicHomePage/>
)
)}/>
-
¿Cómo puedo usar “loggedIn” como función o variable? puedes explicarlo un poco
– Kunvar Singh
24 de junio de 2018 a las 11:56
-
@KunvarSingh probablemente debería ser una función porque el valor cambia.
–Jakob Jingleheimer
24 de junio de 2018 a las 12:09
react-router
fomenta 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 lo pase el loggedIn
apuntalar):
const DumbRouter = ({ loggedIn }) => (
<Router history={history}>
<Switch>
{[
!loggedIn && LoggedOutRoutes,
loggedIn && LoggedInRouter,
<Route component={404Route} />
]}
</Switch>
</Router>
);
const LoggedInRoutes = [
<Route path="https://stackoverflow.com/" component={Profile} />
];
const LoggedOutRoutes = [
<Route path="https://stackoverflow.com/" component={Login} />
];
-
Esto es muy simple, lo cual es bueno. La cuestión es que, por lo general, desea reconocer las mismas rutas si está desconectado o conectado, por lo que puede redirigir correctamente para iniciar sesión si el usuario cerró la sesión. Por lo general, desea que las rutas sean las mismas, pero que se comporten de manera diferente según el estado de inicio de sesión. Además, con su solución está agregando duplicación, al crear la misma ruta en 2 ubicaciones diferentes, lo que es más difícil de mantener.
–Rafael Porras Lucena
1 de abril de 2020 a las 13:21
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 React que son responsables de administrar $auth
objeto. 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();
}
}
}
}
};
});
Entonces puedes simplemente mezclar Authenticator
mezclar con sus componentes de inicio de sesión (pantalla de inicio de sesión, ventana emergente de inicio de sesión, etc.) y llamar this.login
función cuando tenga todos los datos necesarios.
Lo más importante es proteger sus componentes mezclándolos NeedsAuthenticatedUser
mezclando 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 NeedsAuthenticatedUser
utiliza la API de enrutador de reacción (willTransitionTo
y transition.abort()
).
Puede usar HOC y auth es una variable que puede cambiar el valor verdadero o falso (autorización)
<Route path="/login" component={SignIn} />
<Route path="/posts" render = {() => (auth ? (<Post />) : (<Redirect to="/login" />))}/>
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}/>
Residencia en https://tylermcginnis.com/react-router-protected-routes-authentication/.
Puede evitar representar el componente antes de confirmar la autenticación, como se muestra a continuación:
import { useState, useEffect, useRef } from 'react';
import { useHistory } from 'react-router-dom';
const Route = () => {
const [loading, sertLoading] = useState(true);
const history = useHistory();
const ref = useRef<Function>({});
// must use ref!
ref.current.routeGuard = () => {
const authenticationHandler = (): boolean => {
// do authentication here
}
sertLoading(true);
const go = authenticationHandler();
if (go === false) {
history.goBack();
}
sertLoading(false);
}
useEffect(() => {
ref.current.routeGuard();
history.listen(() => {
ref.current.routeGuard();
});
}, []);
return (
<>
{!loading && <YourRouteComponent />}
</>
)
};
O simplemente, yarn add react-routers
qué componente tiene accesorios beforeEach
, beforeRoute
como Vue Route.
Si no han iniciado sesión, redirija al controlador de inicio de sesión. También tenga en cuenta que el cliente tiene acceso a todo el JS que carga, así que no almacene información confidencial en él.
– Coronel Treinta y Dos
27 de junio de 2015 a las 3:17
@Tanner Semerad, ¿tiene algún repositorio de github sobre cómo logró esto brevemente?
– jit
28 de enero de 2016 a las 5:11
@jit No, lo siento. La respuesta de miciek a continuación era lo que necesitaba, pero tenga en cuenta que esto fue anterior a react-router 1.0. Sé que varias cosas han cambiado desde que se lanzó 1.0, pero en su mayoría es similar.
– Tanner Semerad
28 de enero de 2016 a las 16:06
La respuesta de @jayair es lo que estoy usando ahora, y funciona muy bien
– Tanner Semerad
31 de agosto de 2017 a las 23:12