javascript - link - ¿Cómo restringir el acceso a rutas en react-router?
react router tutorial (6)
¿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 mis componentes
<Route>
, 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>
Actualización (16 de agosto de 2019)
En react-router v4 y usando React Hooks, esto se ve un poco diferente.
Comencemos con su
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 utilizando una biblioteca
Auth
para verificar si el usuario está autenticado actualmente.
Reemplace esto con su función de verificación de autenticación.
Si es así, establecemos el indicador
isAuthenticated
en
true
.
Hacemos esto cuando nuestra aplicación se carga por primera vez.
También vale la pena mencionar, es posible que desee agregar un signo de carga en su aplicación mientras se ejecuta la verificación de autenticación, por lo que no muestra la página de inicio de sesión cada vez que actualiza la página.
Luego pasamos la bandera a nuestras rutas.
Creamos dos tipos de rutas
AuthenticatedRoute
y
UnauthenticatedRoute
.
El
AuthenticatedRoute.js
tiene este aspecto.
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
está establecido en
true
.
Si es así, representará el componente deseado.
De lo contrario, redirigirá a la página de inicio de sesión.
El
UnauthenticatedRoute.js
por 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
isAuthenticated
se establece en
false
,
isAuthenticated
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 Mixins se considera dañino ( https://facebook.github.io/react/blog/2016/07/13/mixins-considered-harmful.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 su ejemplo, sería:
<Route handler={requireAuth(Todos)} name="todos"/>
Cubro este y algunos otros temas en un detallado tutorial paso a paso aquí: https://serverless-stack.com/chapters/create-a-route-that-redirects.html
Hay (¿ahora?) Un ejemplo de esto en los documentos de React Router 4 para
Redirect
import { Route, Redirect } from ''react-router''
<Route exact path="/" render={() => (
loggedIn ? (
<Redirect to="/dashboard"/>
) : (
<PublicHomePage/>
)
)}/>
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 son responsables de administrar el objeto
$auth
.
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 mezclar
Authenticator
mezclando 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
this.login
función de
this.login
cuando tenga todos los datos necesarios.
Lo más importante es proteger sus componentes mediante la mezcla de
NeedsAuthenticatedUser
mixin.
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
usa la API react-router (
willTransitionTo
y
transition.abort()
).
por lo general, un usuario conectado recibirá un token, y lo utiliza 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 si el token es nulo o autentique el token con el servidor para ver si el usuario es un inicio de sesión válido.
espero que esto ayude :)
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 le pase el accesorio
loggedIn
):
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} />
];
private-route.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/ .