尝试使用有效令牌直接访问页面时,JWT 身份验证不起作用;将在登录时开始工作。反应/节点/快递

Posted

技术标签:

【中文标题】尝试使用有效令牌直接访问页面时,JWT 身份验证不起作用;将在登录时开始工作。反应/节点/快递【英文标题】:JWT Authentication not working when trying to access a page directly with valid token; will work when starting at login. React/Node/Express 【发布时间】:2021-01-17 09:36:45 【问题描述】:

我有一个生成 JWT 的登录,然后是一个中间件,用于验证从标头中获取的令牌。令牌还按照建议存储在客户端的 localStorage 中。问题是登录正常工作,“已验证”返回true,但尝试时

    重新加载页面或

    使用有效令牌直接访问仪表板,而不是从登录页面访问。 错误发生在邮递员中的“jwtExpired”或“未授权(授权不是jwt令牌)”(见下文)

当使用邮递员并运行静态文件时,就会出现这种行为;运行“npm start”时不会发生。我将复制邮递员中生成的令牌并将其放在标题中并尝试获取“/dashboard”并给我留下同样的错误。此外,它是 console.error 的“jwtExpired”。我已经检查并重新检查了令牌,它是正确的。在许多 SO 问题和文档之后,我必须在服务器端遗漏一些东西。错误出现在这个中间件文件authorization.js中:

const jwt = require("jsonwebtoken")
require("dotenv").config()

module.exports = async (req, res, next) => 
try 

// step 1 destructure
const jwtToken = req.header("token")

if(!jwtToken)
    return res.status(403).json("Not Authorized (authorization not jwt Token)")
/////////////////This is where the program stops when reloaded or accessed directly 

// step 2 check if the token is valid 
const payload = jwt.verify(jwtToken, process.env.jwtSecret)
// step 3 gives access as req.user
req.user = payload.user
next()

 catch (err) 
    console.error(err.message)
    return res.status(403).json("Not Authorized (authorization catch)")


从我的 server.js 文件开始:

const express= require("express")
const app = express()
const cors = require("cors")
const path = require('path')

// middleware
app.use(express.json())
app.use(cors())

// ROUTES
// register and login
 app.use("/auth", require("./routes/jwtAuth"))
 app.use("/dashboard", require("./routes/dashboard"))

 app.use("/", express.static(path.join(__dirname, 'client/build')))

 app.get("*", (req, res) => 
     res.sendFile(path.join(__dirname, "client/build/index.html"));
   );

 const PORT = process.env.PORT || 5000

 app.listen(PORT, () => 
     console.log(`Server is running on port $PORT`)
 )

使用 jwtAuth.js 登录、注册和/或检查授权(validInfo 仅检查正确的电子邮件地址):

