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

如何实现支持用户名(User表)或邮箱(ContactPerson表)的登录功能?

实现用户名/邮箱双方式登录的正确方案

原HQL语句的问题

你的HQL存在两个核心错误:

  1. 逻辑运算符优先级混乱:AND优先级高于OR,原语句会被解析成(usr.name = :username AND usr.pass = :password) OR ctp.email = :username,导致只要输入内容匹配任意联系人邮箱,无需验证密码就可能返回结果,完全不符合登录逻辑。
  2. 表关联逻辑错误:JOIN的ON条件应该是User和ContactPerson之间的外键关联关系(比如usr.id = ctp.userId),而非用输入的用户名来关联两张表,这会导致关联逻辑完全失效。

正确实现方案

方案一:使用关联查询的HQL语句

首先明确User和ContactPerson的关联关系(假设ContactPerson表存在userId字段,关联User表的id主键),通过括号包裹OR条件确保逻辑优先级正确:

已映射实体关联的情况(推荐)

如果User实体类已通过注解/XML映射了与ContactPerson的关联(比如@OneToOne private ContactPerson contactPerson;),可以直接用属性关联:

FROM User usr
LEFT JOIN usr.contactPerson ctp
WHERE (usr.name = :loginValue OR ctp.email = :loginValue)
AND usr.pass = :password

未映射实体关联的情况

如果没有实体关联,需显式指定JOIN的外键条件:

FROM User usr
LEFT JOIN ContactPerson ctp ON usr.id = ctp.userId
WHERE (usr.name = :loginValue OR (ctp.email = :loginValue AND ctp IS NOT NULL))
AND usr.pass = :password

使用LEFT JOIN可确保未关联ContactPerson的用户依然能通过用户名登录。

修改后的Java代码:

public LoginUsr logInUsr(RequestLoginUsr request) {
    List<User> userList = getCurrentSession()
            .createQuery("FROM User usr LEFT JOIN ContactPerson ctp ON usr.id = ctp.userId WHERE (usr.name = :loginValue OR (ctp.email = :loginValue AND ctp IS NOT NULL)) AND usr.pass = :password", User.class)
            .setParameter("loginValue", request.getUsername())
            .setParameter("password", request.getPassword())
            .getResultList();

    if (userList.size() == 1) {
        User u = userList.get(0);
        return new LoginUsr(u.getSurname(), u.getId(), u.getFkUsrPrc(), null);
    } else {
        return null;
    }
}

方案二:分两次查询(更直观)

如果觉得关联查询逻辑复杂,可拆分两次查询:先尝试用户名+密码登录,失败后再用邮箱查找对应用户并验证密码:

public LoginUsr logInUsr(RequestLoginUsr request) {
    // 1. 尝试用户名+密码登录
    List<User> userNameMatch = getCurrentSession()
            .createQuery("from User where name = :loginValue and pass = :password", User.class)
            .setParameter("loginValue", request.getUsername())
            .setParameter("password", request.getPassword())
            .getResultList();

    if (!userNameMatch.isEmpty()) {
        User u = userNameMatch.get(0);
        return new LoginUsr(u.getSurname(), u.getId(), u.getFkUsrPrc(), null);
    }

    // 2. 尝试邮箱+密码登录
    List<User> emailMatch = getCurrentSession()
            .createQuery("select usr from User usr join ContactPerson ctp on usr.id = ctp.userId where ctp.email = :loginValue and usr.pass = :password", User.class)
            .setParameter("loginValue", request.getUsername())
            .setParameter("password", request.getPassword())
            .getResultList();

    if (!emailMatch.isEmpty()) {
        User u = emailMatch.get(0);
        return new LoginUsr(u.getSurname(), u.getId(), u.getFkUsrPrc(), null);
    }

    // 两种方式都未匹配
    return null;
}

额外注意事项

  • 密码安全:当前代码明文比较密码存在严重风险,建议使用BCrypt、Argon2等哈希算法加密存储密码,登录时对输入密码哈希后再对比。
  • 唯一性约束:需在数据库中给ContactPerson.email添加唯一约束,避免多个用户关联同一邮箱导致登录逻辑混乱。
  • 结果去重:如果存在用户名和邮箱重复的极端情况,需在业务逻辑中避免冲突,或在查询中添加distinct关键字确保返回唯一用户。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 08:30:57