<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          如何在React應用中實現(xiàn)“使用GitHub登錄”

          共 11597字,需瀏覽 24分鐘

           ·

          2020-12-21 04:24

          我遇到一個場景,該場景需要使用Create-React-App在React 應用中實現(xiàn)“使用Github登錄”功能。雖然這聽起來很簡單,但在嘗試做這件事時,你可能會遇到一些麻煩。因此,本文的目的是提供一個指南,幫助你在你的應用程序中實現(xiàn)這樣的功能。讓我們現(xiàn)在就開始吧!

          步驟1:在Github上創(chuàng)建OAuth應用

          按照此處[1]提供的步驟登錄到你的Github帳戶并創(chuàng)建OAuth應用。注意:對于本例,在創(chuàng)建OAuth應用時,如果你在本地運行應用,可以將主頁URL設(shè)置為http://localhost:3000/,將授權(quán)回調(diào)URL設(shè)置為http://localhost:3000/login。在根目錄下創(chuàng)建一個 .env 文件,并設(shè)置這些變量:
          REACT_APP_CLIENT_ID=你的Client ID
          REACT_APP_CLIENT_SECRET=你的Client Secret
          REACT_APP_REDIRECT_URI=http://localhost:3000/login

          步驟2:創(chuàng)建React應用

          繼續(xù)使用你的首選來創(chuàng)建你的react應用程序,在這個例子中,我們將使用Create-React-App。如果你使用這種模式,你必須刪除index.css、App.css、App.test.js和serviceWorker.js等文件。編輯index.js,確保它看起來像這樣:
          import React from 'react';
          import ReactDOM from 'react-dom';
          import App from './App';

          ReactDOM.render(<App />, document.getElementById('root'));

          另外,編輯App.js,確保它看起來像這樣:

          import React, { createContext, useReducer } from 'react';
          import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
          import Home from "./components/Home";
          import Login from "./components/Login";
          import { initialState, reducer } from "./store/reducer";


          export const AuthContext = createContext();

          function App() {
          const [state, dispatch] = useReducer(reducer, initialState);

          return (
          <AuthContext.Provider
          value={{
          state,
          dispatch
          }}
          >

          <Router>
          <Switch>
          <Route path="/login" component={Login}/>
          <Route path="/" component={Home}/>
          Switch>

          Router>
          AuthContext.Provider>
          );
          }

          export default App;
          在App.js文件中,導入2個組件(Home.jsLogin.js )。要創(chuàng)建這2個組件,進入src文件夾,創(chuàng)建一個名為component的文件夾,里面有2個文件(Home.js和Login.js)。在根文件夾中,你可以在下面運行此命令來創(chuàng)建它們。
          mkdir -p src/components && cd src/components && touch Home.js Login.js
          接下來,你會觀察到,我們從store導入了狀態(tài)和reducer,繼續(xù)并設(shè)置一個簡單的store,它將保持應用程序狀態(tài)。要做到這一點,導航到src文件夾中,并創(chuàng)建一個名為store的文件夾,在它里面創(chuàng)建一個名為reducer的子文件夾,并在reducer文件夾里面創(chuàng)建一個index.js文件。在根目錄下,你可以運行下面這個命令來創(chuàng)建它們。
          mkdir -p src/store/reducer && cd src/store/reducer && touch index.js

          Store中index.js文件的內(nèi)容應如下所示。

          export const initialState = {
          isLoggedIn: JSON.parse(localStorage.getItem("isLoggedIn")) || false,
          user: JSON.parse(localStorage.getItem("user")) || null,
          client_id: process.env.REACT_APP_CLIENT_ID,
          redirect_uri: process.env.REACT_APP_REDIRECT_URI,
          client_secret: process.env.REACT_APP_CLIENT_SECRET,
          proxy_url: process.env.REACT_APP_PROXY_URL
          };

          export const reducer = (state, action) => {
          switch (action.type) {
          case "LOGIN": {
          localStorage.setItem("isLoggedIn", JSON.stringify(action.payload.isLoggedIn))
          localStorage.setItem("user", JSON.stringify(action.payload.user))
          console.log(action.payload.isLoggedIn)
          return {
          ...state,
          isLoggedIn: action.payload.isLoggedIn,
          user: action.payload.user
          };
          }
          case "LOGOUT": {
          localStorage.clear()
          return {
          ...state,
          isLoggedIn: false,
          user: null
          };
          }
          default:
          return state;
          }
          };
          它包含InitialState對象和一個reducer函數(shù),該函數(shù)包含派發(fā)的動作以突變狀態(tài)。
          這時,我們就可以在我們的組件上下功夫了。讓我們在Login.js上工作,這將是一個簡單的組件,它有一個按鈕,可以觸發(fā)Github API的登錄請求。
          import React, { useState, useEffect, useContext } from "react";
          import { Redirect } from "react-router-dom";
          import Styled from "styled-components";
          import GithubIcon from "mdi-react/GithubIcon";
          import { AuthContext } from "../App";


          export default function Login() {
          const { state, dispatch } = useContext(AuthContext);
          const [data, setData] = useState({ errorMessage: "", isLoading: false });

          const { client_id, redirect_uri } = state;

          useEffect(() => {
          // After requesting Github access, Github redirects back to your app with a code parameter
          const url = window.location.href;
          const hasCode = url.includes("?code=");

          // If Github API returns the code parameter
          if (hasCode) {
          const newUrl = url.split("?code=");
          window.history.pushState({}, null, newUrl[0]);
          setData({ ...data, isLoading: true });

          const requestData = {
          client_id: state.client_id,
          redirect_uri: state.redirect_uri,
          client_secret: state.client_secret,
          code: newUrl[1]
          };

          const proxy_url = state.proxy_url;

          // Use code parameter and other parameters to make POST request to proxy_server
          fetch(proxy_url, {
          method: "POST",
          body: JSON.stringify(requestData)
          })
          .then(response => response.json())
          .then(data => {
          dispatch({
          type: "LOGIN",
          payload: { user: data, isLoggedIn: true }
          });
          })
          .catch(error => {
          setData({
          isLoading: false,
          errorMessage: "Sorry! Login failed"
          });
          });
          }
          }, [state, dispatch, data]);

          if (state.isLoggedIn) {
          return <Redirect to="/" />;
          }

          return (
          <Wrapper>
          <section className="container">
          <div>
          <h1>Welcomeh1>

          <span>Super amazing appspan>
          <span>{data.errorMessage}span>
          <div className="login-container">
          {data.isLoading ? (
          <div className="loader-container">
          <div className="loader">div>
          div>
          ) : (
          <>
          {
          // Link to request GitHub access
          }
          <a
          className="login-link"
          href={`https://github.com/login/oauth/authorize?scope=user&client_id=${client_id}&redirect_uri=${redirect_uri}`}
          onClick={() =>
          {
          setData({ ...data, errorMessage: "" });
          }}
          >
          <GithubIcon />
          <span>Login with GitHubspan>
          a>

          )}
          div>
          div>
          section>
          </Wrapper>
          );
          }

          const Wrapper = Styled.section`
          .container {
          display: flex;
          justify-content: center;
          align-items: center;
          height: 100vh;
          font-family: Arial;

          > div:nth-child(1) {
          display: flex;
          flex-direction: column;
          justify-content: center;
          align-items: center;
          box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.2);
          transition: 0.3s;
          width: 25%;
          height: 45%;
          > h1 {
          font-size: 2rem;
          margin-bottom: 20px;
          }
          > span:nth-child(2) {
          font-size: 1.1rem;
          color: #808080;
          margin-bottom: 70px;
          }
          > span:nth-child(3) {
          margin: 10px 0 20px;
          color: red;
          }
          .login-container {
          background-color: #000;
          width: 70%;
          border-radius: 3px;
          color: #fff;
          display: flex;
          align-items: center;
          justify-content: center;
          > .login-link {
          text-decoration: none;
          color: #fff;
          text-transform: uppercase;
          cursor: default;
          display: flex;
          align-items: center;
          height: 40px;
          > span:nth-child(2) {
          margin-left: 5px;
          }
          }
          .loader-container {
          display: flex;
          justify-content: center;
          align-items: center;
          height: 40px;
          }
          .loader {
          border: 4px solid #f3f3f3;
          border-top: 4px solid #3498db;
          border-radius: 50%;
          width: 12px;
          height: 12px;
          animation: spin 2s linear infinite;
          }
          @keyframes spin {
          0% {
          transform: rotate(0deg);
          }
          100% {
          transform: rotate(360deg);
          }
          }
          }
          }
          }
          `;
          Login.js組件內(nèi)部,請注意以下重要事項:
          1. 我們導入并利用AuthContext使Store中的全局狀態(tài)和操作可在此組件中使用。
          2. 當用戶點擊“用Github登錄”按鈕時,會向Github API提出請求,對我們的應用進行授權(quán)。如果成功的話,Github就會重定向回我們的應用(授權(quán)回調(diào)URL),并在URL中加入“code”。
          3. 我們利用useEffect hook偵聽此“code”何時可用。然后我們從url中收集它,使用code和其他數(shù)據(jù),如:client_id,redirect_uri,client_secret,繼續(xù)通過我們的proxy server[2](代理服務(wù)器)向Github APIs發(fā)出請求(一個簡單的快遞應用,幫助我們繞過CORS錯誤)。下一步,我將詳細討論代理服務(wù)器。
          4. 如果通過代理服務(wù)器的認證返回有效的響應,我們就會調(diào)度“LOGIN”事件,在我們的存儲中設(shè)置用戶數(shù)據(jù)和isLoggedIn有效載荷。
          讓我們更新Home.js組件以顯示一些用戶數(shù)據(jù),例如(頭像,姓名,關(guān)注者人數(shù)等)
          import React, { useContext } from "react";
          import { Redirect } from "react-router-dom";
          import Styled from "styled-components";
          import { AuthContext } from "../App";


          export default function Home() {
          const { state, dispatch } = useContext(AuthContext);

          if (!state.isLoggedIn) {
          return <Redirect to="/login" />;
          }

          const { avatar_url, name, public_repos, followers, following } = state.user

          const handleLogout = () => {
          dispatch({
          type: "LOGOUT"
          });
          }

          return (
          <Wrapper>
          <div className="container">
          <button onClick={()=> handleLogout()}>Logoutbutton>

          <div>
          <div className="content">
          <img src={avatar_url} alt="Avatar"/>
          <span>{name}span>
          <span>{public_repos} Reposspan>
          <span>{followers} Followersspan>
          <span>{following} Followingspan>
          div>
          div>
          div>
          Wrapper>
          );
          }

          const Wrapper = Styled.section`
          .container{
          display: flex;
          flex-direction: column;
          height: 100vh;
          font-family: Arial;
          button{
          all: unset;
          width: 100px;
          height: 35px;
          margin: 10px 10px 0 0;
          align-self: flex-end;
          background-color: #0041C2;
          color: #fff;
          text-align: center;
          border-radius: 3px;
          border: 1px solid #0041C2;
          &:hover{
          background-color: #fff;
          color: #0041C2;
          }
          }
          >div{
          height: 100%;
          width: 100%;
          display: flex;
          font-size: 18px;
          justify-content: center;
          align-items: center;
          .content{
          display: flex;
          flex-direction: column;
          padding: 20px 100px;
          box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.2);
          width: auto;

          img{
          height: 150px;
          width: 150px;
          border-radius: 50%;
          }

          >span:nth-child(2){
          margin-top: 20px;
          font-weight: bold;
          }

          >span:not(:nth-child(2)){
          margin-top: 8px;
          font-size: 14px;
          }

          }
          }
          }
          `
          ;

          步驟3:創(chuàng)建代理服務(wù)器

          最后一步是創(chuàng)建代理服務(wù)器,以幫助我們繞過CORS錯誤。它將是一個簡單的express應用程序,我們將在header中啟用“Access-Control-Allow-Origin”。我們將使用它來轉(zhuǎn)發(fā)請求和接收來自Github API的響應,并將所需的響應發(fā)送回客戶端(我們的React應用程序)。將這些變量添加到**.env**文件中:
          REACT_APP_PROXY_URL=http://localhost:5000/authenticate
          SERVER_PORT=5000
          在根文件夾中,創(chuàng)建一個名為server的文件夾,并在其中創(chuàng)建一個index.js文件。
          const express = require("express");
          const bodyParser = require("body-parser");
          const FormData = require("form-data");
          const fetch = require("node-fetch");
          const { client_id, redirect_uri, client_secret } = require("./config");

          const config = require("./config");

          const app = express();

          app.use(bodyParser.json());
          app.use(bodyParser.json({ type: "text/*" }));
          app.use(bodyParser.urlencoded({ extended: false }));

          // Enabled Access-Control-Allow-Origin", "*" in the header so as to by-pass the CORS error.
          app.use((req, res, next) => {
          res.header("Access-Control-Allow-Origin", "*");
          next();
          });

          app.post("/authenticate", (req, res) => {
          const { code } = req.body;

          const data = new FormData();
          data.append("client_id", client_id);
          data.append("client_secret", client_secret);
          data.append("code", code);
          data.append("redirect_uri", redirect_uri);

          // Request to exchange code for an access token
          fetch(`https://github.com/login/oauth/access_token`, {
          method: "POST",
          body: data,
          })
          .then((response) => response.text())
          .then((paramsString) => {
          let params = new URLSearchParams(paramsString);
          const access_token = params.get("access_token");

          // Request to return data of a user that has been authenticated
          return fetch(`https://api.github.com/user`, {
          headers: {
          Authorization: `token ${access_token}`,
          },
          });
          })
          .then((response) => response.json())
          .then((response) => {
          return res.status(200).json(response);
          })
          .catch((error) => {
          return res.status(400).json(error);
          });
          });

          const PORT = process.env.SERVER_PORT || 5000;
          app.listen(PORT, () => console.log(`Listening on ${PORT}`));

          源碼

          如果你按照上面列出的幾個步驟進行操作,則可以在應用程序中無縫集成“使用Github登錄”功能。

          來源:https://levelup.gitconnected.com/how-to-implement-login-with-github-in-a-react-app-bd3d704c64fc
          作者:Princewill Iroka

          粉絲福利

          146期留言+在看幸運用戶:暫無

          臨走前留下,今天的福利

          • 福利1:《教你玩轉(zhuǎn)手機攝影,隨手拍出好照片》獲取資源請在公眾號對話框中回復關(guān)鍵字:FL04,如果沒有關(guān)注請掃下面的二維碼
          • 福利2:在看+留言我隨機抽取一位認真留言的小伙伴,給他發(fā)一個紅包獎勵

          最近文章

          -?END -

          點贊 + 在看 + 留言,下一個幸運兒就是你!
          走心的分享更容易被抽中~

          開獎時間?下期文末

          參考資料

          [1]

          此處: https://developer.github.com/apps/building-oauth-apps/creating-an-oauth-app/

          [2]

          proxy server: https://github.com/PrincewillIroka/login-with-github/blob/master/server/index.js


          瀏覽 35
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  摸逼网站 | 怡春院在线 | 躁逼网站 | 亚洲婷婷国产 | 国产日本欧美在线 |