【发布时间】:2020-12-15 01:33:57
【问题描述】:
我在 Angular 中有一个组件,我使用 HttpClient 向服务器发出 GET 请求以获取当前登录的用户。由于这是一个 SSR 应用程序,因此代码在客户端和服务器上都运行。问题是它在服务器上运行时,会话数据不可用,这意味着对后端的请求无法通过身份验证,因此失败。在客户端,会话数据可用,因此请求成功。
我将express-session 与以下会话选项一起使用:
const sessionOptions: session.SessionOptions = {
secret: process.env.SESSION_SECRET || 'placeholder',
resave: false,
saveUninitialized: true,
cookie: { secure: false },
};
server.use(session(sessionOptions));
我使用 Twitter OAuth 进行身份验证。
const router = Router();
router.get('/sessions/connect', (req, res) => {
const twitterAuth = new TwitterAuth(req);
twitterAuth.consumer.getOAuthRequestToken((error, oauthToken, oauthTokenSecret, _) => {
if (error) {
console.error('Error getting OAuth request token:', error);
res.sendStatus(500);
} else {
req.session.oauthRequestToken = oauthToken;
req.session.oauthRequestTokenSecret = oauthTokenSecret;
res.redirect(`https://twitter.com/oauth/authorize?oauth_token=${oauthToken}`);
}
});
});
router.get('/sessions/disconnect', (req, res) => {
req.session.oauthRequestToken = null;
req.session.oauthRequestTokenSecret = null;
res.redirect('/');
});
router.get('/sessions/callback', (req, res) => {
const twitterAuth = new TwitterAuth(req);
const oauthVerifier = req.query.oauth_verifier as string;
twitterAuth.consumer.getOAuthAccessToken(
req.session.oauthRequestToken,
req.session.oauthRequestTokenSecret,
oauthVerifier,
async (error, oauthAccessToken, oauthAccessTokenSecret, results) => {
if (error) {
console.error('Error getting OAuth access token:', error, `[${oauthAccessToken}] [${oauthAccessTokenSecret}] [${results}]`);
res.sendStatus(500);
} else {
req.session.oauthAccessToken = oauthAccessToken;
req.session.oauthAccessTokenSecret = oauthAccessTokenSecret;
const twitter = twitterAuth.api(req.session);
try {
const response = await twitter.get('account/verify_credentials', {});
const screenName = response.screen_name;
console.log(`Signed in with @${screenName}`);
console.log(response);
res.redirect('/');
} catch (err) {
console.error(err);
res.sendStatus(500);
}
}
}
);
});
router.get('/user', async (req, res) => {
const twitterAuth = new TwitterAuth(req);
const twitter = twitterAuth.api(req.session);
try {
const response = await twitter.get('account/verify_credentials', {});
res.json({
name: response.name,
screenName: response.screen_name,
});
} catch (err) {
console.error(err);
res.sendStatus(401);
}
});
在客户端,GET 请求如下所示:
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { catchError } from 'rxjs/operators';
import { makeStateKey, TransferState } from '@angular/platform-browser';
import { User } from '../types/user';
const USER_KEY = makeStateKey('user');
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.sass']
})
export class HomeComponent implements OnInit {
user?: User;
constructor(private httpClient: HttpClient, private state: TransferState) {}
ngOnInit(): void {
this.user = this.state.get(USER_KEY, null);
if (!this.user) {
this.httpClient.get('/api/twitter/user').pipe(catchError(this.handleHttpError)).subscribe((user: User) => {
this.user = user;
this.state.set(USER_KEY, user);
});
}
}
// [irrelevant code omitted]
}
这个想法是首先在服务器上执行 GET 请求,然后使用 TransferState 保存用户,这样一旦相同的代码在客户端上再次运行,它就可供客户端使用。但是,问题是请求在服务器上失败并出现以下错误:
ERROR HttpErrorResponse {
headers: HttpHeaders {
normalizedNames: Map(0) {},
lazyUpdate: null,
lazyInit: [Function (anonymous)]
},
status: 401,
statusText: 'Unauthorized',
url: 'https://<domain>/api/twitter/user',
ok: false,
name: 'HttpErrorResponse',
message: 'Http failure response for https://<domain>/api/twitter/user: 401 Unauthorized',
error: 'Unauthorized'
当我控制台记录客户端 GET 调用和服务器 GET 调用的 expressjs request.session 对象时,我注意到服务器 GET 调用具有不同的会话 ID,因此它缺少用于身份验证的令牌和令牌机密请求。如何确保客户端和服务器共享相同的会话 ID 和相同的令牌?
【问题讨论】:
标签: node.js angular typescript express session