因此,我正在努力遵循安全最佳实践,并通过React应用程序在一个仅安全的http cookie中发送我的JWT令牌。
这对请求很有效,但我发现这种方法的主要问题是,如果我不能检查令牌是否存在,我如何判断用户是否在客户端登录?我能想到的唯一方法是制作一个简单的http到一个只返回200的受保护端点。
有什么想法吗?(不寻找代码实现(
我要采用的方法是假设用户已登录,并发出所需的请求,该请求将在请求标头中自动发送httpOnly
令牌。
如果请求中不存在令牌,则服务器端应使用401进行响应,然后您可以在客户端进行相应的响应。
使用类似/api/users/me
的端点
服务器端
也许你不仅需要知道用户是否已经登录,还需要知道该用户是谁。因此,许多API都实现了像/api/users/me
这样的端点,它通过发送的cookie或授权头(或者你已经实现了服务器来验证请求(来验证请求。
然后,如果请求成功通过身份验证,它将返回当前用户。如果身份验证失败,则返回一个401 Not Authorized
(有关状态代码,请参阅维基百科(。
实现可能看起来像这样:
// UsersController.ts
// [...]
initializeRoutes() {
this.router.get('users/me', verifyAuthorization(UserRole.User), this.getMe);
}
async getMe(req: Request, res: Response) {
// an AuthorizedRequest has the already verified JWT token added to it
const { id } = (req as AuthorizedRequest).token;
const user = await UserService.getUserById(id);
if (!user) {
throw new HttpError(404, 'user not found');
}
logger.info(`found user <${user.email}>`);
res.json(user);
}
// [...]
// AuthorizationMiddleware.ts
export function verifyAuthorization(expectedRole: UserRole) {
// the authorization middleware throws a 401 in case the JWT is invalid
return async function (req: Request, res: Response, next: NextFunction) {
const authorization = req.headers.authorization;
if (!authorization?.startsWith('Bearer ')) {
logger.error(`no authorization header found`);
throw new HttpError(401, 'unauthorized');
}
const token = authorization.split(' ')[1];
const decoded = AuthenticationService.verifyLoginToken(token);
if (!decoded) {
logger.warn(`token not verified`);
throw new HttpError(401, 'unauthorized');
}
(req as AuthorizedRequest).token = decoded;
const currentRole = UserRole[decoded.role] ?? 0;
if (currentRole < expectedRole) {
logger.warn(`user not authorized: ${UserRole[currentRole]} < ${UserRole[expectedRole]}`);
throw new HttpError(403, 'unauthorized');
}
logger.debug(`user authorized: ${UserRole[currentRole]} >= ${UserRole[expectedRole]}`);
next();
};
}
客户端
如果响应代码是200 OK
并且包含用户数据,则将此数据存储在内存中(或者,如果不包含敏感信息,则将其存储在本地存储器中(。
如果请求失败,请重定向到登录页面(或者您希望应用程序在这种情况下运行的方式(。