Thymeleaf获取principal.username报错 jQuery拼接链接参数异常
问题描述
开发时尝试通过jQuery将用户名拼接至页面链接参数,遇到两个异常:
- 配置了
sec:authentication="principal.username"的span标签可以正常渲染用户名,但调用$("#emailTarget").attr("id")获取span内用户名时,返回undefined,不符合预期 - 在配置了
th:inline="javascript"的内联脚本中,通过[[${principal.username}]]直接取值触发Thymeleaf模板解析错误,核心报错为SpEL表达式解析异常:EL1007E: Property or field 'username' cannot be found on null,即取值时principal对象为null
错误堆栈
org.thymeleaf.templateparser.markup.InlinedOutputExpressionMarkupHandler.handleText(InlinedOutputExpressionMarkupHandler.java:80) at org.attoparser.HtmlMarkupHandler.handleText(HtmlMarkupHandler.java:208) at org.attoparser.AbstractChainedMarkupHandler.handleText(AbstractChainedMarkupHandler.java:203) at org.attoparser.MarkupParser.parseBuffer(MarkupParser.java:557) at org.attoparser.MarkupParser.parseDocument(MarkupParser.java:301) ... 98 more Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "principal.username" (template: "index" - line 49, col 25) at org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator.evaluate(SPELVariableExpressionEvaluator.java:292) at org.thymeleaf.standard.expression.VariableExpression.executeVariableExpression(VariableExpression.java:166) at org.thymeleaf.standard.expression.SimpleExpression.executeSimple(SimpleExpression.java:66) at org.thymeleaf.standard.expression.Expression.execute(Expression.java:109) at org.thymeleaf.standard.expression.Expression.execute(Expression.java:138) at org.thymeleaf.standard.processor.AbstractStandardExpressionAttributeTagProcessor.doProcess(AbstractStandardExpressionAttributeTagProcessor.java:144) at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74) at org.thymeleaf.processor.element.AbstractElementTagProcessor.process(AbstractElementTagProcessor.java:95) at org.thymeleaf.util.ProcessorConfigurationUtils$ElementTagProcessorWrapper.process(ProcessorConfigurationUtils.java:633) at org.thymeleaf.engine.ProcessorTemplateHandler.handleOpenElement(ProcessorTemplateHandler.java:1314) at org.thymeleaf.engine.OpenElementTag.beHandled(OpenElementTag.java:205) at org.thymeleaf.engine.TemplateModel.process(TemplateModel.java:136) at org.thymeleaf.engine.TemplateManager.process(TemplateManager.java:519) at org.thymeleaf.util.LazyProcessingCharSequence.writeUnresolved(LazyProcessingCharSequence.java:85) at org.thymeleaf.util.AbstractLazyCharSequence.write(AbstractLazyCharSequence.java:103) at org.thymeleaf.engine.AbstractTextualTemplateEvent.writeContent(AbstractTextualTemplateEvent.java:224) at org.thymeleaf.engine.Text.write(Text.java:78) at org.thymeleaf.engine.OutputTemplateHandler.handleText(OutputTemplateHandler.java:71) ... 107 more Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'username' cannot be found on null at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:213) at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:104) at org.springframework.expression.spel.ast.PropertyOrFieldReference.access$000(PropertyOrFieldReference.java:51)
原始问题模板代码
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> <head> <meta charset="ISO-8859-1"> <title>Registration and Login App</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <!-- 导航栏 --> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#" th:href="@{/}">Registration and Login Module</a> </div> <div id="navbar" class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <li sec:authorize="isAuthenticated()"><a th:href="@{/logout}">Logout</a></li> </ul> </div> </div> </nav> <br> <br> <div class="container"> <h1>Registration and Login with Spring Boot, Spring Security, Thymeleaf, Hibernate and MySQL</h1> Welcome <span sec:authentication="principal.username"> User</span> </div> <a th:href="login" id="changeMe" class="btn btn-danger">Delete</a> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <script type="text/javascript" th:inline="javascript"> let email=[[${principal.username}]]; $('#changeMe').prop("href", "/delete?email="+email) </script> </body> </html>
问题根因
- jQuery取值逻辑完全错误:
attr("id")方法的作用是获取标签的id属性值,首先原代码里显示用户名的span根本没有设置id="emailTarget"属性,就算给span加上这个id,调用attr("id")拿到的也只是固定字符串emailTarget,不可能拿到标签内部渲染的文本内容。 - Thymeleaf取值上下文错误:
sec:authentication是thymeleaf-extras-springsecurity扩展提供的专属标签,它会单独从Security上下文获取认证信息,普通Thymeleaf模型变量里默认没有直接暴露principal对象,直接在th表达式里写${principal.username}自然会拿到null;如果用户处于未登录状态,认证对象本身就是null,也会触发空指针报错。 - 缺少权限控制:删除操作属于登录后才可用的功能,原代码没有给删除按钮加登录态校验,未登录用户访问页面时也会执行用户名取值逻辑,进一步放大了空值问题。
修复方案
- 修正DOM取值逻辑
如果要通过jQuery拿到span里渲染好的用户名,先给span加上对应id,再调用.text()方法获取标签内部文本:
<!-- 给用户名span添加id --> <span id="emailTarget" sec:authentication="principal.username">User</span>
// 取span内渲染完成的用户名,去除首尾空格 let email = $("#emailTarget").text().trim(); $('#changeMe').prop("href", "/delete?email="+email)
- 修正Thymeleaf内联脚本取值语法
如果要直接在脚本内通过Thymeleaf表达式拿认证信息,使用Spring Security扩展提供的内置对象#authentication取值,同时加默认值兜底,避免空指针:
<script type="text/javascript" th:inline="javascript"> // 未登录场景下自动使用空字符串作为兜底值,不会触发模板解析错误 let email = /*[[${#authentication.getPrincipal().getUsername()}]]*/ ''; $('#changeMe').prop("href", "/delete?email="+email) </script>
- 加登录态权限控制
给删除按钮添加sec:authorize="isAuthenticated()"属性,未登录用户访问页面时不会渲染这个按钮,从根源避免未登录场景下的空值问题。
内容的提问来源于stack exchange,提问作者metatrone
相关产品推荐
相关产品推荐

