1

我有一个带有圣所和灯塔的 laravel 新副本。当我通过 axios 进行登录路由时,一切都按预期工作。通过 axios 登录后,我添加了一个lazyquery以尝试查询一些受保护的字段,但我未通过身份验证。我不知道为什么,我已经处理了三天。我真的很感谢你的帮助。

这有效

useEffect(() => {
    axios.defaults.withCredentials = true;
    // get the token from the server
    axios.get(`http://api.newods.test/sanctum/csrf-cookie`).then(function (resolve){
      // try login with the user
      axios.post('http://api.newods.test/api/login', {
        email: 'test@test.com',
        password: 'test'
      }).then(function (resolve) {
        console.log(`logged in ${resolve.data}`);
        axios
          .get("http://api.newods.test/api/gated", { withCredentials: true })
          .then(function (resolve) {
            console.log(`gated ${resolve.data}`);
            axios
              .get("http://api.newods.test/api/logout", {
                withCredentials: true,
              })
              .then(function (resolve) {
                console.log(`logged out ${resolve.data}`);
                axios
                  .get("http://api.newods.test/api/gated", {
                    withCredentials: true,
                  })
                  .then(function (resolve) {
                    console.log(
                      `trying to get to gated after logging out ${resolve.data}`
                    );
                  });
              });
          });
      });
    });

  }, []);

但是当我缩短它并更改为这个时,我会未经身份验证

const HELLO = gql\`
  query hello {
    hello
  }
`;


function Home() {

  const [hello, { loading, data }] = useLazyQuery(HELLO);

  useEffect(() => {
    axios.defaults.withCredentials = true;
    // get the token from the server
    axios.get(`http://api.newods.test/sanctum/csrf-cookie`).then(function (resolve){
      // try login with the user
      axios.post('http://api.newods.test/api/login', {
        email: 'test@test.com',
        password: 'test'
      }).then(function (resolve) {
        console.log('logged in');

      });
    });

  }, []);


  return (
    <div className="container">
      <div>Index</div>
      <button onClick={() => hello()}>
        Click to hello world
      </button>
      <p>{data && data.hello || ''}</p>
    </div>
  );
}

export default withApollo(Home);

unauthenticated当我添加指令并且我看到来自 axios 登录请求的令牌在标题中时,它会返回@guard......我不确定我在这里缺少什么我非常感谢你的帮助。

架构.graphql

type Query {
    users: [User!]! @paginate(defaultCount: 10)
    user(id: ID @eq): User @find
    hello: String! @guard
    me: User @auth
}

.env

SESSION_DRIVER=cookie
SESSION_LIFETIME=120
SESSION_DOMAIN=.newods.test
SANCTUM_STATEFUL_DOMAINS=newods.test:3000 

配置/cors.php

