【问题标题】:Handling Sessions with Httponly Cookies in React在 React 中使用 Httponly Cookie 处理会话
【发布时间】:2021-02-21 21:39:25
【问题描述】:

当您从 React 中的 HttpOnly cookie 获取令牌时,处理用户会话的最佳做法是什么?

我的登录端点如下所示,您可以看到令牌设置在 cookie 上:

 @Post('login')
    @HttpCode(HttpStatus.OK)
    async login(@Ip() ipAddress, @Request() req, @Res() res: Response) {
      const auth = await this.basicAuthService.login(req.user, ipAddress);
      const cookieOptions = setTokenCookie();
      res.cookie('token', auth.token, { httpOnly: true });
      res.cookie('refreshToken', auth.refreshToken, { httpOnly: true });
      res.send(auth);
    }

我还有另一个端点解码令牌以获取用户数据

 @Get('user-data')
    async getTokenPayload(@Request() req) {
      if (!('token' in req.cookies)) {
        throw new HttpException('Token was not provided', HttpStatus.NOT_FOUND);
      }

      const { token } = req.cookies;
      return this.basicAuthService.getTokenPayload(token);
    }

在前端,我像这样使用来自 React 的 API 上下文,如您所见,我正在从 /user-data 端点获取数据:

export const UserContext = createContext<UserContextState>(userContextValue);

export const UserProvider:FC<UserProviderProps> = ({ children }) => {
  const [user, setUser] = useState<User>(userInitialValue);

  useEffect(() => {
    const getData = async () => {
      const tokenDecoded = await getUserData();
      setUser(tokenDecoded.user);
    };

    getData();
  }, []);

  return (
    <UserContext.Provider value={{ user, setUser }}>
      { children }
    </UserContext.Provider>
  );
};

一切正常,问题是每次浏览器刷新时都会发出请求,以获取用户数据并将其设置为反应状态。我不确定这是否是一个好习惯,因为有时用户没有经过身份验证,显然/user-data 请求会返回错误。我不想将令牌存储在 localStorage 上或将 HttpOnly 设置为 false。有更好的方法吗?

【问题讨论】:

  • 当你说它正在工作时,cookie 会在你的 React 应用程序中设置并标记为 httpOnly?
  • 否,cookie 在浏览器中设置并标记为 httpOnly。为了从该 cookie 中获取数据,我在后端创建了一个新端点来对其进行解码,cookie 在请求中发送,响应是用户数据,并且我将响应设置为 React 状态。
  • 您是否使用 NodeJS 来制作此服务器端 cookie?
  • 是的,使用nest.js作为BE

标签: reactjs security authentication jwt-auth


【解决方案1】:

据我了解,您有服务器端会话,例如 express-session 我知道并且可以解释,但我相信这个概念与其他人相同。

  • 因此,据我了解,如果当用户登录并进行会话时,该 cookie 将在浏览器中设置,并且只有在满足到期日期的情况下才会被删除,然后该 cookie 将保留在那里.这意味着即使页面重新加载该cookie永远不会去任何地方

  • 所以我非常相信你所说的 cookie 没有在浏览器中设置,或者你可能只是解释错误,因为如果 cookie 被设置并且即使在页面重新加载时还没有过期应该在那里

因此,如果您使用 NodeJS 作为后端,下面是一个实现,说明如何使用 react 应用程序处理 express-session,并在用户登录后在浏览器中设置该 cookie 并将该会话保存在 mongodb 中实例进行会话

首先你需要以下包

npm i express-session connect-mongodb-session 或 yarn add express-session connect-mongodb-session

现在我们有了需要设置 mongoStore 和 express-session 中间件的包:

//Code in server.js/index.js (Depending on your server entry point)
import expressSession from "express-session";
import MongoDBStore from "connect-mongodb-session";
import cors from "cors";
const mongoStore = MongoDBStore(expressSession);

const store = new mongoStore({
  collection: "userSessions",
  uri: process.env.mongoURI,
  expires: 1000,
});
app.use(
  expressSession({
    name: "SESS_NAME",
    secret: "SESS_SECRET",
    store: store,
    saveUninitialized: false,
    resave: false,
    cookie: {
      sameSite: false,
      secure: process.env.NODE_ENV === "production",
      maxAge: 1000,
      httpOnly: true,
    },
  })
);

现在会话中间件已准备就绪,但现在您必须设置 cors 以接受您的 ReactApp,以便传递 cookie 并由服务器将其设置在那里

//Still you index.js/server.js (Server entry point)

app.use(
  cors({
    origin: "http://localhost:3000",
    methods: ["POST", "PUT", "GET", "OPTIONS", "HEAD"],
    credentials: true,
  })
);

现在我们的中间件都设置好了,让我们看看你的登录路径

router.post('/api/login', (req, res)=>{
    //Do all your logic and now below is how you would send down the cooki

    //Note that "user" is the retrieved user when you were validating in logic
    // So now you want to add user info to cookie so to validate in future
    const sessionUser = {
       id: user._id,
       username: user.username,
       email: user.email,
    };
    //Saving the info req session and this will automatically save in your     mongoDB as configured up in sever.js(Server entry point)
    request.session.user = sessionUser;

    //Now we send down the session cookie to client
    response.send(request.session.sessionID);

})

现在我们的服务器已准备就绪,但现在我们必须修复在客户端发出请求的方式,以便此流程可以 100% 工作:

以下代码:React 应用程序/您在处理登录时使用的任何前端

//So you will have all your form logic and validation and below
//You will have a function that will send request to server 

const login = () => {
    const data = new FormData();
    data.append("username", username);
    data.append("password", password);

    axios.post("http://localhost:5000/api/user-login", data, {
      withCredentials: true, // Now this is was the missing piece in the client side 
    });
};

现在有了所有这些,您现在拥有作为 httpOnly 的服务器会话 cookie

【讨论】:

  • 在我的应用程序 cookie 中设置在浏览器中,它们是 HttpOnly,如果我刷新它们仍然存在。我在我的 cookie 中存储的是一个 Jwt 令牌,其中包含我需要的用户信息,以便使用 API 上下文将其设置为我的反应状态。由于我的 cookie 是 HttpOnly 我无法在前端访问它,所以现在我请求解码它并返回用户数据,然后将其设置为反应状态
  • 我认为这不是一个好习惯,所以我想知道在我的反应状态下持久保存用户数据(来自存储在我的 cookie 中作为 HttpOnly 的 jwt)的最佳方法是什么
  • 我认为这是一个重复的,但它没有答案stackoverflow.com/questions/60482566/…
  • 你必须了解 httpOnly cookie 背后的核心概念。这样做是为了避免客户端使用 javaScript 访问它,因为这会引发安全问题,因此当使用 httpOnly cookie 时,所有与会话相关的内容都留给服务器处理。因为如果客户端可以访问该 cookie,那么它就违背了拥有 httpOnly cookie 的目的
  • 是的,我明白了。我只是想知道这是否是最好的解决方案,正如我提到的那样有效。对我来说,每次浏览器在前端刷新时都请求获取用户数据似乎有点奇怪。
猜你喜欢
  • 2020-06-14
  • 2023-03-12
  • 1970-01-01
  • 2012-01-11
  • 1970-01-01
  • 1970-01-01
  • 2012-04-07
  • 2017-09-05
  • 2017-02-04
相关资源
最近更新 更多