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

Shiro INI配置如何添加额外参数 实现数据库多字段查询认证

问题原因

Shiro原生JdbcRealm执行authenticationQuery认证查询时,逻辑固定仅向SQL传入1个绑定参数:即登录时UsernamePasswordToken中存储的登录标识(前端传入的username字段值),仅会对第一个?占位符赋值。你写的SQL包含2个占位符,第二个占位符没有传入参数,自然会抛出参数未配置的错误,这个问题无法直接通过编写多占位符的SQL在原生配置下解决。

解决方案

方案1:零代码修改,调整SQL适配原生JdbcRealm逻辑

不需要自定义任何Java类,仅修改ini中的SQL语句,通过子查询将单个传入的参数复用给多字段匹配条件,整个SQL仅保留1个?占位符即可。
修改后的认证SQL配置示例:

jdbcRealm.authenticationQuery = SELECT user_password FROM user, (SELECT ? AS login_key) tmp 
WHERE (
  user_name = tmp.login_key 
  OR CAST(user_id AS CHAR) = tmp.login_key 
  OR user_email = tmp.login_key
) AND user_status = 1

注意事项:

  • 如果user_id是数值类型,必须通过数据库的类型转换函数转为字符串,避免和传入的字符串类型登录值做匹配时出现类型错误,MySQL用CAST(user_id AS CHAR)、Oracle用TO_CHAR(user_id)即可。
  • 原有的userRolesQuery角色查询、权限查询也需要做相同改造,否则用用户ID/邮箱登录时,无法查询到对应角色、权限,会导致授权失败。角色查询改造示例:
jdbcRealm.userRolesQuery = SELECT t.login_type_name FROM login_type t 
INNER JOIN schooler_user_rel r ON r.login_type_id = t.login_type_id 
INNER JOIN user u ON u.user_id = r.user_id,
(SELECT ? AS login_key) tmp
WHERE (
  u.user_name = tmp.login_key
  OR CAST(u.user_id AS CHAR) = tmp.login_key
  OR u.user_email = tmp.login_key
)

方案2:自定义Realm扩展逻辑(适合后续有更多登录扩展需求的场景)

如果后续需要增加更多登录校验逻辑(比如不同登录类型走不同校验规则、加额外的账号状态判断),可以自定义Realm继承原生JdbcRealm,重写认证查询的参数绑定逻辑,手动给多个占位符赋值。

  1. 编写自定义Realm类:
import org.apache.shiro.authc.*;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class MultiFieldAuthJdbcRealm extends JdbcRealm {
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        String loginValue = upToken.getUsername();
        String dbPassword = null;

        try (Connection conn = getDataSource().getConnection();
             PreparedStatement ps = conn.prepareStatement(getAuthenticationQuery())) {
            // 按你SQL里的占位符数量,全部绑定同一个登录入参
            ps.setString(1, loginValue);
            ps.setString(2, loginValue);
            ps.setString(3, loginValue);
            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    dbPassword = rs.getString(1);
                }
            }
        } catch (SQLException e) {
            throw new AuthenticationException("账号认证查询执行失败", e);
        }

        if (dbPassword == null) {
            throw new UnknownAccountException("账号不存在");
        }
        return new SimpleAuthenticationInfo(loginValue, dbPassword.toCharArray(), getName());
    }
}
  1. 修改shiro.ini中的Realm配置为自定义类,其余原有配置保持不变即可:
jdbcRealm = 你的类全限定名.MultiFieldAuthJdbcRealm
jdbcRealm.dataSource = $ds
jdbcRealm.credentialsMatcher = $passwordMatcher
jdbcRealm.permissionsLookupEnabled = false
# 这里可以直接写多占位符的SQL,自定义Realm会给所有占位符赋值
jdbcRealm.authenticationQuery = SELECT user_password FROM user WHERE (user_name = ? OR CAST(user_id AS CHAR) = ? OR user_email = ?) AND user_status = 1
# 角色、权限查询如果也需要支持多字段登录,也可以通过重写对应查询方法实现多参数绑定
jdbcRealm.userRolesQuery = SELECT login_type_name FROM login_type t INNER JOIN schooler_user_rel r ON r.login_type_id = t.login_type_id INNER JOIN user u ON u.user_id = r.user_id WHERE u.user_name = ? OR CAST(u.user_id AS CHAR) = ? OR u.user_email = ?

authc.usernameParam = username
authc.passwordParam = password

内容的提问来源于stack exchange,提问作者Vijay Kumar Kanta

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 12:09:21