Day 22


!! イメージ、ログイン、導入などが最も重要なプロセスなので、プロセスを理解したほうがいいです.

ログインプロセス


重要なのは、ログインプロセスがどのように実現されているかを知ることです.
  • ブラウザは、HTML、CSS、JS
  • を取得するようにフロントエンドに要求する.
  • のデータが必要な部分はバックエンドであり、バックエンドからDBを介して
  • に導入する.
    では、商品登録で登録したい人だけにしてもらいたいとしたら?
    ブラウザがバックエンドにcreateProductを要求すると.
    バックエンドでは、ログインしていることを確認する必要があります.

    以前はどうやってこれを区別していましたか.
    ブラウザでID/PWを使用してログインする場合は、バックエンドにIDを記録し、いつログインするかを指定します.
    (これをメモリセッションと呼びます)
    この情報を使用して、ログインしていない作成者または作成者にメッセージを送信します.しかし、ここの問題は何ですか?
    人々は接続するたびに(トラフィックと呼ばれる)バックエンドコンピュータに格納しますが、メモリが切れた場合、情報は格納されません.
    ->ストレージ容量を増やし、cpuを交換して高速化する必要があります(scaleup)
    ->しかし、それができないため、statepullと呼ばれる同じ情報を持つ複数のバックエンドサーバをインポートして処理できます.

    ->ただし、私のログオンレコードが残りのバックエンドコンピュータでない場合、ログオンされているかどうかは認識できません(情報は同じですが、処理方法が異なるため)
    ->セッション(ログイン情報)をDBに入れたら?ここでは、Backendコンピュータを追加すれば、どのバックエンドコンピュータを使用してもログインは同じです.これがstatelessです.

    ->データベースがボトルネックになり、メモリが無限ではないため、バックエンドコンピュータが増えてもデバイスを増やすのは難しい
    ->では、複数のDBを作成してもいいのではないでしょうか.
    ->私たちのDBは大きいですが、どうやってコピーしますか?

    資格認定表パーティション


    ->DBテーブルは別々に保存されています.パーティション、パーティションと呼ばれています.

    ->だから会員情報をUserとして保存し、ポイントと口座はUserPaymentがあなたが保管します(垂直パーティション)
    ->1番から1000番までは1番DB、2000番から3000番までは2番DB、3000番から4000番までは3番DB(水平パーティション、データベースサディン)
    じゃ、今どうやって分けますか.パソコンを一つずつつないでくれませんか.(RoundRobin)
    ->それとも最も接続が少ないパソコンで受信しますか?
    ->DBで何を書くかはRedisで区別する
    ->小さなアクセルのようなもので、ディスクではなく一時的にメモリに保存し、バックエンドでデバイスを要求するときはデバイスではなくRedisを使用します.
    ->最終的にはDBにデータをインポートして取り出し、DBに書き込む必要があります.
    ->でもDBを使わなければ?

    JWTトークン



    ->JWTトークンの出現(未出現)
    ->ブラウザからバックエンドにID/パスワードを入れる
    ->DBで登録されている情報を入力すると、ランダム文字のID(これをトークンと呼び、ここでは認証用なので認証トークン)を拡大することができます
    ->認証トークンをバックエンドに送信する
    ->タグをブラウザに保存します.場所はグローバルまたはブラウザの内部リポジトリです(後で学習します)
    ->myTokenという状態にしておく
    ->製品の作成時にname,price...MyTokenも送ってきた!
    ->バックエンドでログインしているかどうかを確認します
    ->これはただのコインですが、今は本物のJWTコインが登場しています
    (ここでは、フロントエンドでタグをmyTokenとして保存し、myTokenをバックエンドに渡すことができますが、問題を処理したり、バックエンド開発者と話をしたりするための構造とプロセスを理解する必要があります)
    ->ただ、コインは一つのことをするたびに設備を剃って、流量が増えて、問題が多いようです.
    ->JSON WEB TOKEN-JWTを書く
    何が違うの?

    ->哲秀と言いましょうかㄱ->aㄴ-bと置き換えると、哲秀は哲秀の意味のアルファベット(暗号化)で、逆に回すと哲秀(復号)になります.
    ->JSONは{名前:[元に戻す],ログイン時間:[2021.10.31]}
    ->暗号化後、不明な文字はJWTと呼ばれます(詳細は説明しますが)
    ->もちろん、解読すればすぐに撤退とログ人気の間に
    ->これは、トークン自体にログイン情報が含まれているため、バックエンドからデータベースを取得する必要がないことを意味します.
    じゃ、今どうしますか.

    ->ブラウザにID/パスワードを入力する
    ->バックエンドでJWTコインをください
    ->そしてブラウザでJWTをmyToken stateとして保存
    ->製品リリース(CreateProduct)myTokenもバックエンドに一緒に送信
    ->バックエンドで復号する場合、ログイン情報がDBに登録されていることを確認する必要はありません👍
    ->商品情報をDBに送信します.
    JWTトークンの制限点
    JWTはバックエンド接続に入るかどうかを区別するため、途中で盗み取ってもDBにこの情報が正しいかどうかを検証することはできません(DBをリフレッシュしないため)
    ->有効期限を通常のトークンに短縮(30分~2時間)
    ->混在して使いましょう.最初にデータベースに記録します.
    ->時間が過ぎて期限切れになったら、自動再送時にも記録しておきます(マークを更新)
    ->セキュリティが重要な場合はredisを貼り、トラフィックを節約する場合はJWT方式を使用します.
    トークン送信プロセスの詳細の表示

    遊園地で会員になってみましょう.

    これによりログイン情報がコインに変換されることがわかります.こんなに長いのは情報を変換したからです.
    今この情報を利用すると??

    △コインがなければ、こんなものを要求することもできません.
    「HTTP HEADERSへの許可」:「Beare+トークン」
    ベレルはいつものように書いてありますが、この用途は何ですか.

    (コインを入れればOK)
    HTTP HEADERSにログイン情報を追加する方法は非常に重要であり、どこでも使用できるので、必ずご理解ください
    !! トークンの使用上の注意

    jwt.io
    JWTトークン表示のWebサイトを開きます.このように、身分、使用日、使用用途などは事実上誰にでも見られるので、敏感な情報はこのコインに入れないほうがいいです.ただし操作はできません.作成時にkeyで作成し、操作時にバックエンドで検証するため、時間がかかります.
    実はハーモニーにはどう書けばいいのでしょうか?
    apolloの設定ページに置けばいいです
    ハッシュ#ハッシュ#
    会員情報を保存する場合は、Eメール、氏名、パスワードを保存します.
    しかしパスワードなどをそのまま保存すると漏れてしまう一般的なパスワードを一緒に使うと….大変なことになった.
    ->パスワードを入力すると、テーブルに暗号化された文字が保存されます
    入力1234->abcdとして保存
    弾けないと思ってるの?また持ってきたテーブルは全部交換しました.
    ->暗号化には2つの方法があります
    ->双方向暗号化:1234-abcd->書き換えにabcd-1234を使用
    ->一方向暗号化:1234-$%
    このプロセスをハッシュpassword hashと呼ぶ

    ログインとContext-API


    実際のコードで実装
    app.tsx(グローバルステータスの変更が必要)
    import { Global } from "@emotion/react";
    import {
      ApolloClient,
      ApolloProvider,
      InMemoryCache,
      ApolloLink,
    } from "@apollo/client";
    import "antd/dist/antd.css";
    import { AppProps } from "next/dist/shared/lib/router/router";
    import Layout from "../src/components/commons/layout";
    import { globalStyles } from "../src/commons/styles/globalStyles";
    import { createUploadLink } from "apollo-upload-client";
    
    // Import the functions you need from the SDKs you need
    import { initializeApp } from "firebase/app";
    // TODO: Add SDKs for Firebase products that you want to use
    // https://firebase.google.com/docs/web/setup#available-libraries
    
    
    // Initialize Firebase
    export const firebaseApp = initializeApp(firebaseConfig);
    
    export const GlobalContext = createContext(null);
    
    function MyApp({ Component, pageProps }: AppProps) {
      const [myAccessToken, setMyAccessToken] = useState("");
      const [myuserInfo, setMyUserInfo] = useState({});
      const myValue = {
        accessToken: myAccessToken,
        setMyAccessToken: setMyAccessToken,
        userInfo: myuserInfo,
        setMyUserInfo: setMyUserInfo,
      };
    
      const uploadLink = createUploadLink({
        uri: "백엔드주소",
      });
    
      const client = new ApolloClient({
        link: ApolloLink.from([uploadLink as any]),
        cache: new InMemoryCache(),
      });
    
      return (
        <GlobalContext.Provider value={myValue}>
          <ApolloProvider client={client}>
            <Global styles={globalStyles} />
            <Layout>
              <Component {...pageProps} />
            </Layout>
          </ApolloProvider>
        </GlobalContext.Provider>
      );
    }
    
    export default MyApp;
    
    useContextによるログインページのインポートによる他のログインページの実装
    import { ChangeEvent, useContext, useState } from "react";
    import { GlobalContext } from "../_app";
    import { gql, useMutation } from "@apollo/client";
    import {
      IMutation,
      IMutationLoginUserArgs,
    } from "../../src/commons/types/generated/types";
    import { useRouter } from "next/router";
    
    const LOGIN_USER = gql`
      mutation loginUser($email: String!, $password: String!) {
        loginUser(email: $email, password: $password) {
          accessToken
        }
      }
    `;
    
    export default function loginPage() {
      const router = useRouter();
      const { setAccessToken } = useContext(GlobalContext);
      const [myEmail, setmyEmail] = useState("");
      const [myPassword, setmyPassword] = useState("");
      const [loginUser] = useMutation<
        Pick<IMutation, "loginUser">,
        IMutationLoginUserArgs
      >(LOGIN_USER);
    
      function onChangeMyEmail(event: ChangeEvent<HTMLInputElement>) {
        setmyEmail(event.target.value);
      }
    
      function onChangeMyPassword(event: ChangeEvent<HTMLInputElement>) {
        setmyPassword(event.target.value);
      }
    
      async function onClickLogin() {
        const result = await loginUser({
          variables: {
            email: myEmail,
            password: myPassword,
          },
        });
        setAccessToken(result.data?.loginUser.accessToken); // 여기서 setAccessTocken 필요! (글로벌 스테이트에)
        // 로그인 성공된 페이지로 이동하기
        router.push('/22-02-login-success')
      }
    
      return (
        <>
          <div>
            아이디 : <input type="text" onChange={onChangeMyEmail} />
          </div>
          <div>
            비밀번호 : <input type="password" onChange={onChangeMyPassword} />
          </div>
          <div>
            <button onClick={onClickLogin}>로그인하기!! </button>
          </div>
        </>
      );
    }
    
    ログインが成功したかどうかを示す画面、routerを作成します.プッシュ接続、
    正常な画面の作成
    export default function LoginSuccessPage(){
    
        return(
            <>
            <div>로그인에 성공하였습니다 !</div>
            </>
    
        )
    }
    
    その後、認証が頻繁に行われる場合は、ヘッダを追加する必要があります.
    app.tsxを変更する必要があります.
      const uploadLink = createUploadLink({
        uri: "백엔드 주소",
        headers:{authorization: `Bearer ${myAccessToken}`}
      });
    以降の「ログイン成功」ページでは、ログイン情報をロードし、その名前を表示します.
    import { gql, useQuery } from "@apollo/client";
    import { IQuery } from "../../src/commons/types/generated/types";
    
    const FETCH_USER_LOGGEDF_IN = gql`
      query fetchUserLoggedIn {
        fetchUserLoggedIn {
          email
          name
          picture
        }
      }
    `;
    
    export default function LoginSuccessPage() {
      const { data } = useQuery<Pick<IQuery, "fetchUserLoggedIn">>(
        FETCH_USER_LOGGEDF_IN
      );
      return (
        <>
          <div>로그인에 성공하였습니다 !</div>
          <div>{data?.fetchUserLoggedIn.name}님 환영합니다!!!</div>
        </>
      );
    }