React教程(十六) : 如何实现身份验证 (expressjs)

要解决的问题:
1:JWT存在local storage和cookie里面有什么不同,如何选择?
2:使用ExpressJS作为前端React应用的Web Server。 登录、登出等请求都会由ExpressJS代码处理。

先来看第一个问题,local storage 和 cookie的方案各自优缺点。local storage的缺点是会被脚本注入攻击。 因为JS可以访问local storage中的内容,所以如果攻击者在网站的可编辑部分加入一段JS脚本,就有可能使得运行该页面的用户的身份信息(比如保存在local storage中的JWT)泄漏。

cookie虽然可以通过设置http-only来做到不被JS脚本访问,但cookie同样受到被csrf(跨站请求伪造,cross site request forgery)攻击。
如果后台设置了:Access-Controll-Allow-Origin 不允许其他域的访问,可以解决该问题。 当然,还有其他各种方案,如每次编辑表单时,都从后台请求一个临时token,提交时带上该token。

考虑到React框架会自动对输入做escape,所以用local storage作为选择方案。

再考虑第二个问题:,用ExpressJS发布React应用,参考:
React教程(四) : ExpressJS
https://www.cnblogs.com/Andy1982/p/13922735.html
代码示例:https://github.com/992990831/modernization/tree/main/full-demo

在上述代码的基础上,先执行:npm install node-fetch --save
在server文件夹下新增controller.js, 代码如下:


const fetch = require('node-fetch');

async function login(req, res, next) {
    
    let mockAuthAPI = new Promise((resolve, reject) => {
        setTimeout(() => {
            let userInfo = {
                name: 'Andy',
                age: '38',
                title: '开发'
            };
            resolve(userInfo);
        }, 2000);
    });
    
    
    let accountInfoJson = await mockAuthAPI;
    console.log(accountInfoJson);

    return res.status(200).json(accountInfoJson);
}

async function logout(req, res, next) {
    res.status(200).send();
}

module.exports.login = login;
module.exports.logout = logout;

修改router.js, 加入以下代码

const authController = require('./controller');

appRouter.get('/login', authController.login);
appRouter.get('/logout', authController.logout);

运行node ./server/index.js
访问http://localhost:3006/login
两秒的延迟后,就会返回hardcode的user info。

模拟接口已经ready,接下来在react中调用该接口。
打开src->util->Request.tsx 修改如下:

export const Get_Order_Url = '/order';
export const Sign_In_Url = '/login';

export const Request = axios.create({
    baseURL: 'http://localhost:3006/', //改为express的端口地址。
    responseType: 'json'
});

新建 src->services->AuthService.tsx
添加代码如下:


import { Request, Sign_In_Url } from '../util/Request';

export interface UserInfo{
    id: number;
    note: string;
}

class AuthService {
  async authenticate(userName:string='andy', password:string='123456'): Promise<void> {
    let success = false;

    try {
      const resp = await Request.get(Sign_In_Url);
      
      localStorage.setItem('auth_token', JSON.stringify(resp.data));
      success = true;
    } catch {
      console.error('auth failed');
    }

    return success ? Promise.resolve() : Promise.reject();
  }
}

export const authervice = new AuthService();

src -> components -> account -> Account.tsx 代码:


import { debug } from 'console';
import React, { FC, useState } from 'react';
import styled from "styled-components";
import { authervice, UserInfo } from '../../services/AuthService'

const Layout = styled.div`
  position: absolute;
    left: 0;
    top: 0;
    width: 90vw;
    height: 75vh;
    background-color: #ffeedd;
  margin: 5vw;
  max-width: 100%;
  font-size: 2rem;

  @media (min-width: 1024px) {
    flex-wrap: nowrap;
  }
`;

const SignInButton = styled.a`
  font-size: 2rem;
  color: blue;
  height: 50px;
`;

const SignOutButton = styled.a`
  font-size: 2rem;
  color: red;
  height: 50px;
`;

const ResultArea = styled.div`
  font-size: 2rem;
  color: grey;
  margin-top: 5vh;
  text-align: center;
`

const LoadingArea = styled.div`
  font-size: 1rem;
  color: grey;
  margin-top: 5vh;
  text-align: center;
`

export const Account = () => {
  const [userInfo, setUserInfo] = useState<string | null>();
  const [loading, setLoading] = useState<boolean>(false);

  function login() {
    setLoading(true);

    authervice.authenticate('andy', '123456').then(() => {
      setTimeout(() => {
        let userInfo = localStorage.getItem('auth_token')
        setUserInfo(userInfo);
        setLoading(false);
      }, 500)

    });

  }

  function logout() {
    localStorage.removeItem('auth_token');
    setUserInfo(null);
  }

  return (
    <Layout>
      <div style={{ textAlign: 'center' }}>
        {
          userInfo ?
            <SignOutButton onClick={logout} href='javascript:void(0)'>登出</SignOutButton> :
            <SignInButton onClick={login} href='javascript:void(0)'>登录</SignInButton>
        }
      </div>

      {
        loading && <LoadingArea>登录中......</LoadingArea>
      }
      <ResultArea>
        {userInfo}
      </ResultArea>
    </Layout>
  )
}

由于使用nodejs作为web server,所以运行上述代码的过程是:
npm run build
node ./server/index.js
访问 http://localhost:3006/account

单击登录按钮

posted @ 2020-11-17 22:34  老胡Andy  阅读(405)  评论(0编辑  收藏  举报