在我的React/nextJS应用程序中,我正在检查getInitialProps
静态函数中的有效令牌。我用这个作为HOC——但在这种情况下这应该无关紧要。
如果令牌无效(或丢失(,则用户将被重定向到登录页面。这是由redirect
函数完成的,如下所示。到目前为止,一切都很好。
如何将用户重定向到登录组件的页面的url传递给用户?
如果用户未登录并且正在调用类似的内容http://my-server.com/any-page,他被重定向到索引页面(http://my-server.com(:会有一个登录表单。如果登录成功,我想将他重定向回第一个被调用的页面:http://my-server.com/any-page
- 以未登录用户身份调用受限页面
- 重定向到索引登录页
- 登录后重定向回第1页
我不知道如何将这些信息传递给登录功能。。。
使用服务器道具.js
export default WrappedComponent =>
class extends Component {
static async getInitialProps (context) {
const { req, pathname } = context
let isValid = false
if (req && req.headers) {
const cookies = req.headers.cookie
if (typeof cookies === 'string') {
const cookiesJSON = jsHttpCookie.parse(cookies)
initProps.token = cookiesJSON['auth-token']
if (cookiesJSON['auth-token']) {
jwt.verify(cookiesJSON['auth-token'], secret, (error, decoded) => {
if (error) {
console.error(error)
} else {
isValid = true
}
})
}
}
}
// Redirect to index (=login) page if isValid is false
if (!isValid && pathname && pathname !== '/') {
redirect(context, pathname ? '/?ref=' + pathname : '/')
}
return initProps
}
render () {
return <WrappedComponent {...this.props} />
}
}
重定向.js
import Router from 'next/router'
export default (context, target) => {
if (context.res) {
// server
context.res.writeHead(303, { Location: target })
context.res.end()
} else {
// In the browser, we just pretend like this never even happened ;)
Router.replace(target)
}
}
pages/index.js
在index.js上有submit
函数来登录用户。在那里,用户应该被重定向到初始页面:
_onSubmit (event) {
this.props.loginMutation({
variables: { username, password }
}).then(response => {
const token = response.data.token
if (token) {
Cookies.set('auth-token', token, { expires: 1 })
this.props.client.resetStore().then(() => {
window.location.assign('/') // <-- Redirect to initial called page
})
}
})
}
在with-server-props.js
中,用URL对象替换路径
redirect(context, {
pathname: '/',
query: { redirect: req.url } // req.url should give you the current url on server side
})
这将向url添加一个重定向参数https://example.com/?redirect=/about
然后您可以使用getInitialProps
:获取任何页面上的url参数
this.redirectUrl = (req && req.query['redirect']) ? decodeURIComponent(req.query['redirect']) : '/'
最后
window.location.assign(this.redirectUrl)
希望有帮助,让我知道。
您需要的是react-router
或更具体地说是react-router-dom
包。如果你了解它的工作方式,那就轻而易举了。
对于您的场景,您使用<Redirect to={url} />
,而不是在未进行身份验证时调用redirect()
。这会自动执行浏览器url替换和更新全局状态。不过,你已经巧妙地分配了一个与任何特殊情况相匹配的url。例如,"/login/ref/:toref"将是处理url"/login/ref/{specialaccess}"的基表达式。
注意":"。它是params匹配器,用于检索登录组件中的url
正如他们所说,一行代码胜过千言万语。因此,我做了一个小项目来充分展示如何实现react-router-dom
的一些重要功能。
在此处查找:https://codesandbox.io/s/y0znxpk30z
在项目中,当您尝试访问https://y0znxpk30z.codesandbox.io/specialaccess通过浏览器模拟器,您可以在登录时获得身份验证后重定向到特殊访问页面。否则,如果您访问https://y0znxpk30z.codesandbox.io登录后,您会被重定向到主页。
记住,你必须像这样包装任何期望全局道具withRouter
的组件:
export default withRouter(component-name);
这在每个组件中都提供了this.props.location
、this.props.history
和this.props.match
,因为我们已经将应用程序的根组件放置在包中默认可用的<BrowserRouter><BrowserRouter/>
HOC中。
使用this.props.match
,我们可以很容易地引用并重定向回我们之前在":toref"中指定的url。
你可以在这里阅读更多关于react-router
的
jwt.verify
函数以异步回调方式使用。
这种风格更适合以这种方式使用的WrappedComponent
的componentDidMount
生命周期方法
为其传递回调意味着isValid
的值可能永远不会更新得足够早,即使客户端JWT令牌是有效的,并且用户将始终被重定向。
我建议使用不带回调的同步变体(测试以比较在呈现封装组件之前的时间(。更好的是,将jwt.verify
回调样式转换为返回promise的函数,以便在getInitialProps
是async
函数的情况下,可以在await
表达式中解析它。
if (req && req.headers) {
const cookies = req.headers.cookie
if (typeof cookies === 'string') {
const cookiesJSON = jsHttpCookie.parse(cookies)
initProps.token = cookiesJSON['auth-token']
if (cookiesJSON['auth-token']) {
try {
const payload = jwt.verify(cookiesJSON['auth-token'], secret)
if (payload) {
isValid = true
}
} catch (err) {
isValid = false
}
}
}
}
现在,在重定向用户的_onsubmit
方法中,可以获得WrappedComponent.getInitialProps
中设置的ref
查询参数值,并使用该值重定向用户。
const ref = new URLSearchParams(location.search).get('ref');
location.assign(`/${ref}`);
将返回url作为查询参数或位置状态传递到登录页面。我在Next.js的文档页面上找到了一个使用查询参数推送路由的示例。
import Router from 'next/router'
const handler = () => {
Router.push({
pathname: '/about',
query: { name: 'Zeit' }
})
}
export default () => (
<div>
Click <span onClick={handler}>here</span> to read more
</div>
)
使用从HOC传入的返回url尝试Router.push,而不是Router.replace。希望这能有所帮助。