Passport faillureRedirect 没有重定向,而是破坏了我的 React 应用程序

Passport faillureRedirect not redirecting, but instead is breaking my React app

我决定在我的 React 应用程序上使用 Passport(本地策略)进行身份验证(第一次尝试)。我让它工作到用户可以创建一个新帐户,然后登录然后它被重定向到受保护页面的地步。到目前为止,它看起来不错,我也可以毫无问题地注销。但是,当我尝试在不输入正确凭据的情况下访问受保护的路由时,或者当手动转到受保护的路由时,整个应用程序中断并且我收到一个 React Error: TypeError: clients.map is not a function,它指的是到我在受保护路由中使用的代码,以从数据库中获取和显示数据。

对我来说,似乎 React 试图在没有数据库数据的情况下先渲染组件,因此出现错误?因为如果我删除身份验证部分,组件就可以正常呈现。但同时,我是Passport新手,不知道是不是和passport配置本身有关。

Users.js api 路线

const express = require('express');
const router = express.Router();
const passport = require('passport');
const bcrypt = require('bcryptjs');
const User = require('../models').User;
const salt = bcrypt.genSaltSync(10);


// Register
router.post('/signup', (req, res) => {
console.log(req.body);

    const { username, email, password, password2 } = req.body;

    if (!username || !email || !password || !password2) {
        throw 'Please enter all fields';
        }
    if (password != password2) {
        throw 'Passwords do not match'
    };
    if (password.length < 6) {
        throw 'Password must be at least 6 characters';
    }
    else     {
        User.findOne({
            where: {
                email
            }
        }).then(user => {
            if (user) {
                res.send("Email already exists!")
            } else {
                const encryptedPassword = bcrypt.hashSync(password, salt);

                let newUser = {
                    username,
                    email,
                    password: encryptedPassword
                };
                User.create(newUser)
                    .then(() => {
                        delete newUser.password;
                        res.send(newUser)
                    })
                    .catch(function (err) {
                        console.log(err);
                         res.json(err);
                    });
            }
        });
    }

});

// Login
router.post('/login', (req, res, next) => {
    const { email, password } = req.body;

    if (!email || !password) {
        throw 'Please enter all fields';
    }
    if (email === null || password === null) {
        throw 'Please enter the right credentials';
    }

    passport.authenticate('local', {
        successRedirect: '/admin',
        failureRedirect: '/auth/login',
        failureFlash: true
    })(req, res, next);
});

// Logout
router.get('/logout', function (req, res) {
    req.logOut();
    req.session.destroy(function (err) {
        res.redirect('/auth/login'); 
    });
});

module.exports = router;

Passport.js 护照配置

const LocalStrategy = require('passport-local').Strategy;
const bcrypt = require('bcryptjs');
const User = require('../models').User;