return [
 
    'paths' => ['api/*', 'sanctum/csrf-cookie', 'graphql'],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['*'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true,

];

配置/灯塔

'route' => [
        /*
         * The URI the endpoint responds to, e.g. mydomain.com/graphql.
         */
        'uri' => '/graphql',

        /*
         * Lighthouse creates a named route for convenient URL generation and redirects.
         */
        'name' => 'graphql',

        /*
         * Beware that middleware defined here runs before the GraphQL execution phase,
         * make sure to return spec-compliant responses in case an error is thrown.
         */
        'middleware' => [
            \Nuwave\Lighthouse\Support\Http\Middleware\AcceptJson::class,

            // Logs in a user if they are authenticated. In contrast to Laravel's 'auth'
            // middleware, this delegates auth and permission checks to the field level.
            \Nuwave\Lighthouse\Support\Http\Middleware\AttemptAuthentication::class,
        ],

        /*
         * The `prefix` and `domain` configuration options are optional.
         */
        //'prefix' => '',
        //'domain' => '',
    ],

在我使用阿波罗的下一个应用程序中

创建.js

import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { createHttpLink } from 'apollo-link-http';
import { setContext } from 'apollo-link-context';
import Cookies from 'js-cookie';
import { serverUrl } from '../config';

export default function createApolloClient(initialState, ctx) {
  // The `ctx` (NextPageContext) will only be present on the server.
  // use it to extract auth headers (ctx.req) or similar.

  const authLink = setContext((_, { headers  }) => {
    // get the authentication token from local storage if it exists
    const token = Cookies.get("XSRF-TOKEN");
    // console.log(`token is ${token}`);
    // return the headers to the context so httpLink can read them
    return {
      headers: {
        ...headers,
        "Access-Control-Allow-Credentials": true,
        ...(token ? { authorization: `X-XSRF-TOKEN=${token}` } : {}),
      },
    };
  });

  const httpLink = createHttpLink({
    uri: serverUrl,
    credentials: 'same-origin',
  });

  return new ApolloClient({
    ssrMode: Boolean(ctx),
    link: authLink.concat(httpLink),
    connectToDevTools: true,
    cache: new InMemoryCache().restore(initialState),
  });
}

与Apollo.js

import React from "react";
import Head from "next/head";
import { ApolloProvider } from "@apollo/react-hooks";
import { ApolloClient } from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";
import { HttpLink } from "apollo-link-http";
import fetch from "isomorphic-unfetch";
import createApolloClient from './create';

let apolloClient = null;

/**
 * Creates and provides the apolloContext
 * to a next.js PageTree. Use it by wrapping
 * your PageComponent via HOC pattern.
 * @param {Function|Class} PageComponent
 * @param {Object} [config]
 * @param {Boolean} [config.ssr=true]
 */
export function withApollo(PageComponent, { ssr = true } = {}) {
  const WithApollo = ({ apolloClient, apolloState, ...pageProps }) => {
    const client = apolloClient || initApolloClient(apolloState);

    return (
      <ApolloProvider client={client}>
        <PageComponent {...pageProps} />
      </ApolloProvider>
    );
  };

  // Set the correct displayName in development
  if (process.env.NODE_ENV !== "production") {
    const displayName =
      PageComponent.displayName || PageComponent.name || "Component";

    if (displayName === "App") {
      console.warn("This withApollo HOC only works with PageComponents.");
    }

    WithApollo.displayName = `withApollo(${displayName})`;
  }

  if (ssr || PageComponent.getInitialProps) {
    WithApollo.getInitialProps = async (ctx) => {
      const { AppTree } = ctx;

      // Initialize ApolloClient, add it to the ctx object so
      // we can use it in `PageComponent.getInitialProp`.
      const apolloClient = (ctx.apolloClient = initApolloClient(
        {},
        ctx.req.headers.cookie
      ));

      // Run wrapped getInitialProps methods
      let pageProps = {};
      if (PageComponent.getInitialProps) {
        pageProps = await PageComponent.getInitialProps(ctx);
      }

      // Only on the server:
      if (typeof window === "undefined") {
        // When redirecting, the response is finished.
        // No point in continuing to render
        if (ctx.res && ctx.res.finished) {
          return pageProps;
        }

        // Only if ssr is enabled
        if (ssr) {
          try {
            // Run all GraphQL queries
            const { getDataFromTree } = await import("@apollo/react-ssr");
            await getDataFromTree(
              <AppTree
                pageProps={{
                  ...pageProps,
                  apolloClient,
                }}
              />
            );
          } catch (error) {
            // Prevent Apollo Client GraphQL errors from crashing SSR.
            // Handle them in components via the data.error prop:
            // https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-query-data-error
            console.error("Error while running `getDataFromTree`", error);
          }

          // getDataFromTree does not call componentWillUnmount
          // head side effect therefore need to be cleared manually
          Head.rewind();
        }
      }

      // Extract query data from the Apollo store
      // @ts-ignore
      const apolloState = apolloClient.cache.extract();

      return {
        ...pageProps,
        apolloState,
      };
    };
  }

  return WithApollo;
}

/**
 * Always creates a new apollo client on the server
 * Creates or reuses apollo client in the browser.
 * @param  {Object} initialState
 */
function initApolloClient(initialState = {}, cookie = "") {
  // Make sure to create a new client for every server-side request so that data
  // isn"t shared between connections (which would be bad)

  if (typeof window === "undefined") {
    return createApolloClient(initialState, cookie);
  }

  // Reuse client on the client-side
  if (!apolloClient) {
    // @ts-ignore
    apolloClient = createApolloClient(initialState);
  }

  return apolloClient;
}

使用@guard 指令的查询结果

4

1 回答 1

2

我有一个非常相似的架构,但使用的是 Vue。通过将您的代码与我的工作实现进行比较,我认为您的大部分问题都在 create.js 中。

我对 js-cookie 了解不多,但这就是我获取 XSRF-TOKEN 并对其进行解码的方式。

let token = RegExp('XSRF-TOKEN[^;]+').exec(document.cookie)
token = decodeURIComponent(token ? token.toString().replace(/^[^=]+./, '') : '')

然后,在您的 setContext 中,您需要按如下方式设置标头。

return {
   headers: {
     ...headers,
     'X-XSRF-TOKEN': token,
   }
}

此外,即使我使用的是子域,我也遇到了凭据问题:'same-origin'。因此我建议:

const httpLink = createHttpLink({
  uri: serverUrl,
  credentials: 'include',
})
于 2020-07-05T21:42:59.950 回答