Vapor UserAuthenticator未执行 req.auth.require(User.self)始终失败
问题原因
导致自定义认证器不执行、始终获取不到登录用户的核心原因有4个:
- 认证器挂载错误:路由分组中你使用的是
User.authenticator(),这是Fluent为User模型自动生成的默认认证器,和你自定义实现的UserAuthenticator完全无关,你的自定义认证逻辑根本没有被注册到路由中间件链中,自然不会执行。 - 触发条件不满足:
AsyncBearerAuthenticator的内置逻辑会先校验请求头,只有携带格式为Authorization: Bearer <access_token>的合法请求头时,才会进入你实现的authenticate方法,测试时如果没带这个头,哪怕挂载对了认证器也不会触发逻辑。 - 认证器逻辑设计错误:Bearer认证的核心逻辑是通过请求头携带的token反查用户身份,你当前实现中要求从请求query参数中解码
LoginUserDTO(需要传邮箱、密码字段),完全违背Bearer认证的调用约定,哪怕认证器触发,这一步解码就会失败。 - 用户对象上下文不匹配:认证通过后你登录的是
loginUser.toUser()生成的临时对象,不是数据库中查询到的完整User模型实例,和request.auth.require(User.self)的匹配逻辑不兼容,即便走到这一步也无法正确获取用户。
另外你路由中用try?调用auth.require会吞掉所有认证过程抛出的错误,只会返回nil,不利于调试定位问题。
修复方案
按以下步骤修改即可:
- 修正路由中间件挂载,替换为你自定义的认证器,同时加上guard中间件让认证失败时直接返回401,避免吞错:
// 替换原有protected路由分组代码 let protected = app.grouped(UserAuthenticator(), User.guardMiddleware()) protected.post("me") { request async throws -> String in let user = try request.auth.require(User.self) return "yes" }
- 重写
UserAuthenticator的认证逻辑,直接通过请求头中的token查库,校验通过后登录数据库中查询到的关联用户,同时补上你之前TODO的过期时间校验:
struct UserAuthenticator: AsyncBearerAuthenticator { func authenticate(bearer: BearerAuthorization, for request: Request) async throws { typealias User = App.User // 用请求头传入的token匹配数据库中有效token guard let userToken = try await UserToken.query(on: request.db) .filter(\.$accessToken == bearer.token) // 校验token未过期 .filter(\.$accessTokenExpirationDate > Date()) // 预加载token关联的用户对象 .with(\.$user) .first() else { throw Abort(.unauthorized) } // 直接登录查库得到的完整用户实例 request.auth.login(userToken.user) } }
- 测试接口时,给
/mePOST请求添加请求头:Key为Authorization,值为Bearer 你调用login接口返回的accessToken,不需要在query或请求体中传递邮箱、密码字段。
额外提一个笔误:你login接口返回的结构体名ClientTokenReponse拼写错误,正确应为ClientTokenResponse,不影响本次认证逻辑但建议修正。
内容的提问来源于stack exchange,提问作者kalafun
相关产品推荐
相关产品推荐

