Gatsby结合Contentful实现client-only路由私有内容权限控制问题
Gatsby client-only路由权限控制问题
目标
计划为/dashboard路径下的内容使用client-only路由,该路径下部分内容来自Contentful,将通过页面模板渲染,路由示例为{MYDOMAIN}/dashboard/{SLUG_FROM_CONTENTFUL},目的是确保代理公司参与的项目内容无法被爬虫抓取或公开访问,仅登录后的「雇主」可见。
已尝试方案
页面通过gatsby-node.js生成,认证与client-only路由的实现参考Gatsby官方认证教程,目前基础功能运行正常:登录状态下访问/dashboard会加载Profile.js,未登录状态下访问/dashboard会加载Login.js。
但存在异常:未登录状态下直接访问/dashboard/url-from-contentful时,不会跳转至/dashboard/login,而是直接返回对应页面内容。
相关代码如下:
gatsby-node.js 代码
exports.createPages = async ({graphql, actions}) => { const { createPage } = actions; const { data } = await graphql(` query { agency: allContentfulAgency { edges { node { slug } } } } `); data.agency.edges.forEach(({ node }) => { createPage({ path: `dashboard/${node.slug}`, component: path.resolve("src/templates/agency-template.js"), context: { slug: node.slug, }, }); }); } exports.onCreatePage = async ({ page, actions }) => { const { createPage } = actions; if(page.path.match(/^\/dashboard/)) { page.matchPath = "/dashboard/*"; createPage(page); } };
auth.js 代码
export const isBrowser = () => typeof window !== "undefined"; export const getUser = () => isBrowser() && window.localStorage.getItem("gatsbyUser") ? JSON.parse(window.localStorage.getItem("gatsbyUser")) : {}; const setUser = (user) => window.localStorage.setItem("gatsbyUser", JSON.stringify(user)); export const handleLogin = ({ username, password }) => { if (username === `john` && password === `pass`) { return setUser({ username: `john`, name: `Johnny`, email: `johnny@example.org`, }); } return false; }; export const isLoggedIn = () => { const user = getUser(); return !!user.username; }; export const logout = (callback) => { setUser({}); call };
PrivateRoute.js 代码
import React from "react"; import { navigate } from "gatsby"; import { isLoggedIn } from "../services/auth"; const PrivateRoute = ({ component: Component, location, ...rest }) => { if (!isLoggedIn() && location.pathname !== `/dashboard/login`) { navigate("/dashboard/login"); return null; } return <Component {...rest} />; }; export default PrivateRoute;
dashboard.js 代码
import React from "react"; import { Router } from "@reach/router"; import Layout from "../components/Layout"; import Profile from "../components/Profile"; import Login from "../components/Login"; import PrivateRoute from "../components/PrivateRoute"; import Agency from "../templates/agency-template"; const App = () => ( <Layout> <Router> <PrivateRoute path="/dashboard/url-from-contentful" component={Agency} /> <PrivateRoute path="/dashboard/profile" component={Profile} /> <PrivateRoute path="/dashboard" /> <Login path="/dashboard/login" /> </Router> </Layout> ); export default App;
agency-template.js 代码
import React from "react"; import { graphql, Link } from "gatsby"; import styled from "styled-components"; import SEO from "../components/SEO"; import Layout from "../components/Layout"; import Gallery from "../components/Gallery"; import GeneralContent from "../components/GeneralContent/GeneralContent"; const agencyTemplate = ({ data }) => { const { name, excerpt, richDescription, richDescription: { raw }, images, technology, website, } = data.agency; const [mainImage, ...projectImages] = images; return ( <> <SEO title={name} description={excerpt} /> <Layout> <div className="container__body"> <GeneralContent title={name} /> <Gallery mainImage={mainImage} /> <GeneralContent title="Project Details" content={richDescription} /> <div className="standard__images"> <Gallery projectImages={projectImages} /> </div> <ViewWebsite> <Link className="btn" to={website}> View the website </Link> </ViewWebsite> </div> </Layout> </> ); }; export const query = graphql` query ($slug: String!) { agency: contentfulAgency(slug: { eq: $slug }) { name excerpt technology website images { description gatsbyImageData( layout: FULL_WIDTH placeholder: TRACED_SVG formats: [AUTO, WEBP] quality: 90 ) } richDescription { raw } } } `; export default agencyTemplate;
Gatsby作为SSG应该可以实现CMS内容的访问限制,提问者对client-only路由的底层逻辑理解存在偏差,且刚接触React和Gatsby,希望获得相关指导。
最终解决方案
调整了实现逻辑:不再通过gatsby-node.js调用模板生成页面,改为采用React原生路由逻辑,查询到Contentful数据后通过map方法动态生成Agency组件路由,同时结合useContext做状态管理、引入Auth0实现认证,解决了权限控制问题。相关代码如下:
return ( <> <Router> <DashboardArea path="/dashboard/" user={user} /> {agencyData.map(({ node }, index) => node.slug ? ( <Agency key={index} data={node} path={`/dashboard/${node.slug}`} /> ) : null )} </Router> </> );
内容的提问来源于stack exchange,提问作者Bagseye
相关产品推荐
相关产品推荐

