React-Router 实现 Auth 权限拦截

使用 React-Router 实现权限拦截分离。

假设应用有3个组件,一个显示登录状态的组件,一个公开信息组件,一个私有信息丝丝组件,其中私有信息需要在用户登录之后才能访问。根据此逻辑应用需要一个登录状态组件,一个公开信息组件,一个私有信息组件,分别把它们定义成组件:AuthPageLoginPublicPrivate

Login

1
2
3
4
5
6
7
8
9
10
11
const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
this.isAuthenticated = true
setTimeout(cb, 1000) // 模拟异步。
},
signout(cb) {
this.isAuthenticated = false
setTimeout(cb, 1000)
}
}

使用 fakeAuth 对象来管理用户的登录状态,以及 登录登出 操作。

当用户未登录的时候去访问私有信息,应用会让用户先登录,登录成功之后,自动跳转回私有信息页面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
redirectToReferrer: false
}
this.login = ()=> {
fakeAuth.authenticate(() => {
this.setState({redirectToReferrer: true})
})
}
}
render() {
// 上一个页面
const {from} = this.props.location.state || {from: {pathname: '/'}}
const {redirectToReferrer} = this.state
if (redirectToReferrer) { // 登录成功后->重定向回上一个页面
return (
<Redirect to={from}/>
)
}
return (
<div>
<p>若想访问 {from.pathname} ,你需要先登录</p>
<button onClick={this.login}>登录</button>
</div>
)
}
}

AuthPage

1
2
3
4
5
6
7
8
9
const AuthPage = withRouter(({history}) => {
return fakeAuth.isAuthenticated ?
(<p>Welcome!
<button onClick={()=>{
fakeAuth.signout(()=>history.push('/'))
}}>Signout</button>
</p>) :
(<p>You're not Logined!</p>)
})

在登录态组件中,根据用户的登录状态,如果未登录则显示未登录信息,如果已经登录,则显示登录状态,并且用户可以点击登出按钮退出登录。

Public

1
2
3
const Public = () => {
return (<h3>公开的页面</h3>)
}

Private

1
const Private = () => <h3>非公开的页面</h3>

Router

1
2
3
4
5
6
7
8
9
10
11
12
var router = (<BrowserRouter>
<div>
<AuthPage />
<ul>
<li><Link to="/public">public page</Link></li>
<li><Link to="/private">private page</Link></li>
</ul>
<Route path="/public" component={Public}/>
<Route path="/login" component={Login}/>
<PrivateRoute path="/private" component={Private}/>
</div>
</BrowserRouter>)

定义好应用程序的 Router,Router 中定义了三个路径,/public/login/private。其中,对于 /private 路径和 Private 组件做了一点特殊的处理,使用一个 PrivateRoute 来定义该 Route

PrivateRoute

1
2
3
4
5
6
7
8
9
10
11
12
13
const PrivateRoute = ({component: Component, ...rest}) => {
console.log(rest);
// Object {path: "/private"}
return (
<Route {...rest} render={props => (
fakeAuth.isAuthenticated ?
(<Component {...props}/>) :
(<Redirect to={{
pathname: '/login',
state: { from: props.location }
}}/>)
)}/>)
}

PrivateRoute 中,如果用户已经登录,则直接渲染该 Route 中的 component 属性所对应的组件,如果未登录,则使用 Redirect 组件转向 /login 登录页面。通过这样的处理,在应用程序中,在同一时刻只会是 登录态 或者 未登录态