javascript - proteger - static router react router dom
¿Cómo implementar rutas autenticadas en React Router 4? (11)
Intenté implementar rutas autenticadas pero descubrí que React Router 4 ahora evita que esto funcione:
<Route exact path="/" component={Index} />
<Route path="/auth" component={UnauthenticatedWrapper}>
<Route path="/auth/login" component={LoginBotBot} />
</Route>
<Route path="/domains" component={AuthenticatedWrapper}>
<Route exact path="/domains" component={DomainsIndex} />
</Route>
El error es:
Advertencia: no debe usar
<Route component>
y<Route children>
en la misma ruta;<Route children>
será ignorado
En ese caso, ¿cuál es la forma correcta de implementar esto?
Aparece en los documentos
react-router
(v4), sugiere algo como
<Router>
<div>
<AuthButton/>
<ul>
<li><Link to="/public">Public Page</Link></li>
<li><Link to="/protected">Protected Page</Link></li>
</ul>
<Route path="/public" component={Public}/>
<Route path="/login" component={Login}/>
<PrivateRoute path="/protected" component={Protected}/>
</div>
</Router>
¿Pero es posible lograr esto mientras se agrupan varias rutas?
ACTUALIZAR
Ok, después de un poco de investigación, se me ocurrió esto:
import React, {PropTypes} from "react"
import {Route} from "react-router-dom"
export default class AuthenticatedRoute extends React.Component {
render() {
if (!this.props.isLoggedIn) {
this.props.redirectToLogin()
return null
}
return <Route {...this.props} />
}
}
AuthenticatedRoute.propTypes = {
isLoggedIn: PropTypes.bool.isRequired,
component: PropTypes.element,
redirectToLogin: PropTypes.func.isRequired
}
Es correcto enviar una acción en
render()
se siente mal.
¿Realmente tampoco parece correcto con
componentDidMount
o algún otro gancho?
Aquí está la ruta simple limpia y protegida
const ProtectedRoute
= ({ isAllowed, ...props }) =>
isAllowed
? <Route {...props}/>
: <Redirect to="/authentificate"/>;
const _App = ({ lastTab, isTokenVerified })=>
<Switch>
<Route exact path="/authentificate" component={Login}/>
<ProtectedRoute
isAllowed={isTokenVerified}
exact
path="/secrets"
component={Secrets}/>
<ProtectedRoute
isAllowed={isTokenVerified}
exact
path="/polices"
component={Polices}/>
<ProtectedRoute
isAllowed={isTokenVerified}
exact
path="/grants" component={Grants}/>
<Redirect from="/" to={lastTab}/>
</Switch>
isTokenVerified
es una llamada al método para verificar el token de autorización, básicamente, devuelve boolean.
Basado en la respuesta de . Hice un enfoque diferente usando la sintaxis ES6 y rutas anidadas con componentes envueltos:
import React, { cloneElement, Children } from ''react''
import { Route, Redirect } from ''react-router-dom''
const PrivateRoute = ({ children, authed, ...rest }) =>
<Route
{...rest}
render={(props) => authed ?
<div>
{Children.map(children, child => cloneElement(child, { ...child.props }))}
</div>
:
<Redirect to={{ pathname: ''/'', state: { from: props.location } }} />}
/>
export default PrivateRoute
Y usándolo:
<BrowserRouter>
<div>
<PrivateRoute path=''/home'' authed={auth}>
<Navigation>
<Route component={Home} path="/home" />
</Navigation>
</PrivateRoute>
<Route exact path=''/'' component={PublicHomePage} />
</div>
</BrowserRouter>
Implementé usando
<Route path=''/dashboard'' render={() => (
this.state.user.isLoggedIn ?
(<Dashboard authenticate={this.authenticate} user={this.state.user} />) :
(<Redirect to="/login" />)
)} />
los accesorios de autenticación se pasarán a los componentes, por ejemplo, el registro mediante el cual se puede cambiar el estado del usuario. Rutas de aplicación completas
import React from ''react'';
import { Switch, Route } from ''react-router-dom'';
import { Redirect } from ''react-router'';
import Home from ''../pages/home'';
import Login from ''../pages/login'';
import Signup from ''../pages/signup'';
import Dashboard from ''../pages/dashboard'';
import { config } from ''../utils/Config'';
export default class AppRoutes extends React.Component {
constructor(props) {
super(props);
// initially assuming that user is logged out
let user = {
isLoggedIn: false
}
// if user is logged in, his details can be found from local storage
try {
let userJsonString = localStorage.getItem(config.localStorageKey);
if (userJsonString) {
user = JSON.parse(userJsonString);
}
} catch (exception) {
}
// updating the state
this.state = {
user: user
};
this.authenticate = this.authenticate.bind(this);
}
// this function is called on login/logout
authenticate(user) {
this.setState({
user: user
});
// updating user''s details
localStorage.setItem(config.localStorageKey, JSON.stringify(user));
}
render() {
return (
<Switch>
<Route exact path=''/'' component={Home} />
<Route exact path=''/login'' render={() => <Login authenticate={this.authenticate} />} />
<Route exact path=''/signup'' render={() => <Signup authenticate={this.authenticate} />} />
<Route path=''/dashboard'' render={() => (
this.state.user.isLoggedIn ?
(<Dashboard authenticate={this.authenticate} user={this.state.user} />) :
(<Redirect to="/login" />)
)} />
</Switch>
);
}
}
Consulta el proyecto completo aquí: https://github.com/varunon9/hello-react
Mi respuesta anterior no es escalable. Esto es lo que creo que es un buen enfoque.
Sus rutas
<Switch>
<Route
exact path="/"
component={matchStateToProps(InitialAppState, {
routeOpen: true // no auth is needed to access this route
})} />
<Route
exact path="/profile"
component={matchStateToProps(Profile, {
routeOpen: false // can set it false or just omit this key
})} />
<Route
exact path="/login"
component={matchStateToProps(Login, {
routeOpen: true
})} />
<Route
exact path="/forgot-password"
component={matchStateToProps(ForgotPassword, {
routeOpen: true
})} />
<Route
exact path="/dashboard"
component={matchStateToProps(DashBoard)} />
</Switch>
La idea es utilizar un contenedor en los accesorios del
component
que devolvería el componente original si no se requiere autenticación o si ya está autenticado, de lo contrario devolvería el componente predeterminado, por ejemplo, Iniciar sesión.
const matchStateToProps = function(Component, defaultProps) {
return (props) => {
let authRequired = true;
if (defaultProps && defaultProps.routeOpen) {
authRequired = false;
}
if (authRequired) {
// check if loginState key exists in localStorage (Your auth logic goes here)
if (window.localStorage.getItem(STORAGE_KEYS.LOGIN_STATE)) {
return <Component { ...defaultProps } />; // authenticated, good to go
} else {
return <InitialAppState { ...defaultProps } />; // not authenticated
}
}
return <Component { ...defaultProps } />; // no auth is required
};
};
Parece que su duda está en crear su propio componente y luego enviarlo en el método de renderizado.
Bueno, puede evitar ambos simplemente usando el método de representación del componente
<Route>
.
No es necesario crear un componente
<AuthenticatedRoute>
menos que realmente desee.
Puede ser tan simple como a continuación.
Tenga en cuenta la propagación
{...routeProps}
asegurándose de continuar enviando las propiedades del componente
<Route>
componente hijo (
<MyComponent>
en este caso).
<Route path=''/someprivatepath'' render={routeProps => {
if (!this.props.isLoggedIn) {
this.props.redirectToLogin()
return null
}
return <MyComponent {...routeProps} anotherProp={somevalue} />
} />
Consulte la documentación de procesamiento de React Router V4
Si desea crear un componente dedicado, entonces parece que está en el camino correcto.
Dado que React Router V4 es
un enrutamiento puramente declarativo
(lo dice directamente en la descripción), no creo que se salga con la suya al poner su código de redireccionamiento fuera del ciclo de vida normal de los componentes.
Al
observar
el
código de React Router
, realizan el redireccionamiento en
componentWillMount
o
componentDidMount
dependiendo de si se trata o no de una representación del lado del servidor.
Aquí está el código a continuación, que es bastante simple y podría ayudarlo a sentirse más cómodo con dónde colocar su lógica de redireccionamiento.
import React, { PropTypes } from ''react''
/**
* The public API for updating the location programatically
* with a component.
*/
class Redirect extends React.Component {
static propTypes = {
push: PropTypes.bool,
from: PropTypes.string,
to: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object
])
}
static defaultProps = {
push: false
}
static contextTypes = {
router: PropTypes.shape({
history: PropTypes.shape({
push: PropTypes.func.isRequired,
replace: PropTypes.func.isRequired
}).isRequired,
staticContext: PropTypes.object
}).isRequired
}
isStatic() {
return this.context.router && this.context.router.staticContext
}
componentWillMount() {
if (this.isStatic())
this.perform()
}
componentDidMount() {
if (!this.isStatic())
this.perform()
}
perform() {
const { history } = this.context.router
const { push, to } = this.props
if (push) {
history.push(to)
} else {
history.replace(to)
}
}
render() {
return null
}
}
export default Redirect
Sé que ha pasado un tiempo, pero he estado trabajando en un paquete npm para rutas privadas y públicas.
Aquí se explica cómo hacer una ruta privada:
<PrivateRoute exact path="/private" authed={true} redirectTo="/login" component={Title} text="This is a private route"/>
Y también puede hacer rutas públicas a las que solo pueden acceder usuarios no autorizados
<PublicRoute exact path="/public" authed={false} redirectTo="/admin" component={Title} text="This route is for unauthed users"/>
¡Espero que ayude!
Tnx Tyler McGinnis para la solución. Mi idea es de Tyler McGinnis.
const DecisionRoute = ({ trueComponent, falseComponent, decisionFunc, ...rest }) => {
return (
<Route
{...rest}
render={
decisionFunc()
? trueComponent
: falseComponent
}
/>
)
}
Puedes implementar eso así
<DecisionRoute path="/signin" exact={true}
trueComponent={redirectStart}
falseComponent={SignInPage}
decisionFunc={isAuth}
/>
decisionFunc solo una función que devuelve verdadero o falso
const redirectStart = props => <Redirect to="/orders" />
Vas a querer usar el componente
Redirect
.
Hay algunos enfoques diferentes para este problema.
Aquí hay uno que me gusta, tengo un componente PrivateRoute que toma un accesorio
authed
y luego lo renderiza en función de esos accesorios.
function PrivateRoute ({component: Component, authed, ...rest}) {
return (
<Route
{...rest}
render={(props) => authed === true
? <Component {...props} />
: <Redirect to={{pathname: ''/login'', state: {from: props.location}}} />}
/>
)
}
Ahora tus
Route
pueden verse así
<Route path=''/'' exact component={Home} />
<Route path=''/login'' component={Login} />
<Route path=''/register'' component={Register} />
<PrivateRoute authed={this.state.authed} path=''/dashboard'' component={Dashboard} />
Si todavía está confundido, escribí esta publicación que puede ayudar: Rutas protegidas y autenticación con React Router v4
instalar react-router-dom
luego cree dos componentes, uno para usuarios válidos y otro para usuarios no válidos.
prueba esto en app.js
import React from ''react'';
import {
BrowserRouter as Router,
Route,
Link,
Switch,
Redirect
} from ''react-router-dom'';
import ValidUser from "./pages/validUser/validUser";
import InValidUser from "./pages/invalidUser/invalidUser";
const loggedin = false;
class App extends React.Component {
render() {
return (
<Router>
<div>
<Route exact path="/" render={() =>(
loggedin ? ( <Route component={ValidUser} />)
: (<Route component={InValidUser} />)
)} />
</div>
</Router>
)
}
}
export default App;
Solo agrego mi solución al problema.
Estoy usando tokens jwt para autenticación, por lo que si el usuario tiene ese token, los redirigiré a la página de inicio o los redireccionaré a la página de inicio de sesión predeterminada (que es esta ruta ''/''). Entonces, una vez que el usuario inició sesión e intente acceder a la URL de la página de inicio de sesión (en mi caso, ''/''). Los redirigiré a casa de forma predeterminada (''/ home'').
Y mis componentes tienen HOC llamado requireAuth para verificar si el token de usuario es válido. Si no, entonces llame a la acción de cierre de sesión que elimina el token localhistory.
import React, { Component, Fragment } from ''react'';
import ReactDOM from ''react-dom'';
import { BrowserRouter, Route, Switch, Redirect } from ''react-router-dom'';
//and also import appropriate components
//middleware
class checkStatus extends React.Component {
render() {
if(localStorage.getItem(''token'')){
return (
<Fragment>
<App>
<Route path="/home" exact component={Overview} />
<Route path="/home/add" exact component={Add} />
<Route path="/signout" component={Signout} />
<Route path="/details" component={details} />
<Route exact path="/" render={() => <Redirect to="/home" />} />
</App>
</Fragment>
)
}else{
return (
<Fragment>
<Route path="/" exact component={Signin} />
<Redirect to="/" />
</Fragment>
)
}
} }
ReactDOM.render( <Provider store={store}>
<BrowserRouter>
<Switch >
<Route path="/" exact component={checkStatus} />
<Route path="/:someParam" component={checkStatus}/>
</Switch >
</BrowserRouter> </Provider>, document.querySelector(''#root'')
);
const Root = ({ session }) => {
const isLoggedIn = session && session.getCurrentUser
return (
<Router>
{!isLoggedIn ? (
<Switch>
<Route path="/signin" component={<Signin />} />
<Redirect to="/signin" />
</Switch>
) : (
<Switch>
<Route path="/" exact component={Home} />
<Route path="/about" component={About} />
<Route path="/something-else" component={SomethingElse} />
<Redirect to="/" />
</Switch>
)}
</Router>
)
}