express-jwt 和带有 cookie 的 Angular 9:未在“授权”标头中设置令牌

Posted

技术标签:

【中文标题】express-jwt 和带有 cookie 的 Angular 9:未在“授权”标头中设置令牌【英文标题】:express-jwt and Angular 9 with cookie: token not set in the `Authorization` header 【发布时间】:2020-07-27 21:24:57 【问题描述】:

你好,

我正在努力在我的 express 后端和 Angular 9 前端之间实现 JWT。

我决定使用 cookie(稍后我会确保它的安全)。

使用 express-jwt,我可以使用以下代码创建一个 cookie 并将其发送到我的 angular 应用程序:

// The middleware function
const checkIfAuthenticated = expressJwt(
   secret: RSA_PUBLIC_KEY
)

[...]

// The part where I send the cookie to Angular
res.status(200)
   .cookie('SESSIONID', jwtBearerToken, httpOnly:true, secure:false)
   .send('OK');

[...]

// My testing route, to be tried after the browser got the cookie
app.get('/test', checkIfAuthenticated, (req, res) => 
    console.log(req.cookies)
    res.status(200).json("Authorized!")
)

我可以使用开发人员的工具查看 cookie(注意开发过程中的 secure:false),但是如果我让 checkIfAuthenticated 激活,我将收到以下消息:

UnauthorizedError: No authorization token was found

然后,Angular 发出的请求包含令牌,但不在教程所述的 Authorization 标头中(例如:https://blog.angular-university.io/angular-jwt-authentication/),而是令牌在常规 cookie 中。

这令人惊讶,因为关于 express-jwt 文档:

模块的默认行为是从 Authorization标头

使用 Wireshark,我在调用后端时观察到,JWT 令牌作为常规 cookie 传递,express-jwt 期望在授权标头中找到它。

我从几个小时开始浏览网络,但我不知道应该如何在 Authorization 标头中发送通过 .cookie('SESSIONID', jwtBearerToken, httpOnly:true, secure:false) 发送的经典 cookie。我原以为 cookie-parser 会负责这件事,但我现在很怀疑。

欢迎任何帮助,在此先感谢!

【问题讨论】:

【参考方案1】:

我不完全确定这个系统是如何设置的,以及 JWT 的 cookie 中间件是如何工作的,从 angular-univeristy 教程的评论部分,我可以看到有人评论了 authorizaiton 标头也不起作用,结论是

Safari needs an ending slash on the API's for the Authorization Header to be appended...
Where chrome doesn't need the ending slash and can always add API Headers.
I've appended the slash and it's also working on mobile devices now!

我会说如果 cookie 让您头疼,也许没有必要。cookie 可以很好地存储会话,但也有很多法律管理隐私,您需要告知用户该网站使用 cookie,就像在“授权”标头中使用 JWT 一样可以完成身份验证和授权。

考虑以下使用 express 的中间件

import express from 'express';
import cors from 'cors';
import jwt from 'jsonwebtoken';

const app = express();
const JWT_SECRET = 'Your Secret';

app.use(express.urlencoded( extended: true ));
app.use(express.json());
app.set('trust proxy', true);
app.disable('x-powered-by');
app.use(cors());

// This can throw an error so always catch it!
function decodeJWT(token) 
  return new Promise((resolve, reject) => 
    jwt.verify(token, JWT_SECRET, (err, obj) => 
      if (err) return reject(err);
      return resolve(obj);
    );
  );


function encodeJWT(payload, options =  expiresIn: '30d' ) 
  return new Promise((resolve, reject) => 
    jwt.sign(payload, JWT_SECRET, options, (err, token) => 
      if (err) return reject(err);
      return resolve(token);
    );
  );


// This is a middleware function that will activate
// from every request that is sent to the server
// The Angular application can read and write to the 'Authorization' header
app.use(async(req, res, next) => 
  const JWT_TOKEN = req.header('Authorization');
  if (JWT_TOKEN) 
    try 
      // This is the custom property we add to the request
      // Adding this property we can check if the JWT token exists
      // in other API end points of our Express application
      // if this verification does not throw an error, it is valid
      // otherwise, the token will not exist in other API points
      req.JWT = await decodeJWT(JWT_TOKEN);
      res.setHeader('Authroization', JWT_TOKEN)
     catch (err) 
      console.error(err);
      delete req.JWT;
      res.removeHeader('Authorization');
    
  
  next();
);

// Declare all rest API below the middleware
app.post('/api/login', async(req, res, next) => 
  const email = req.body.email;
  const password = req.body.password;
  if (email && password)  // Custom validation can be implemented here
    try 
      const JWT_TOKEN = await encodeJWT( email: email, auth: true );
      res.setHeader('Authorization', JWT_TOKEN);
      return res.status(200).json(JWT_TOKEN);
     catch (err) 
      return res.sendStatus(500).json(err);
    
  
  res.sendStatus(401).send("Email or Password invalid!"); 
);

app.get('/api/members/only', (req, res, next) => 
  if (req.JWT)  // Or if req.JWT.auth === true
    return res.status(200).send('Hello world!');
  
  return res.status(401).send('Access Denied');
);

app.listen(8080, () => conosle.log('app listening'));

在您的 Angular 应用程序中,您可以使用电子邮件和密码发布到 /api/login 路由,并接收 JWT 令牌以在“授权”标头中设置。

Angular 中的授权服务可能如下所示

@Injectable()
export class AuthService 

    constructor(private http: HttpClient) 
    

    async login(email: string, password: string) 
      let JWT = await this.http.post('/api/login', email, password);
      localStorage.set('Authorization', JWT);
      return JWT;
    

    logout() 
      return localStorage.removeItem('Authorization');
    

    membersArea() 
      // Also consider a HTTP Interceptor instead of manually setting the headers
      let headers = new Headers();
      headers.append('Content-Type', 'application/json');
      headers.append('Authorization', localStorage.getItem('Authorization'));

      let options = new RequestOptions( headers: headers );
      return this.http.get('/api/members/only', options);
    

希望对你有帮助

【讨论】:

嗨@SVNTY,感谢您的帮助。晚上我想了很多,我想我会回到“其他方式”。但是,我真的很好奇它应该如何与 cookie 一起工作。我已经看到您引用的消息,并尝试添加/,以防万一。没有骰子!如果有人知道应该如何管理它,我会公开这个问题,这可能对其他人有用!祝你有美好的一天。

以上是关于express-jwt 和带有 cookie 的 Angular 9:未在“授权”标头中设置令牌的主要内容,如果未能解决你的问题,请参考以下文章

使用 angular 和 express-jwt 实现刷新令牌

使用带有 RS256 加密的 express-jwt 在应用程序路由上解码 JWT 令牌会引发未经授权的错误

带有 express-unless 的 Node.js express-jwt 给出警告“对象可能是'未定义'”

JWT 和 express-JWT 有问题

jsonwebtoken和express-jwt的使用

如何查看存储在 JWT 中的数据?使用 auth0 和 express-jwt