Vert.x FormLoginHandler对接PostgreSQL SqlAuthentication认证失败问题
Vert.x SqlAuthentication 认证报无效用户名密码问题排查
问题现象
使用FormLoginHandler、PostgreSQL配合SqlAuthentication实现用户认证,已确认输入的用户名密码凭据完全正确,仍抛出如下错误:
Jun 15, 2022 1:14:34 PM io.vertx.ext.web.RoutingContext
SEVERE: Unhandled exception in router
io.vertx.ext.web.handler.HttpException: Unauthorized
Caused by: io.vertx.core.impl.NoStackTraceThrowable: Invalid username/password
原有实现代码片段:
SqlAuthenticationOptions sauthopts = new SqlAuthenticationOptions(); sauthopts.setAuthenticationQuery(AUTHENTICATE_QUERY); SqlAuthentication authenticationProvider = SqlAuthentication.create(sqlClient, sauthopts); router.route("/secure/*").handler(RedirectAuthHandler.create(authenticationProvider, "/login.html")); FormLoginHandler formLoginHandler = FormLoginHandler.create(authenticationProvider); router.route("/loginhandler").handler(formLoginHandler);
问题根因&修复方案
当前实现存在3个高频遗漏配置点,按优先级排查即可:
- 认证查询语句返回列不符合强制规范
这是90%同类报错的诱因。SqlAuthentication要求自定义的认证查询必须按固定顺序返回3列,缺任意一列都会直接判定凭据无效:- 第一列:数据库存储的用户密码(哈希值或明文)
- 第二列:密码使用的哈希算法标识(开发测试用明文可传
noop,生产常用pbkdf2、bcrypt等) - 第三列:密码哈希使用的盐值(无盐则传空字符串)
同时注意PostgreSQL对接时查询的用户名占位符必须用?(位置参数),不能使用命名参数。
正确的查询示例参考:
// 注意返回列顺序、占位符写法 private static final String AUTHENTICATE_QUERY = "SELECT password, 'pbkdf2' AS algo, salt FROM sys_user WHERE username = ?"; - 密码哈希校验策略不匹配
如果数据库存储的密码哈希算法和查询返回的算法标识不一致,比如数据库存的是MD5哈希值,但查询返回的算法标识写了pbkdf2,哈希比对必然失败。开发阶段临时调试可以把算法标识设为noop,此时会直接做明文比对,快速排除逻辑问题。 - FormLoginHandler配置缺失
两个常见问题:一是默认读取表单中name="username"和name="password"的参数作为凭据,如果前端登录表单输入框name属性和默认值不一致,会读不到用户输入的凭据,直接报无效;二是未限定登录路由的请求方法、未配置登录成功跳转地址,会导致异常请求触发认证逻辑、认证完成后无正确响应。需要手动补全配置对齐业务逻辑。
完整可运行参考片段
// 1. 初始化认证配置 SqlAuthenticationOptions authOpts = new SqlAuthenticationOptions(); // 配置符合返回列要求的认证查询,开发调试用noop明文校验 authOpts.setAuthenticationQuery("SELECT password, 'noop' as algo, '' as salt FROM users WHERE username = ?"); SqlAuthentication authProvider = SqlAuthentication.create(sqlClient, authOpts); // 2. 配置鉴权拦截器 router.route("/secure/*").handler(RedirectAuthHandler.create(authProvider, "/login.html")); // 3. 配置表单登录处理器 FormLoginHandler loginHandler = FormLoginHandler.create(authProvider) .setUsernameParam("username") // 和前端表单输入框name属性对齐 .setPasswordParam("password") // 和前端表单输入框name属性对齐 .setDirectLoggedInOKURL("/secure/index"); // 配置登录成功后的默认跳转地址 // 仅接收POST方法的登录提交请求 router.post("/loginhandler").handler(loginHandler);
内容的提问来源于stack exchange,提问作者Lucas
相关产品推荐
相关产品推荐

