Spring项目UserDetailsService配置的SQL执行报错,求排查思路
问题分析与解决方案
从你提供的PostgreSQL错误日志能看出核心问题:实际执行的SQL语句是不完整的。比如第一条错误日志里的执行语句只写到两个子查询的结尾,完全没包含后面的ON t1.credencial = t2.credencial WHERE t1.username = ?部分——这直接导致PostgreSQL抛出语法错误,因为JOIN操作必须要有连接条件,过滤条件也缺失了。
为什么会出现这种情况?问题出在你的Spring Security XML配置里:当你在<property>的value属性中写多行SQL语句时,XML解析器会把换行、空白字符当作节点分隔符处理,导致最终传递给数据库的SQL被截断,只保留了前半部分内容。
修复方案
你需要把authoritiesByUsernameQuery的SQL语句用CDATA块包裹起来,这样XML解析器会完整读取整个SQL字符串,不会截断或解析错误。修改后的配置如下:
<bean id="userDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl"> <property name="dataSource" ref="dataSource"></property> <property name="usersByUsernameQuery" value="select login, senha, enabled from usuario where login = ?"></property> <property name="authoritiesByUsernameQuery"> <value><![CDATA[ SELECT t1.username, t2.authority FROM ( SELECT u.login as username, c.nome as credencial FROM usuario u, usuario_credencial uc, credencial c WHERE u.id = uc.usuario_id and c.id = uc.credenciais_id ) as t1 INNER JOIN ( SELECT c.nome as credencial, a.nome as authority FROM credencial c, credencial_autorizacao ca, autorizacao a WHERE c.id = ca.credencial_id and a.id = ca.autorizacoes_id ) as t2 ON t1.credencial = t2.credencial WHERE t1.username = ?; ]]></value> </property> </bean>
当然你也可以把SQL压缩成单行来避免这个问题,但用CDATA的方式既能保留SQL的可读性格式,又能确保解析完整,更适合后续维护。
额外优化建议
你还可以把老式的隐式JOIN(用逗号分隔表)改成显式JOIN语法,这样SQL逻辑更清晰,也符合现代SQL规范,同时能减少出错概率:
SELECT u.login as username, a.nome as authority FROM usuario u JOIN usuario_credencial uc ON u.id = uc.usuario_id JOIN credencial c ON c.id = uc.credenciais_id JOIN credencial_autorizacao ca ON c.id = ca.credencial_id JOIN autorizacao a ON a.id = ca.autorizacoes_id WHERE u.login = ?;
这个简化后的SQL和你原来的子查询逻辑完全等价,你可以直接替换到CDATA块中使用。
内容的提问来源于stack exchange,提问作者Kleber Mota
相关产品推荐
相关产品推荐

