You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Prisma GraphQL结合GraphQL Shield的JWT登录系统问题求助

嘿,刚好我之前也折腾过GraphQL Shield + Prisma的登录流程,你的两个问题我大概能猜到原因,给你分析下:

问题1:同时用email和password查询User失败

首先得明确Prisma里db.user()(单个用户查询)的参数要求:它接受的是唯一约束条件,也就是你在Prisma Schema里标记了@unique的字段,或者复合唯一索引。比如如果你的User模型里只有email @unique,那单独传email是符合UserWhereUniqueInput要求的,但同时传email和password就不行——因为这俩组合不是唯一约束,Prisma不知道怎么用这个条件定位单个唯一用户。

那为什么单个password能运行?可能你刚好给password也加了@unique?但这显然不合理,用户密码重复太正常了。

解决方法很简单:如果要用多个非唯一条件查询,应该用列表查询db.users()(Prisma 1)或者prisma.user.findFirst()(Prisma 2+),然后取第一个匹配的结果。举个例子:

// Prisma 1写法
Mutation: {
  login: (_, data, ctx) => {
    return ctx.db.users({
      where: {
        email: ctx.request.body.variables.email,
        password: ctx.request.body.variables.password
      }
    })
    .then(users => {
      // 取第一个匹配的用户
      const user = users[0];
      console.log(user);
      // 这里可以继续生成JWT等操作
      return user;
    })
    .catch(err => {
      console.error(err);
      return err;
    })
  },
  ...
}

// 如果是Prisma 2+,更推荐用findFirst
Mutation: {
  login: async (_, { email, password }, ctx) => {
    try {
      const user = await ctx.prisma.user.findFirst({
        where: { email, password }
      });
      console.log(user);
      return user;
    } catch (err) {
      console.error(err);
      throw err;
    }
  }
}

如果非要用db.user()的话,你得在Prisma Schema里给email和password加复合唯一约束,但真的不建议这么做,完全没必要。


问题2:调用ctx.db.query.user时被GraphQL Shield拦截

首先要搞清楚:GraphQL Shield管的是客户端发送的GraphQL请求字段,而不是你服务端内部调用Prisma的方法。你配置了Mutation: { login: allow },那客户端发送mutation { login(...) { ... } }的时候,Shield会放行这个请求;而你在login解析器里调用ctx.db.query.user,这是服务端和Prisma的交互,Shield根本管不着。

那为什么会报「Not Authorised!」?大概率是这几个原因:

  • Shield默认规则是deny,但你没正确开放Mutation.login:比如拼写错误?比如你的GraphQL Schema里Mutation字段是Login(首字母大写),但Shield里写的是login,大小写不匹配就会触发默认的deny规则。
  • Shield的应用顺序不对:比如你把Shield middleware放在了其他权限校验之后,或者没正确挂载到Apollo Server上。比如在Apollo Server 3+里,应该用plugins配置Shield,而不是旧版的middleware。
  • 你混淆了Prisma的API版本:ctx.db.query.user是Prisma 1的写法,如果你用的是Prisma 2+,这个API已经废弃了,换成prisma.user.findUnique()才对,旧API可能触发一些奇怪的权限问题。

给你一个正确的Shield配置示例(适配Prisma 2+和Apollo Server 3+):

// 定义Shield规则
const permissions = shield({
  Query: {
    user: allow, // 客户端调用query { user(...) }会被放行
  },
  Mutation: {
    login: allow, // 客户端调用mutation { login(...) }会被放行
  },
  fallbackRule: deny, // 其他未配置的字段默认拒绝访问
});

// 配置Apollo Server
const server = new ApolloServer({
  typeDefs, // 你的GraphQL Schema
  resolvers, // 你的解析器
  context: ({ req }) => ({
    req,
    prisma: new PrismaClient() // 或者全局实例
  }),
  plugins: [shield(permissions)], // 正确挂载Shield插件
});

另外,建议你不要直接从ctx.request.body.variables取参数,解析器的第二个参数data(或者解构的{ email, password })已经包含了客户端传递的变量,直接用更规范。

内容的提问来源于stack exchange,提问作者Rip3rs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:18:44