我试图实现经过身份验证的路由,但发现React Router 4现在阻止了它的运行:
<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>
错误是:
警告:您不应在同一路线中使用
<Route component>
和<Route children>
;<Route children>
将被忽略
在这种情况下,有什么正确的方法来实现这个?
它出现在react-router
(v4)文档中,它表示类似
<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>
但是在将一堆路线组合在一起时是否可以实现这一目标?
更新
好的,经过一番研究,我想出了这个:
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
}
在render()
发送一个动作是正确的,这感觉不对。用componentDidMount
或其他一些钩子看起来不是很正确吗?
答案 0 :(得分:168)
您将要使用Redirect
组件。这个问题有几种不同的方法。这是我喜欢的一个,有一个PrivateRoute组件,它接收authed
道具,然后根据道具进行渲染。
function PrivateRoute ({component: Component, authed, ...rest}) {
return (
<Route
{...rest}
render={(props) => authed === true
? <Component {...props} />
: <Redirect to={{pathname: '/login', state: {from: props.location}}} />}
/>
)
}
现在你的Route
看起来像这样
<Route path='/' exact component={Home} />
<Route path='/login' component={Login} />
<Route path='/register' component={Register} />
<PrivateRoute authed={this.state.authed} path='/dashboard' component={Dashboard} />
如果您仍然感到困惑,我写了这篇可能会有所帮助的帖子 - Protected routes and authentication with React Router v4
答案 1 :(得分:14)
Tnx Tyler McGinnis寻求解决方案。 我从Tyler McGinnis的想法中提出我的想法。
const DecisionRoute = ({ trueComponent, falseComponent, decisionFunc, ...rest }) => {
return (
<Route
{...rest}
render={
decisionFunc()
? trueComponent
: falseComponent
}
/>
)
}
您可以像这样实现
<DecisionRoute path="/signin" exact={true}
trueComponent={redirectStart}
falseComponent={SignInPage}
decisionFunc={isAuth}
/>
decisionFunc只是一个返回true或false的函数
const redirectStart = props => <Redirect to="/orders" />
答案 2 :(得分:4)
安装react-router-dom
然后为有效用户创建两个组件,为无效用户创建其他组件。
在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;
答案 3 :(得分:3)
只需添加我的解决方案即可。
我正在使用jwt令牌进行身份验证,因此,如果用户拥有该令牌,那么我会将其重定向到主页,否则默认情况下会将它们重定向到登录页面(此路由为'/')。因此,一旦用户登录并尝试访问登录页面网址(在我的情况下为'/')。默认情况下,我会将其重定向到home('/ home')。
我的组件确实有一个名为requireAuth的HOC,以检查用户令牌是否有效。如果无效,则调用注销操作以删除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')
);
答案 4 :(得分:2)
我知道已经有一段时间了,但我一直在为私人和公共路线npm package工作。
以下是如何制作私人路线:
<PrivateRoute exact path="/private" authed={true} redirectTo="/login" component={Title} text="This is a private route"/>
您还可以制作只有未经过用户才能访问的公共路线
<PublicRoute exact path="/public" authed={false} redirectTo="/admin" component={Title} text="This route is for unauthed users"/>
我希望它有所帮助!
答案 5 :(得分:2)
基于@Tyler McGinnis的答案。我使用了 ES6语法和嵌套路由并包装了组件的方法不同:
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
并使用它:
<BrowserRouter>
<div>
<PrivateRoute path='/home' authed={auth}>
<Navigation>
<Route component={Home} path="/home" />
</Navigation>
</PrivateRoute>
<Route exact path='/' component={PublicHomePage} />
</div>
</BrowserRouter>
答案 6 :(得分:1)
您似乎犹豫是在创建自己的组件,然后在render方法中调度?那么你可以通过使用render
组件的<Route>
方法来避免这两种情况。除非您真的想要,否则无需创建<AuthenticatedRoute>
组件。它可以像下面一样简单。请注意{...routeProps}
点差,确保您继续将<Route>
组件的属性发送到子组件(在这种情况下为<MyComponent>
)。
<Route path='/someprivatepath' render={routeProps => {
if (!this.props.isLoggedIn) {
this.props.redirectToLogin()
return null
}
return <MyComponent {...routeProps} anotherProp={somevalue} />
} />
请参阅React Router V4 render documentation
如果你确实想要创建一个专用组件,那么看起来你是在正确的轨道上。由于React Router V4是纯粹的声明性路由(它在描述中如此说明),我认为你不会放弃将重定向代码放在正常的组件生命周期之外。查看code for React Router itself,他们会在componentWillMount
或componentDidMount
中执行重定向,具体取决于它是否是服务器端呈现。以下是下面的代码,它非常简单,可以帮助您更轻松地放置重定向逻辑。
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
答案 7 :(得分:1)
我使用 -
实现<Route path='/dashboard' render={() => (
this.state.user.isLoggedIn ?
(<Dashboard authenticate={this.authenticate} user={this.state.user} />) :
(<Redirect to="/login" />)
)} />
验证道具将被传递给组件,例如使用哪个用户状态可以更改的注册。完整的AppRoutes -
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>
);
}
}
点击此处查看完整项目:https://github.com/varunon9/hello-react
答案 8 :(得分:1)
我的上一个答案不可扩展。我认为这是个好方法-
您的路线-
<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>
想法是在component
道具中使用包装器,如果不需要身份验证或已经通过身份验证,它将返回原始组件,否则将返回默认组件,例如登录。
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
};
};
答案 9 :(得分:0)
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>
)
}
答案 10 :(得分:0)
这是简单的干净保护路线
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
是一种用于检查授权令牌的方法,基本上它返回布尔值。
答案 11 :(得分:0)
我也在寻找答案。这里的所有答案都很好,但是如果用户在打开应用程序后重新启动应用程序,它们都无法给出答案。 (我是说要一起使用Cookie)。
无需创建甚至不同的privateRoute组件。下面是我的代码
import React, { Component } from 'react';
import { Route, Switch, BrowserRouter, Redirect } from 'react-router-dom';
import { Provider } from 'react-redux';
import store from './stores';
import requireAuth from './components/authentication/authComponent'
import SearchComponent from './components/search/searchComponent'
import LoginComponent from './components/login/loginComponent'
import ExampleContainer from './containers/ExampleContainer'
class App extends Component {
state = {
auth: true
}
componentDidMount() {
if ( ! Cookies.get('auth')) {
this.setState({auth:false });
}
}
render() {
return (
<Provider store={store}>
<BrowserRouter>
<Switch>
<Route exact path="/searchComponent" component={requireAuth(SearchComponent)} />
<Route exact path="/login" component={LoginComponent} />
<Route exact path="/" component={requireAuth(ExampleContainer)} />
{!this.state.auth && <Redirect push to="/login"/> }
</Switch>
</BrowserRouter>
</Provider>);
}
}
}
export default App;
这是authComponent
import React from 'react';
import { withRouter } from 'react-router';
import * as Cookie from "js-cookie";
export default function requireAuth(Component) {
class AuthenticatedComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
auth: Cookie.get('auth')
}
}
componentDidMount() {
this.checkAuth();
}
checkAuth() {
const location = this.props.location;
const redirect = location.pathname + location.search;
if ( ! Cookie.get('auth')) {
this.props.history.push(`/login?redirect=${redirect}`);
}
}
render() {
return Cookie.get('auth')
? <Component { ...this.props } />
: null;
}
}
return withRouter(AuthenticatedComponent)
}
在我写过博客之后,您也可以在那里获得更深入的解释。
答案 12 :(得分:0)
这里介绍了如何使用React和Typescript解决它。希望对您有帮助!
import * as React from 'react';
import { Route, RouteComponentProps, RouteProps, Redirect } from 'react-router';
const PrivateRoute: React.SFC<RouteProps> = ({ component: Component, ...rest }) => {
if (!Component) {
return null;
}
const isLoggedIn = true; // Add your provider here
return (
<Route
{...rest}
render={(props: RouteComponentProps<{}>) => isLoggedIn ? (<Component {...props} />) : (<Redirect to={{ pathname: '/', state: { from: props.location } }} />)}
/>
);
};
export default PrivateRoute;
<PrivateRoute component={SignIn} path="/signin" />
答案 13 :(得分:0)
下面将详细介绍最终对我的组织最有效的解决方案,它仅对sysadmin路由进行渲染检查,如果不允许用户进入页面,则将用户重定向到应用程序的其他主路径。
SysAdminRoute.tsx
import React from 'react';
import { Route, Redirect, RouteProps } from 'react-router-dom';
import AuthService from '../services/AuthService';
import { appSectionPageUrls } from './appSectionPageUrls';
interface IProps extends RouteProps {}
export const SysAdminRoute = (props: IProps) => {
var authService = new AuthService();
if (!authService.getIsSysAdmin()) { //example
authService.logout();
return (<Redirect to={{
pathname: appSectionPageUrls.site //front-facing
}} />);
}
return (<Route {...props} />);
}
我们实施的主要途径有3条,面向公众的/ site,登录的客户端/ app和/ sysadmin的sys admin工具。您将基于您的“真实性”被重定向,这是/ sysadmin的页面。
SysAdminNav.tsx
<Switch>
<SysAdminRoute exact path={sysadminUrls.someSysAdminUrl} render={() => <SomeSysAdminUrl/> } />
//etc
</Switch>
答案 14 :(得分:0)
可接受的答案很好,但是当我们需要我们的组件来反映URL的更改时并不能解决问题。
说,您组件的代码如下:
for i in dp.index:
fill = []
for xa in x:
if abs(dp.xlist[i]-xa)<0.001:
tmp = dp.ylist[i]
else:
tmp = 0
fill.append(tmp)
然后您更改URL:
export const Customer = (props) => {
const history = useHistory();
...
}
const handleGoToPrev = () => {
history.push(`/app/customer/${prevId}`);
}
import React from 'react';
import { Redirect, Route } from 'react-router-dom';
import store from '../store/store';
export const PrivateRoute = ({ component: Component, ...rest }) => {
let isLoggedIn = !!store.getState().data.user;
return (
<Route {...rest} render={props => isLoggedIn
? (
<Component key={props.match.params.id || 'empty'} {...props} />
) : (
<Redirect to={{ pathname: '/login', state: { from: props.location } }} />
)
} />
)
}
答案 15 :(得分:0)
这只是初学者的基本方法,不适合专业的 redux 开发人员
private String ClassApp=System.getenv("ClassApp");
private String Country=System.getenv("Country");