module.exports = function(passport) {

passport.use(new LocalStrategy(
   // Our user will sign in using an email, rather than a "username"
    {
        usernameField: "email"
    },
  function (email, password, done) {
        // When a user tries to sign in this code runs
        User.findOne({
            where: {
                email: email
            }
        })
            .then(user => {
                if (!user) {
                    return done(null, false, { message: 'No user found 
under those credentials' });
                }
            bcrypt.compare(password, user.password, (err, isMatch) => {
                // if (err) throw err;
                if (err) {
                    return done(err, null)
                }
                if (isMatch) {
                    return done(null, user);
                } else {
                    return done(null, { message: 'Email or Password not 
valid' });
                }
            });

        })
    }
));

passport.serializeUser(function (user, done) {
    done(null, user.id);
});

passport.deserializeUser(function (id, done) {
    User.findOne({
        where: {
            id: id
        }
    }).then(function (user) {
        if (user) {
            done(null, user.get());
        } else {
            done(user.errors, null);
        }
    }); 

});


};

最后,我的反应组件在授权时正确呈现,但在没有授权时中断:

import React, { Component } from 'react';
import API from '../../utils/API';
import { Link } from 'react-router-dom';
import {
    Button,
    Modal,
    ModalHeader,
    ModalBody,
    Form,
    FormGroup,
    Input
} from 'reactstrap';

import './style.css';
import Axios from 'axios';


class AdminComp extends Component {
    state = {
        clients: [],
        lastName: '',
        firstName: '',
        phone: '',
        petName: '',
        breed: '',
        notes: '',
        modal: false,
        clientSearch: ''
    }

    componentDidMount() {
        this.getAllClients()
    }

    getAllClients = () => {
        API.getClients()
                .then(res => {
                if (res.data.status === "error") {
                    throw new Error(res.data.message);
                }
                this.setState({ clients: res.data })
            }
            )
            .catch(err => console.log(err));
    };

    //Logout User
    handleLogOut(e) {
        e.preventDefault();

        Axios.get("/auth/logout")
            .then(response => {
                if (response.data.status === "error") {
                    throw new Error(response.data.message);
                }
                window.location.href = "/auth/login"
                console.log("logged out", response.data)
            })
            .catch(err => {
                  window.location.href = "/auth/login"
                console.log(err)
            })
    }

//Modal Functions
    toggle = () => {
        this.setState({
            modal: !this.state.modal
        });
    }

    onSubmitModal = e => {

        e.preventDefault();
        if (!this.state.clientSearch || isNaN(this.state.clientSearch)
        ) {
            return;
        }

        this.getSingleClient();
        this.toggle();
    }

    onChangeModal = (e) => {
        this.setState({ [e.target.name]: e.target.value });
    };

    getSingleClient = () => {
        let clientSearchValue = this.state.clientSearch;

        API.getClient(clientSearchValue)
            .then(res => {
                if (res.data) {
                    this.setState({
                        clientSearch: res.data
                    }, () => console.log(this.state.clientSearch))
                } else {
                    this.setState({
                        modal: false
                    })
                    alert("Client ID number does not exist, please try again")
                }
            })
            .catch(error => console.log(error))
    }

    handleChange = (e) => {
        this.setState({
            [e.target.id]: e.target.value
        })
    }

    handleDeleteClient = id => {
        API.deleteClient(id)
            .then(alert("Client with Id number: " + id + " has been 
successfully deleted!"))
            .then(res => this.getAllClients())
            .catch(err => console.log(err));
    }


    handleFormSubmit = (e) => {
        e.preventDefault();
        if (!this.state.lastName ||
            !this.state.firstName ||
            !this.state.phone ||
            !this.state.petName ||
            !this.state.breed ||
            !this.state.notes) {
            return;
        }
        API.addClient({
            lastName: this.state.lastName.toLowerCase(),
            firstName: this.state.firstName.toLowerCase(),
            phone: this.state.phone.toLowerCase(),
            petName: this.state.petName.toLowerCase(),
            breed: this.state.breed.toLowerCase(),
            notes: this.state.notes.toLowerCase()
        })
            .then(alert("New Client added to list!"))
            .then(this.setState({ petName: "", breed: "", notes: "", lastName: "", firstName: "", phone: "" }))
            .then(res => this.getAllClients())
            .catch(err => console.log(err));
    };


    render() {



        const clients = this.state.clients;

        const clientsList = clients.length ? (
            clients.map(client => {
                return (
                    <div key={client.id}>
                        <div className="card-content">
                            <table style={{ width: "100%", tableLayout: "fixed", border: "1px", background: "white" }}>
                                <tbody>
                                    <tr>
                                        <td style={{ width: "50px", textAlign: "center" }}>{client.id}</td>
                                        <td style={{ width: "100px", textAlign: "center" }}>{client.lastName}</td>
                                        <td style={{ width: "100px", textAlign: "center" }}>{client.firstName}</td>
                                        <td style={{ width: "100px", textAlign: "center" }}>{client.phone}</td>
                                        <td style={{ width: "160px", textAlign: "center" }}>{client.petName}</td>
                                        <td style={{ width: "120px", textAlign: "center" }}>{client.breed}</td>
                                        <td style={{ width: "367px", textAlign: "center" }}>{client.notes}</td>
                                        <td><Link style={{ width: "70px", border: "1px solid white" }} className="btn btn-info" to={'api/clients/' + client.id}>Edit
                                    </Link>

                                            <button style={{ background: "red", color: "white", width: "70px" }} className="btn btn-warning" onClick={(e) => { if (window.confirm(`Are you sure you wish to delete ${client.firstName} ${client.lastName} permanently?`)) this.handleDeleteClient(client.id) }}>
                                            Delete
                                </button>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                </div>
            )
        })
    ) : (
            <div >No clients in database</div>
        );

    return (
        <div className="container">
            <div className="row">
                <div className="col-md-12">

                    <hr style={{ background: "white" }}></hr>
                    <h1 style={{ textAlign: 'center' }}><b>Welcome to the Admin Panel Paola</b></h1>

                    <button onClick={this.handleLogOut}>Logout</button>

                    <Link to={"/auth/signup"}><button>Add an employee</button></Link>

                    <hr style={{ background: "white" }}></hr>
                </div>
            </div>
            <div className="row">
                <div className="col-md-4 bg-dark" style={{
                    color: 'white',
                    marginBottom: '30px',
                    padding: '15px',
                    textAlign: 'center',
                    border: '1px solid white'
                }}>
                    <h3>Search for a Client</h3>
                    <Form onSubmit={this.onSubmitModal}>
                        <FormGroup>
                            <Input
                                type="text"
                                name="clientSearch"
                                id="clientSearch"
                                placeholder="Enter Client ID Number"
                                onChange={this.onChangeModal}
                            ></Input>
                            <Button
                                color="info"
                                style={{ marginTop: '1rem' }}
                                block>
                                Submit
                        </Button>
                        </FormGroup>
                    </Form>

                    <Modal
                        isOpen={this.state.modal}
                        toggle={this.toggle}>
                        <ModalHeader toggle={this.toggle}>These Records were found</ModalHeader>
                        <ModalBody>
                            <table>
                                <tbody>
                                    <tr style={{ textAlign: 'center', padding: '7px' }}>
                                        <th>ID</th>
                                        <th>Last Name</th>
                                        <th>First Name</th>
                                        <th>Phone</th>
                                        <th>Pet Name</th>
                                        <th>Breed</th>
                                        <th>Notes</th>
                                    </tr>
                                    <tr style={{ textAlign: 'center', padding: '7px' }}>
                                        <td>{this.state.clientSearch.id}</td>
                                        <td>{this.state.clientSearch.lastName}</td>
                                        <td>{this.state.clientSearch.firstName}</td>
                                        <td>{this.state.clientSearch.phone}</td>
                                        <td>{this.state.clientSearch.petName}</td>
                                        <td>{this.state.clientSearch.breed}</td>
                                        <td>{this.state.clientSearch.notes}</td>
                                        <td><Link style={{ width: "60px", border: "1px solid white" }} className="btn btn-info" to={'api/clients/' + this.state.clientSearch.id}>Edit
                                </Link>
                                        </td>
                                    </tr>
                                </tbody>
                            </table>
                        </ModalBody>
                    </Modal>
                </div>

                <div className="col-md-8 bg-dark" style={{ border: '1px solid white', color: 'white', marginBottom: "30px" }}>
                    <form className="white" onSubmit={this.handleFormSubmit.bind(this)} style={{ marginBottom: "50px" }}>
                        <h2 className="grey-text text-darken-3" style={{ textAlign: "center", marginTop: "15px" }}>Add a New Client</h2>
                        <p>* Fields required</p>
                        <hr style={{ background: "white" }}></hr>
                        <div className="input-field">
                            <label htmlFor="lastName">* Last Name</label>
                            <input type="text" id='lastName' value={this.state.lastName} onChange={this.handleChange} />
                        </div>
                        <div className="input-field">
                            <label htmlFor="firstName">* First Name</label>
                            <input type="text" id='firstName' value={this.state.firstName} onChange={this.handleChange} />
                        </div>
                        <div className="input-field">
                            <label htmlFor="phone">* Phone</label>
                            <input type="text" id='phone' value={this.state.phone} onChange={this.handleChange} />
                        </div>
                        <div className="input-field">
                            <label htmlFor="petName">* Pet Name</label>
                            <input type="text" id='petName' value={this.state.petName} onChange={this.handleChange} />
                        </div>
                        <div className="input-field">
                            <label htmlFor="breed">* Breed</label>
                            <input type="text" id='breed' value={this.state.breed} onChange={this.handleChange} />
                        </div>
                        <div className="input-field">
                            <label htmlFor="notes">Notes</label>
                            <input type="text" id='notes' value={this.state.notes} onChange={this.handleChange} />
                        </div>
                        <div className="input-field">
                            <button className="btn-primary lighten-1 z-depth-0" onClick={this.handleFormSubmit}
                            >Add Client</button>
                        </div>
                    </form>
                </div>
                <div className="row">

                    <div className="col-md-12">
                        <div style={{ background: "white", paddingTop: "12px", marginBottom: "100px" }}>
                            <h6><b>
                                <span style={{ border: "1px solid", padding: "8px 17px 8px 17px" }}>Id</span>
                                <span style={{ border: "1px solid", padding: "8px 11px 8px 12px" }}>Last Name</span>
                                <span style={{ border: "1px solid", padding: "8px 8px 8px 8px" }}> First Name</span>
                                <span style={{ border: "1px solid", padding: "8px 27px 8px 25px" }}>Phone</span>
                                <span style={{ border: "1px solid", padding: "8px 44px 8px 44px" }}>Pet Name</span>
                                <span style={{ border: "1px solid", padding: "8px 37px 8px 38px" }}>Breed</span>
                                <span style={{ border: "1px solid", padding: "8px 200px 8px 200px", background: "white" }}>Notes / Actions</span>
                            </b></h6>
                            {clientsList}
                        </div>
                    </div>
                </div>

            </div>
        </div >
       )
    }
}

export default AdminComp;

这也是我保护路由的方式:

// Requiring our models
var db = require("../models");
// const passport = require('../passport');
const { ensureAuthenticated } = require('../passport/auth');


module.exports = function (app) {

  //Authentication Routes
  app.get("/auth/signup", ensureAuthenticated, (req, res) => {
    res.send("Passed!")
  })

  app.get("/admin", ensureAuthenticated, (req, res) => {
    res.send("Admin Passed!")
  })
  //Client routes
  app.get("/api/clients", ensureAuthenticated, (req, res) => {
    db.Client.findAll({}).then(function (dbClient) {
      res.json(dbClient);
    });
  });
}:

Auth.js 文件:

  module.exports = {
  ensureAuthenticated: function(req, res, next) {
    if (req.isAuthenticated()) {
      return next();
    }
    req.flash('error_msg', 'Please log in to view that resource');
    res.redirect('/auth/login');
  },
  forwardAuthenticated: function(req, res, next) {
    if (!req.isAuthenticated()) {
      return next();
    }
    res.redirect('/admin');      
  }
};

非常感谢

我对 /login 路由做了一些调整以使其工作:

// 登录 router.post("/login", 函数 (req, res, next) {

// generate the authenticate method and pass the req/res
passport.authenticate('local', function (err, user, info) {
    if (err) {
        return res.status(401).json(err);
    }
    if (!user) { 
        return res.status(401).json(info); 
    }

    req.logIn(user, function (err) {
        if (err) { return next(err); }
        return res.send(user);
    });

})(req, res, next);

});