在 react-admin 中添加无需身份验证的自定义页面
Adding custom page without authentication in react-admin
我想在我的 react-admin
门户网站中添加一个注册页面。由于 react-admin
不提供注册页面,我创建了一个自定义页面并将其添加到自定义路由中:
customRoutes.js
import React from 'react';
import { Route } from 'react-router-dom';
import SignupForm from './signupForm';
export default [
<Route path="/signup" component={SignupForm} noLayout/>,
];
问题是当用户已经登录时,我只能在 /signup
打开页面。否则我会自动重定向到 /login
路由。
如何禁用自定义路由的身份验证? <Route>
是否接受某些属性或与 dataProvider.js
有关?
编辑:
为我的 signupForm.js
添加代表代码:
import React from 'react';
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import { fetchUtils } from 'react-admin';
import { ApiUrl } from './config';
class SignupForm extends React.Component {
constructor() {
super();
this.state = {
fields: {
username: '',
password: ''
}
}
handleChange = name => event => {
let fields = this.state.fields;
fields[name] = event.target.value;
this.setState({
fields: fields,
});
};
handleSubmit = (event) => {
// Prevent default
event.preventDefault();
if (this.handleValidation()) {
let body = JSON.parse(JSON.stringify(this.state.fields));
let url = ApiUrl + '/api/user/create';
let options = {}
options.headers = new Headers({ Accept: 'application/json' });
options.method = 'POST'
options.body = JSON.stringify(body);
fetchUtils.fetchJson(url, options)
.then(data => {
alert(data.json.message);
this.props.history.push('/login')
})
.catch((err, ...rest) => {
console.log(err.status, err.message);
alert(err.message);
});
}
}
render() {
const { classes } = this.props;
const actions = [
<Button
type="submit"
label="Submit"
color='primary'
variant="flat"
>Submit</Button>,
];
return (
<Dialog
open={true}
style={{
textAlign: "center",
}}
onClose={this.handleClose}
classes={{ paper: classes.dialogPaper }}
>
<DialogTitle>Create an Account</DialogTitle>
<form className={classes.container} noValidate autoComplete="off" onSubmit={this.handleSubmit}>
<TextField
required
id="username"
label="Username"
value={this.state.fields["username"]}
onChange={this.handleChange('username')}
/>
<br />
<TextField
required
id="password"
label="Password"
value={this.state.fields["password"]}
onChange={this.handleChange('password')}
type="password"
/>
<div style={{ textAlign: 'right', padding: 40 }}>
{actions}
</div>
</form>
</Dialog>
);
}
}
export default withStyles(styles)(SignupForm);
问题是,根据 /signup
的请求,react-admin
正在调用类型为 AUTH_GET_PERMISSIONS
的 authProvider
,其代码为:
if (type === AUTH_GET_PERMISSIONS) {
const role = localStorage.getItem('role');
return role ? Promise.resolve(role) : Promise.reject();
}
由于用户未登录,因此 localStorage.role
从未初始化。
更改为:
if (type === AUTH_GET_PERMISSIONS) {
const role = localStorage.getItem('role');
return role ? Promise.resolve(role) : Promise.resolve('guest');
}
React-admin V3版本,默认将authProvider改为return'guest'角色:
authProvider.js
...,
getPermissions: () => {
const role = localStorage.getItem('permissions')
return role ? Promise.resolve(role) : Promise.resolve('guest') // default 'guest'
},
...
现在您的 customRoutes pages will no longer redirect to /login, and you can use the usePermissions 钩子可以检查自定义页面中的角色。
您可以在 customRoutes.js
文件中使用所有未经身份验证的路由创建一个类似枚举的对象 PublicRoutes
,如下所示:
customRoutes.js
export const PublicRoutes = {
SIGNUP: "/signup",
}
export const customRoutes = [
<Route path={PublicRoutes.SIGNUP} component={SignupForm} noLayout/>
]
然后在您的 authProvider
中导入 history
或 router
对象(在您的 Admin
组件中创建),以访问当前位置 pathname
。
接下来,创建一个函数表达式isPublicRoute
,它将检查当前路由是否可以在不进行身份验证的情况下提供服务。
在 AUTH_GET_PERMISSIONS
和可选的 AUTH_CHECK
之上添加此检查(如果您在此处有例如 JWT 解析器)。
对于 AUTH_GET_PERMISSIONS
return Promise.resolve()
因为我们有 public 路由的权限。对于 AUTH_CHECK
return Promise.reject()
因为我们在这里不需要授权(例如获取或解析 JWT)。
authProvider.js
import {PublicRoutes} from "./customRoutes";
...
const isPublicRoute = (pathname) => Object.values(PublicRoutes).some(route => route === pathname);
...
const {pathname} = router.location;
if (type === AUTH_GET_PERMISSIONS) {
// has to be on top
if(isPublicRoute(pathname)) return Promise.resolve();
...
}
if (type === AUTH_CHECK) {
// has to be on top
if(isPublicRoute(pathname)) return Promise.reject()
...
}
在您的 authProvider 中,您应该有一个名为 checkAuth
的方法,如果 window.location.pathname
是 public 路由,则 returns Promise.resolve()
。
const publicRoutes = [
'/appointment',
]
const authProvider: AuthProvider = {
login: ({ username, password }) => {
},
checkAuth: () => {
if (publicRoutes.includes(window.location.pathname)) {
return Promise.resolve()
}
// your authentication endpoint / system
// ...
},
getPermissions: () => {
},
logout: () => {
},
checkError: (error) => {
},
getIdentity: () => {
},
};
我想在我的 react-admin
门户网站中添加一个注册页面。由于 react-admin
不提供注册页面,我创建了一个自定义页面并将其添加到自定义路由中:
customRoutes.js
import React from 'react';
import { Route } from 'react-router-dom';
import SignupForm from './signupForm';
export default [
<Route path="/signup" component={SignupForm} noLayout/>,
];
问题是当用户已经登录时,我只能在 /signup
打开页面。否则我会自动重定向到 /login
路由。
如何禁用自定义路由的身份验证? <Route>
是否接受某些属性或与 dataProvider.js
有关?
编辑:
为我的 signupForm.js
添加代表代码:
import React from 'react';
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import { fetchUtils } from 'react-admin';
import { ApiUrl } from './config';
class SignupForm extends React.Component {
constructor() {
super();
this.state = {
fields: {
username: '',
password: ''
}
}
handleChange = name => event => {
let fields = this.state.fields;
fields[name] = event.target.value;
this.setState({
fields: fields,
});
};
handleSubmit = (event) => {
// Prevent default
event.preventDefault();
if (this.handleValidation()) {
let body = JSON.parse(JSON.stringify(this.state.fields));
let url = ApiUrl + '/api/user/create';
let options = {}
options.headers = new Headers({ Accept: 'application/json' });
options.method = 'POST'
options.body = JSON.stringify(body);
fetchUtils.fetchJson(url, options)
.then(data => {
alert(data.json.message);
this.props.history.push('/login')
})
.catch((err, ...rest) => {
console.log(err.status, err.message);
alert(err.message);
});
}
}
render() {
const { classes } = this.props;
const actions = [
<Button
type="submit"
label="Submit"
color='primary'
variant="flat"
>Submit</Button>,
];
return (
<Dialog
open={true}
style={{
textAlign: "center",
}}
onClose={this.handleClose}
classes={{ paper: classes.dialogPaper }}
>
<DialogTitle>Create an Account</DialogTitle>
<form className={classes.container} noValidate autoComplete="off" onSubmit={this.handleSubmit}>
<TextField
required
id="username"
label="Username"
value={this.state.fields["username"]}
onChange={this.handleChange('username')}
/>
<br />
<TextField
required
id="password"
label="Password"
value={this.state.fields["password"]}
onChange={this.handleChange('password')}
type="password"
/>
<div style={{ textAlign: 'right', padding: 40 }}>
{actions}
</div>
</form>
</Dialog>
);
}
}
export default withStyles(styles)(SignupForm);
问题是,根据 /signup
的请求,react-admin
正在调用类型为 AUTH_GET_PERMISSIONS
的 authProvider
,其代码为:
if (type === AUTH_GET_PERMISSIONS) {
const role = localStorage.getItem('role');
return role ? Promise.resolve(role) : Promise.reject();
}
由于用户未登录,因此 localStorage.role
从未初始化。
更改为:
if (type === AUTH_GET_PERMISSIONS) {
const role = localStorage.getItem('role');
return role ? Promise.resolve(role) : Promise.resolve('guest');
}
React-admin V3版本,默认将authProvider改为return'guest'角色:
authProvider.js
...,
getPermissions: () => {
const role = localStorage.getItem('permissions')
return role ? Promise.resolve(role) : Promise.resolve('guest') // default 'guest'
},
...
现在您的 customRoutes pages will no longer redirect to /login, and you can use the usePermissions 钩子可以检查自定义页面中的角色。
您可以在 customRoutes.js
文件中使用所有未经身份验证的路由创建一个类似枚举的对象 PublicRoutes
,如下所示:
customRoutes.js
export const PublicRoutes = {
SIGNUP: "/signup",
}
export const customRoutes = [
<Route path={PublicRoutes.SIGNUP} component={SignupForm} noLayout/>
]
然后在您的 authProvider
中导入 history
或 router
对象(在您的 Admin
组件中创建),以访问当前位置 pathname
。
接下来,创建一个函数表达式isPublicRoute
,它将检查当前路由是否可以在不进行身份验证的情况下提供服务。
在 AUTH_GET_PERMISSIONS
和可选的 AUTH_CHECK
之上添加此检查(如果您在此处有例如 JWT 解析器)。
对于 AUTH_GET_PERMISSIONS
return Promise.resolve()
因为我们有 public 路由的权限。对于 AUTH_CHECK
return Promise.reject()
因为我们在这里不需要授权(例如获取或解析 JWT)。
authProvider.js
import {PublicRoutes} from "./customRoutes";
...
const isPublicRoute = (pathname) => Object.values(PublicRoutes).some(route => route === pathname);
...
const {pathname} = router.location;
if (type === AUTH_GET_PERMISSIONS) {
// has to be on top
if(isPublicRoute(pathname)) return Promise.resolve();
...
}
if (type === AUTH_CHECK) {
// has to be on top
if(isPublicRoute(pathname)) return Promise.reject()
...
}
在您的 authProvider 中,您应该有一个名为 checkAuth
的方法,如果 window.location.pathname
是 public 路由,则 returns Promise.resolve()
。
const publicRoutes = [
'/appointment',
]
const authProvider: AuthProvider = {
login: ({ username, password }) => {
},
checkAuth: () => {
if (publicRoutes.includes(window.location.pathname)) {
return Promise.resolve()
}
// your authentication endpoint / system
// ...
},
getPermissions: () => {
},
logout: () => {
},
checkError: (error) => {
},
getIdentity: () => {
},
};