GraphQl 和 passport 会话:查询 GraphQl 时访问 req.user
GraphQl and passport session: access req.user when querying GraphQl
我有一个 GraphQl 服务器和一个 React 前端。我使用护照和 LocalStrategy 来验证运行良好的用户,我可以成功登录现有用户。我还想使用 passport session 创建一个用户会话,这样我以后就可以在我的 GraphQl 解析器中访问登录的用户进行身份验证。我希望护照在成功验证用户身份后将用户设置为会话。但是在从客户端向服务器发送正确的凭据后,GraphQl 查询无法访问 req.user
.
GraphQL 服务器代码如下所示:
import express from 'express';
import passport from 'passport';
import {Strategy as LocalStrategy} from 'passport-local';
import session from 'express-session';
import cors from 'cors';
import bodyParser from 'body-parser';
import models from './models';
import typeDefs from './schema';
import resolvers from './resolvers';
import { graphqlExpress, graphiqlExpress } from 'apollo-server-express';
import { makeExecutableSchema } from 'graphql-tools';
export const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
const app = express();
app.use('*', cors({ origin: 'http://localhost:3000' }));
app.set('port', (process.env.PORT || 3001));
//--- Passport ----
app.use(session({
saveUninitialized: true,
resave: false,
secret: 'verysecretsecret'
}));
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser((user, done) => {
done(null, user);
});
passport.use(new LocalStrategy(
{
usernameField: 'email',
passwordField: 'password',
},
function(email, password, done) {
models.User.findOne({
where: {
email: email
}
}).then(function(user) {
if (user) {
if (user.validPassword(password)) {
return done(null, user);
} else {
return done(null, false);
}
}
return done(null, false);
});
}
));
//--- Routes ----
app.use('/graphiql', graphiqlExpress({
endpointURL: '/graphql'
}));
app.use(
'/graphql',
bodyParser.json(),
graphqlExpress( (req) => {
console.log('/graphql User: ' + req.user); // prints undefined after sending correct login credentials to /login
return ({
schema,
context: {
user: req.user,
},
});}),
);
app.use(bodyParser.urlencoded({ extended: true }) );
app.post('/login', passport.authenticate('local'), (req, res) => {
console.log('/login: User', req.user); // prints the logged in user's data
return res.sendStatus(200);
});
export default app;
这是来自客户端的登录获取请求:
onSubmit = () => {
var details = {
'email': this.state.email,
'password': this.state.password,
};
var formBody = [];
for (var property in details) {
var encodedKey = encodeURIComponent(property);
var encodedValue = encodeURIComponent(details[property]);
formBody.push(encodedKey + "=" + encodedValue);
}
formBody = formBody.join("&");
fetch('http://localhost:3001/login', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
},
credentials: 'include',
body: formBody
}).then(function(response) {
console.log(response);
}).catch(function(err) {
// Error
});
};
我是否必须在客户端更改某些内容才能使服务器接收会话 cookie?还是后端出了问题?
我还向这个 repo 上传了一个最小的例子:https://github.com/schmitzl/passport-graphql-minimal-example
在处理 CORS 时,管理会话会变得有点混乱。您需要更改几项才能获得您期望的行为:
首先,修改您的服务器代码以确保您发送 Access-Control-Allow-Credentials
header:
app.use('*', cors({ origin: 'http://localhost:3000', credentials: true }));
接下来,确保您的请求确实包含 cookie。通过将 credentials
选项设置为 include
,您已通过登录请求完成了此操作。 Apollo 在后台使用 fetch,我们也需要将此选项传递给它。
我可能遗漏了一些东西,但 apollo-boost
似乎没有提供一种简单的方法来执行上述操作(你有 fetchOptions,但包括 credentials
似乎没有任何事物)。我的建议是废弃 apollo-boost
并直接使用适当的库(或使用 apollo-client-preset
)。然后你可以将适当的 credentials
选项传递给 HttpLink
:
import ApolloClient from 'apollo-client'
import { HttpLink, InMemoryCache } from 'apollo-client-preset'
const client = new ApolloClient({
link: new HttpLink({ uri: apolloUri, credentials: 'include' }),
cache: new InMemoryCache()
})
我有一个 GraphQl 服务器和一个 React 前端。我使用护照和 LocalStrategy 来验证运行良好的用户,我可以成功登录现有用户。我还想使用 passport session 创建一个用户会话,这样我以后就可以在我的 GraphQl 解析器中访问登录的用户进行身份验证。我希望护照在成功验证用户身份后将用户设置为会话。但是在从客户端向服务器发送正确的凭据后,GraphQl 查询无法访问 req.user
.
GraphQL 服务器代码如下所示:
import express from 'express';
import passport from 'passport';
import {Strategy as LocalStrategy} from 'passport-local';
import session from 'express-session';
import cors from 'cors';
import bodyParser from 'body-parser';
import models from './models';
import typeDefs from './schema';
import resolvers from './resolvers';
import { graphqlExpress, graphiqlExpress } from 'apollo-server-express';
import { makeExecutableSchema } from 'graphql-tools';
export const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
const app = express();
app.use('*', cors({ origin: 'http://localhost:3000' }));
app.set('port', (process.env.PORT || 3001));
//--- Passport ----
app.use(session({
saveUninitialized: true,
resave: false,
secret: 'verysecretsecret'
}));
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser((user, done) => {
done(null, user);
});
passport.use(new LocalStrategy(
{
usernameField: 'email',
passwordField: 'password',
},
function(email, password, done) {
models.User.findOne({
where: {
email: email
}
}).then(function(user) {
if (user) {
if (user.validPassword(password)) {
return done(null, user);
} else {
return done(null, false);
}
}
return done(null, false);
});
}
));
//--- Routes ----
app.use('/graphiql', graphiqlExpress({
endpointURL: '/graphql'
}));
app.use(
'/graphql',
bodyParser.json(),
graphqlExpress( (req) => {
console.log('/graphql User: ' + req.user); // prints undefined after sending correct login credentials to /login
return ({
schema,
context: {
user: req.user,
},
});}),
);
app.use(bodyParser.urlencoded({ extended: true }) );
app.post('/login', passport.authenticate('local'), (req, res) => {
console.log('/login: User', req.user); // prints the logged in user's data
return res.sendStatus(200);
});
export default app;
这是来自客户端的登录获取请求:
onSubmit = () => {
var details = {
'email': this.state.email,
'password': this.state.password,
};
var formBody = [];
for (var property in details) {
var encodedKey = encodeURIComponent(property);
var encodedValue = encodeURIComponent(details[property]);
formBody.push(encodedKey + "=" + encodedValue);
}
formBody = formBody.join("&");
fetch('http://localhost:3001/login', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
},
credentials: 'include',
body: formBody
}).then(function(response) {
console.log(response);
}).catch(function(err) {
// Error
});
};
我是否必须在客户端更改某些内容才能使服务器接收会话 cookie?还是后端出了问题?
我还向这个 repo 上传了一个最小的例子:https://github.com/schmitzl/passport-graphql-minimal-example
在处理 CORS 时,管理会话会变得有点混乱。您需要更改几项才能获得您期望的行为:
首先,修改您的服务器代码以确保您发送 Access-Control-Allow-Credentials
header:
app.use('*', cors({ origin: 'http://localhost:3000', credentials: true }));
接下来,确保您的请求确实包含 cookie。通过将 credentials
选项设置为 include
,您已通过登录请求完成了此操作。 Apollo 在后台使用 fetch,我们也需要将此选项传递给它。
我可能遗漏了一些东西,但 apollo-boost
似乎没有提供一种简单的方法来执行上述操作(你有 fetchOptions,但包括 credentials
似乎没有任何事物)。我的建议是废弃 apollo-boost
并直接使用适当的库(或使用 apollo-client-preset
)。然后你可以将适当的 credentials
选项传递给 HttpLink
:
import ApolloClient from 'apollo-client'
import { HttpLink, InMemoryCache } from 'apollo-client-preset'
const client = new ApolloClient({
link: new HttpLink({ uri: apolloUri, credentials: 'include' }),
cache: new InMemoryCache()
})