const router = require("express").Router()
const pool = require("../db")
const bcrypt = require("bcrypt")
const jwtGenerator = require("../utils/jwtGenerator")
const validInfo = require("../middleware/validInfo")
const authorization = require("../middleware/authorization")
// registering
router.post("/register", validInfo, async(req, res) =>
try 
    
    // step1 destructure
        const  name, email, password  = req.body
        
    // step2 check if the user exists 
        const user = await pool.query("SELECT * FROM users WHERE user_email=$1", [email])
        if(user.rows.length >0)
            return res.status(401).json("User already exists; email is already registered with the 
database")
        

    // step3 bcrypt the user password for db 
        const saltRound = 10;
        const salt = await bcrypt.genSalt(saltRound)

        const bcryptPassword = await bcrypt.hash(password, salt)

    // step4 insert the info into the db 
        const newUser = await pool.query("INSERT INTO users (user_name, user_email, user_password) 
VALUES ($1, $2, $3) RETURNING *", [name, email, bcryptPassword])
   
    // step5 generate a jwt token 
        const token = jwtGenerator(newUser.rows[0].user_id, newUser.rows[0].user_name)
        res.json( token )

 catch (err) 
    console.error(err.message)
    res.status(500).json("Server Error (register)")

)

// login and logout 
router.post("/login", validInfo, async (req, res) => 
try 
    // step1 deconstruct req.body 
        const  email, password  = req.body
    // step 2 check if user doesnt exist and if not throw and error 
        const user = await pool.query("SELECT * FROM users WHERE user_email=$1", [email])
        if(user.rows.length === 0)
            return res.status(401).json("User email is incorrect or does not exist.")
        
    // step 3 check if incoming pword is the same as db password 
        const validPassword = await bcrypt.compare(password, user.rows[0].user_password)
        if(!validPassword)
            return res.status(401).json("Password is incorrect.")
        
    // step4 give them a jwt token 
    const token = jwtGenerator(user.rows[0].user_id, user.rows[0].user_name)
    res.json( token )
 catch (err) 
    console.log(err.Message)
    res.statusMessage(500).json("Server Error (login)")

)

router.get("/is-verified", authorization, (req, res) => 
try 
    res.json(true)
 catch (error) 
    console.log(err.Message)
    res.statusMessage(500).json("Server Error (is-verified)")

)

module.exports = router;

这里是dashboard.js 数据库和连接:

const router = require("express").Router()
const pool = require("../db")
const authorization = require("../middleware/authorization")

// all connections and name
router.get("/", authorization, async (req, res) => 
try 
  res.json(req.user.name)
    if(req.user.name === 'lead')
      const lead = await pool.query("SELECT * FROM connections LEFT JOIN 
   users ON users.user_id = connections.user_id")                                                                                                      
   ... A bunch of sql queries here that work fine...
      res.json(
        admin: req.user.name,
        results: lead.rows,
        // aggregated queries
        studentsEngaged: studentsEngaged.rows,
        gender:gender.rows,
        distinctStudents: distinctStudents.rows,
        amountSep: amountSep.rows,
        amountOct:amountOct.rows,
        amountNov: amountNov.rows,
        amountDec: amountDec.rows,

        studentSessions:studentSessions.rows,
        homeVisits: homeVisits.rows,
        outsideAgencies: outsideAgencies.rows,
        cpReferrals: cpReferrals.rows,
        amountReferrals:amountReferrals.rows,
        amountDischarges: amountDischarges.rows,
        clas-s-roomPresentations: clas-s-roomPresentations.rows,
        groupSessions: groupSessions.rows,
        checkins: checkins.rows,
        crisisInterventions: crisisInterventions.rows,
        parentContacts: parentContacts.rows,
        meetings : meetings.rows
      )
    else
      const user = await pool.query("SELECT u.user_name, c.connection_id, 
c.contact_type, c.contact_method, c.provision, c.connection_date, 
c.student_id, 
c.purpose, c.gender, c.yearGroup, c.school, c.referral_discharge, 
c.cp_referral 
FROM users AS u LEFT JOIN connections AS c ON u.user_id = c.user_id WHERE 
u.user_id= $1", [req.user.id])                                                                                                      
      res.json(user.rows)
    
 catch (err) 
    console.error(err.message)
    res.status(500).json("Server Error (dashboard catch)")

)

// create connection
router.post("/connections", authorization, async (req, res) => 
try 
  // console.log(req.body);
  const  student_id, contact_type, yearGroup, school, contact_method, 
gender, purpose, provision, connection_date, referral_discharge, cp_referral 
= 
req.body;
  const newConnection = await pool.query(
    "INSERT INTO connections (user_id, student_id, user_name, contact_type, 
yearGroup, school, contact_method, gender, purpose, provision, 
connection_date, 
referral_discharge, cp_referral) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, 
$10, $11, $12, $13) RETURNING *",
    [req.user.id, student_id, req.user.name, contact_type, yearGroup, school, 
contact_method, gender, purpose, provision, connection_date, 
referral_discharge, 
cp_referral]
  );
  res.json(newConnection.rows[0]);
  // console.log(newConnection.rows[0])
 catch (err) 
  console.error(err.message)
 
 );

// update connection 
router.put("/connections/:id", authorization, async (req, res) => 
try 
  const  id  = req.params;
  const  student_id, contact_type, yearGroup, school, contact_method, 
gender, purpose, provision, connection_date, referral_discharge, cp_referral 
 = 
req.body;
  const updateConneciton = await pool.query(
    "UPDATE connections SET student_id=$1, contact_type=$2, yearGroup=$3, 
 school=$4, contact_method=$5, gender=$6, purpose=$7, provision=$8, 
connection_date=$9, referral_discharge=$10, cp_referral=$11 WHERE 
connection_id = 
$12 AND user_id = $13 RETURNING *",
    [student_id, contact_type, yearGroup, school, contact_method, gender, 
purpose, provision, connection_date, referral_discharge, cp_referral, id, 
req.user.id]
  );     

  if (updateConneciton.rows.length === 0) 
    return res.json("This connection is not yours");
  

  res.json("Connection was updated");
 catch (err) 
  console.error(err.message);

 );

// delete connection
router.delete("/connections/:id", authorization, async (req, res) => 
try 
  const  id  = req.params;
  const deleteConnection = await pool.query(
    "DELETE FROM connections WHERE connection_id = $1 AND user_id = $2 
RETURNING *",
    [id, req.user.id]
  );

  if (deleteConnection.rows.length === 0) 
    return res.json("This connection is not yours");
  

  res.json("Connection was deleted");
 catch (err) 
  console.error(err.message);

 ) ;

这是我的 App.js:

  function App() 
  const [isAuthenticated, setIsAuthenticated] = useState(false)

  const setAuth = (boolean) => 
    setIsAuthenticated(boolean)
  

  async function isAuth()
    try 
      const response = await fetch("/auth/is-verified", 
        method:"GET", 
        headers:token: localStorage.getItem("token") 
      )
      var parseRes = await response.json()

  // if(parseRes === true || parseRes === undefined)
  //     setIsAuthenticated(true)
  // else
  //   setIsAuthenticated(false)
  // 
////////this didn't work, just checking which would occur first, error or 
this conditional 
      parseRes === true ? setIsAuthenticated(true): setIsAuthenticated(false)

     catch (err) 
      console.error(`(App Catch)$err.message`)
    
  

  useEffect(() => 
    isAuth()
  )

  return (
    <Fragment>
      <Router>
        <div>
          <Switch>
            <Route exact path="/" render=props => !isAuthenticated ? <Root 
    ...props /> : <Redirect to='/dashboard'/> />
            <Route exact path="/landing" render=props => !isAuthenticated ? 
    <Landing ...props /> : <Redirect to='/dashboard'/> />
            <Route exact path="/register" render=props => !isAuthenticated ? 
    <Register ...props setAuth =setAuth />  : <Redirect to='/login'/> />
        <Route exact path="/login" render=props => !isAuthenticated ? <Login 
    ...props setAuth =setAuth auth=isAuthenticated/> : <Redirect 
    to='/dashboard'/> />
        <Route exact path="/dashboard" render=props => isAuthenticated ? 
    <Dashboard ...props setAuth =setAuth /> : <Redirect to='/login'/> />
      </Switch>
    </div>
  </Router>
</Fragment>

  );


export default App;


enter code here

【问题讨论】:

您是否检查过您的令牌是如何从此文件(“../utils/jwtGenerator”)中签名的?令牌可能只在很短的时间内有效 是的,考虑到“jwt expired”我做了,但是如果我在一天后输入用于登录的 url,我有一个基于身份验证状态的三元运算符,可以适当地重定向到仪表板。我将在问题的底部包含我的 App.js。 【参考方案1】:

在授权.js 中,代码被破坏,我消除了返回和状态消息并使用“res.redirect(“/”);”这会导致程序在每种情况下重定向 1. 令牌无效 2. 标头中没有提供令牌。重定向将用户带回登录页面,在该页面检查 localStorage,如果存在有效令牌,则将重定向到仪表板。最终,如果获得授权,我不会返回状态消息,而是重定向回仪表板。

【讨论】:

以上是关于尝试使用有效令牌直接访问页面时,JWT 身份验证不起作用;将在登录时开始工作。反应/节点/快递的主要内容,如果未能解决你的问题,请参考以下文章

Auth0 - 在 Owin 上使用带有承载访问令牌的 JWT 使用 RS256 进行身份验证

验证 JWT 令牌签名

Laravel Tymon\JWT Auth:在身份验证之前检查未完成的有效令牌?

尝试访问我的 api 平台时未找到 JWT 令牌 401 错误?

使用有效的 JWT 令牌调用经过 keycloak 身份验证的 API

使用 passport-jwt 进行 JWT 身份验证不考虑到期日期