如何在 React 和 Redux 中保持用户登录,使用 JWT 表达后端应用程序并且没有像 redux-persist 这样的包?
Posted
技术标签:
【中文标题】如何在 React 和 Redux 中保持用户登录,使用 JWT 表达后端应用程序并且没有像 redux-persist 这样的包?【英文标题】:How to persist user login in a React & Redux, express backend app with JWT and without packages like redux-persist? 【发布时间】:2020-05-07 20:16:54 【问题描述】:所以,这就是我正在做的事情。当用户登录时,JWT 令牌设置在 localStorage 中。我正在尝试保持用户登录。登录后,注销按钮显示在标题上,但如果我刷新,我认为 redux 存储已被清除。我阅读了一些关于如何在 react 中使用 jwt 令牌验证以及 App.js 本身中的 /me
路由或 componentDidMount()
的文章,但我无法理解。任何帮助将不胜感激。
LoginForm.js
import React, Component from "react"
import validator from "validator"
import loginUser from "../actions/index"
import connect from "react-redux"
class LoginForm extends Component
constructor(props)
super(props)
this.state =
email: "",
password: ""
handleChange = (event) =>
const name, value = event.target
this.setState(
[name]: value
)
handleSubmit = (event) =>
event.preventDefault();
const email, password = this.state;
const loginData =
email: this.state.email,
password: this.state.password
if (!email || !password)
return alert('Email and password are must.');
if (password.length < 6)
return alert('Password must contain 6 characters.');
if (!validator.isEmail(email))
return alert('Invalid email.');
this.props.dispatch(loginUser(loginData))
this.props.history.push("/")
render()
const isAuthInProgress = this.props.auth.isAuthInProgress
return (
<div>
<div className="field">
<p className="control has-icons-left has-icons-right">
<input className="input" onChange=this.handleChange name="email" value=this.state.email type="email" placeholder="Email" />
<span className="icon is-small is-left">
<i className="fas fa-envelope"></i>
</span>
<span className="icon is-small is-right">
<i className="fas fa-check"></i>
</span>
</p>
</div>
<div className="field">
<p className="control has-icons-left">
<input className="input" onChange=this.handleChange name="password" value=this.state.password type="password" placeholder="Password" />
<span className="icon is-small is-left">
<i className="fas fa-lock"></i>
</span>
</p>
<p className="has-text-danger">Forgot password?</p>
</div>
<div className="field">
<p className="control">
isAuthInProgress ? <p>Logging in...</p>
:
<button onClick=this.handleSubmit className="button is-success">
Login
</button>
</p>
</div>
</div >
)
const mapStateToProps = (state) =>
return state
export default connect(mapStateToProps)(LoginForm)
actions/index.js
import axios from "axios"
export const registerUser = (registrationData) =>
console.log("inside register action")
return async dispatch =>
dispatch( type: "REGISTRATION_STARTS" )
try
const res = await axios.post("http://localhost:3000/api/v1/users/register", registrationData)
dispatch(
type: "REGISTRATION_SUCCESS",
data: user: res.data ,
)
catch (err)
dispatch(
type: "REGISTRATION_ERROR",
data: error: "Something went wrong"
)
export const loginUser = (loginData) =>
console.log("inside login action")
return async dispatch =>
dispatch( type: "AUTH_STARTS" )
try
const res = await axios.post("http://localhost:3000/api/v1/users/login", loginData)
dispatch(
type: "AUTH_SUCCESS",
data: user: res.data
)
localStorage.setItem("authToken", res.data.token)
catch (err)
dispatch(
type: "AUTH_ERROR",
data: error: "Something went wrong"
)
routes/users.js
router.post("/register", userController.registerUser)
router.post("/login", userController.loginUser)
router.get("/:userId", userController.getUser)
router.get("/list", userController.listUsers)
userController.js
const User = require("../models/User")
const auth = require("../utils/auth")
const validator = require("validator")
module.exports =
registerUser: (req, res, next) =>
const username, email, password = req.body
User.create(req.body, (err, createdUser) =>
if (err)
return next(err)
else if (!username || !email || !password)
return res.status(400).json( message: "Username, email and password are must" )
else if (!validator.isEmail(email))
return res.status(400).json( message: "Invaid email" )
else if (password.length < 6)
return res.status(400).json( message: "Password should be of at least 6 characters" )
else
return res.status(200).json( user: createdUser )
)
,
loginUser: (req, res, next) =>
const email, password = req.body
if (!email || !password)
return res.status(400).json(message: "Email and password are must")
User.findOne( email , (err, user) =>
if (err)
return next(err)
else if (!validator.isEmail(email))
return res.status(400).json( message: "Invalid email" )
else if (!user)
return res.status(402).json( error: "User not found" )
else if (!user.confirmPassword(password))
return res.status(402).json( error: "Incorrect password" )
// generate token here
const token = auth.signToken(email)
res.status(200).json( user, token )
)
,
getUser: (req, res, next) =>
User.findById(req.params.userId, (err, user) =>
if (err)
return next(err)
else if (!user)
return res.status(404).json( message: "User not found" )
else
return res.status(200).json( user: user )
)
,
listUsers: (req, res) =>
User.find(, (err, users) =>
if (err)
return res.status(404).json( error: "No users found" )
else
return res.status(200).json( user: user )
)
utils/auth.js
const jwt = require("jsonwebtoken")
function signToken(payload)
return jwt.sign(payload, process.env.JWTSECRET)
function verifyToken(req, res, next)
const token = req.headers.Authorization || req.headers.authorization || ""
if (!token)
return res.status(403).json( error: "Not authorized")
jwt.verify(token, process.env.JWTSECRET, (err, decoded) =>
if (err)
return res.status(403).json( error: "Not authorized" )
req.user = decoded
next()
)
module.exports = signToken, verifyToken
【问题讨论】:
【参考方案1】:我认为最好的方法是使用 cookie。
在登录路由中设置cookie
res.cookie("authToken", auth.signToken(email),
httpOnly: false,
)
然后在反应端,您可以使用js-cookie
包来检索它并在后续经过身份验证的请求中使用
import Cookies from 'js-cookie';
class SomeComponent extends Component
constructor(props)
super(props)
this.state =
email: "",
password: "",
cookie: Cookies.get('authToken')
【讨论】:
这种方法是不是容易受到 XSS 攻击?以上是关于如何在 React 和 Redux 中保持用户登录,使用 JWT 表达后端应用程序并且没有像 redux-persist 这样的包?的主要内容,如果未能解决你的问题,请参考以下文章
在 react + redux + react-router-redux 中导航到用户登录成功的主页
无法使用react-native app中的redux-persist检查索引/主文件上是否已加载持久状态
在没有用户登录 react.js 和 redux 的情况下将商品添加到购物车