1、一级路由
const routerData = getRouterData(app);
const UserLayout = routerData['/user'].component;
const BasicLayout = routerData['/'].component;
return (
<LocaleProvider locale={zhCN}>
<ConnectedRouter history={history}>
<Switch>
<Route path="/user" component={UserLayout} />
<AuthorizedRoute
path="/"
render={props => <BasicLayout {...props} />}
authority={['admin', 'user']}
redirectPath="/user/login"
/>
</Switch>
</ConnectedRouter>
</LocaleProvider>
);
整个项目的路由分两部分:一部分是user相关的,一部分是业务相关的
- 含有/user的路由 — 对应的路由组件时UserLayout
- 排除/user外的路由 — 对应的路由组件时BasicLayout
2、二级路由
- UserLayout
UserLayout中设置了二级路由,其中match.path对应为props属性中的match.path,此处即为/user,routerData为所有的路由数据,详见(src/common/router.js),getRoutes(match.path, routerData)方法获取所有的user相关的路由配置,并生成对应的路由组件。同时Redirect组件设置了默认的路由。
<Switch>
{getRoutes(match.path, routerData).map(item => (
<Route
key={item.key}
path={item.path}
component={item.component}
exact={item.exact}
/>
))}
<Redirect exact from="/user" to="/user/login" />
</Switch>
- BasicLayout
所有相关的路由信息,都包含有侧边栏、顶部菜单、内部部分、底部等布局,即SiderMenu、Header、Content、Footer。路由的布局则部署在content组件中,同UserLayout,通过getRoutes获取相关路由的信息,并生成队友路由组件route。
<Layout>
<SiderMenu/>
<Layout>
<Header>
<GlobalHeader/>
</Header>
<Header>
<Content>
<Switch>
{redirectData.map(item => (
<Redirect key={item.from} exact from={item.from} to={item.to} />
))}
{getRoutes(match.path, routerData).map(item => (
<AuthorizedRoute
key={item.key}
path={item.path}
component={item.component}
exact={item.exact}
authority={item.authority}
redirectPath="/exception/403"
/>
))}
<Redirect exact from="/" to={bashRedirect} />
<Route render={NotFound} />
</Switch>
</Content>
<Footer>
<GlobalFooter/>
</Footer>
</Layout>
</Layout>
当前路由集合中第一个通过认证的路由被设置为默认的路由,即Redirect的指向
3、路由认证 AuthorizedRoute
BasicLayout中路由都是循环生成AuthorizedRoute组件完成,包括我们的一级路由也是通过AuthorizedRoute组件封装的,那么AuthorizedRoute到底是干嘛的?
//BasicLayout.js
import Authorized from '../utils/Authorized';
const { AuthorizedRoute, check } = Authorized;
由以上代码可知,AuthorizedRoute是Authorized组件的一个属性。在../utils/Authorized.js中我们可以找到如下代码。其中getAuthority()获取用户存放在localStorage的权限记录。Authorized是由RenderAuthorized(getAuthority())生成。
//../utils/Authorized.js
import RenderAuthorized from '../components/Authorized';
import { getAuthority } from './authority';
let Authorized = RenderAuthorized(getAuthority()); // eslint-disable-line
继续跟踪上述代码,可以看到../components/Authorized中有两个export:1、当前权限值,通过renderAuthorize方法传入当前权限值计算(对应let Authorized = RenderAuthorized(getAuthority()));2、Authorized组件,有三部分组成,其中有我们的
AuthorizedRoute组件。
//../components/Authorized
import AuthorizedRoute from './AuthorizedRoute';
....
Authorized.Secured = Secured;
Authorized.AuthorizedRoute = AuthorizedRoute;
Authorized.check = check;
....
const renderAuthorize = currentAuthority => {
if (currentAuthority) {
if (currentAuthority.constructor.name === 'Function') {
CURRENT = currentAuthority();
}
if (currentAuthority.constructor.name === 'String') {
CURRENT = currentAuthority;
}
} else {
CURRENT = 'NULL';
}
return Authorized;
};
export { CURRENT };
export default renderAuthorize;
通过一些类引用,终于看到了最终的AuthorizedRoute。
//AuthorizedRoute.js
....
return (
<Authorized
authority={authority}
noMatch={<Route {...rest} render={() => <Redirect to={{ pathname: redirectPath }} />} />}
>
<Route {...rest} render={props => (Component ? <Component {...props} /> : render(props))} />
</Authorized>
);
在AuthorizedRoute组件中又包含Authorized组件封装。Authorized是一个高阶组件,通过调用CheckPermissions方法返回一个新的组件。具体的Authorized代码如下:
import CheckPermissions from './CheckPermissions';
render() {
const { children, authority, noMatch = null } = this.props;
const childrenRender = typeof children === 'undefined' ? null : children;
return CheckPermissions(authority, childrenRender, noMatch);
}
CheckPermissions组件(返回有check,引用时被重命名为 CheckPermissions),其中CURRENT即为之前通过RenderAuthorized(getAuthority())时,计算的当前权限。checkPermissions方法,会判定CURRENT是否在authority权限范围内,如果有权限则返回target,否则返回Exception
import { CURRENT } from './index';
...
/**
* 通用权限检查方法
* Common check permissions method
* @param { 权限判定 Permission judgment type string |array | Promise | Function } authority
* @param { 你的权限 Your permission description type:string} currentAuthority
* @param { 通过的组件 Passing components } target
* @param { 未通过的组件 no pass components } Exception
*/
const checkPermissions = (authority, currentAuthority, target, Exception) => {
...
})
...
const check = (authority, target, Exception) => {
return checkPermissions(authority, CURRENT, target, Exception);
};
export default check;
至此可知。结合路由生成是调用AuthorizedRoute组件封装和AuthorizedRoute组件的功能
- 路由生成:
<Switch>
{redirectData.map(item => (
<Redirect key={item.from} exact from={item.from} to={item.to} />
))}
{getRoutes(match.path, routerData).map(item => (
<AuthorizedRoute
key={item.key}
path={item.path}
component={item.component}
exact={item.exact}
authority={item.authority}
redirectPath="/exception/403"
/>
))}
<Redirect exact from="/" to={bashRedirect} />
<Route render={NotFound} />
</Switch>
- AuthorizedRoute
<Authorized
authority={authority}
noMatch={<Route {...rest} render={() => <Redirect to={{ pathname: redirectPath }} />} />}
>
<Route {...rest} render={props => (Component ? <Component {...props} /> : render(props))} />
</Authorized>
当 authority={item.authority}权限通过的时候,及生成路由组件Route,该路由对应的组件为component={item.component};当权限不通过时,即加载nomatch生成的组件Route,此时重定向到redirectPath=”/exception/403”。