在 sec:authorize thymeleaf 标记中连接字符串和变量
Posted
技术标签:
【中文标题】在 sec:authorize thymeleaf 标记中连接字符串和变量【英文标题】:Concatenate string and variable in sec:authorize thymeleaf tag 【发布时间】:2015-03-01 14:41:10 【问题描述】:我正在尝试使用 thymeleaf 标签 sec:authorize :
sec:authorize="hasPermission(#user, 'listagem_$item.simpleName')"
但是当我部署和运行项目时,字符串listagem_$item.simpleName
没有被处理,内容也没有显示出来。
任何人都知道能够在 sec:authorize 标记内进行这种连接的正确方法吗?
更新
尝试后:
<span th:text="|hasPermission(#user, 'listagem_$item.simpleName')|"></span>
看到我得到了正确的结果,我尝试将表达式:|hasPermission(#user, 'listagem_$item.simpleName')|
传输到 sec:authorize 标记,如下所示:
<li th:each="item : $menu" sec:authorize="|hasPermission(#user, 'listagem_$item.simpleName')|">
但我收到此错误:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: An error happened trying to parse Spring Security access expression "|hasPermission(#user, 'listagem_$item.simpleName')|" (private/home:40)] with root cause
org.springframework.expression.spel.SpelParseException: EL1069E:(pos 0): missing expected character '|'
at org.springframework.expression.spel.standard.Tokenizer.process(Tokenizer.java:194)
at org.springframework.expression.spel.standard.Tokenizer.<init>(Tokenizer.java:84)
at org.springframework.expression.spel.standard.InternalSpelExpressionParser.doParseExpression(InternalSpelExpressionParser.java:121)
at org.springframework.expression.spel.standard.SpelExpressionParser.doParseExpression(SpelExpressionParser.java:60)
at org.springframework.expression.spel.standard.SpelExpressionParser.doParseExpression(SpelExpressionParser.java:32)
at org.springframework.expression.common.TemplateAwareExpressionParser.parseExpression(TemplateAwareExpressionParser.java:76)
at org.springframework.expression.common.TemplateAwareExpressionParser.parseExpression(TemplateAwareExpressionParser.java:62)
at org.thymeleaf.extras.springsecurity3.auth.AuthUtils.authorizeUsingAccessExpression(AuthUtils.java:186)
at org.thymeleaf.extras.springsecurity3.dialect.processor.AuthorizeAttrProcessor.isVisible(AuthorizeAttrProcessor.java:100)
at org.thymeleaf.processor.attr.AbstractConditionalVisibilityAttrProcessor.processAttribute(AbstractConditionalVisibilityAttrProcessor.java:58)
at org.thymeleaf.processor.attr.AbstractAttrProcessor.doProcess(AbstractAttrProcessor.java:87)
at org.thymeleaf.processor.AbstractProcessor.process(AbstractProcessor.java:212)
at org.thymeleaf.dom.Node.applyNextProcessor(Node.java:1016)
at org.thymeleaf.dom.Node.processNode(Node.java:971)
at org.thymeleaf.dom.NestableNode.computeNextChild(NestableNode.java:672)
at org.thymeleaf.dom.NestableNode.doAdditionalProcess(NestableNode.java:655)
at org.thymeleaf.dom.Node.processNode(Node.java:990)
at org.thymeleaf.dom.NestableNode.computeNextChild(NestableNode.java:672)
at org.thymeleaf.dom.NestableNode.doAdditionalProcess(NestableNode.java:655)
at org.thymeleaf.dom.Node.processNode(Node.java:990)
at org.thymeleaf.dom.NestableNode.computeNextChild(NestableNode.java:672)
at org.thymeleaf.dom.NestableNode.doAdditionalProcess(NestableNode.java:655)
at org.thymeleaf.dom.Node.processNode(Node.java:990)
at org.thymeleaf.dom.NestableNode.computeNextChild(NestableNode.java:672)
at org.thymeleaf.dom.NestableNode.doAdditionalProcess(NestableNode.java:655)
at org.thymeleaf.dom.Node.processNode(Node.java:990)
at org.thymeleaf.dom.NestableNode.computeNextChild(NestableNode.java:672)
at org.thymeleaf.dom.NestableNode.doAdditionalProcess(NestableNode.java:655)
at org.thymeleaf.dom.Node.processNode(Node.java:990)
at org.thymeleaf.dom.NestableNode.computeNextChild(NestableNode.java:672)
at org.thymeleaf.dom.NestableNode.doAdditionalProcess(NestableNode.java:655)
at org.thymeleaf.dom.Node.processNode(Node.java:990)
at org.thymeleaf.dom.NestableNode.computeNextChild(NestableNode.java:672)
at org.thymeleaf.dom.NestableNode.doAdditionalProcess(NestableNode.java:655)
at org.thymeleaf.dom.Node.processNode(Node.java:990)
at org.thymeleaf.dom.Document.process(Document.java:93)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1155)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1060)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1011)
at org.thymeleaf.spring4.view.ThymeleafView.renderFragment(ThymeleafView.java:335)
at org.thymeleaf.spring4.view.ThymeleafView.render(ThymeleafView.java:190)
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1228)
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1011)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:955)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:146)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:537)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1085)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:658)
at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:222)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1556)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1513)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
我在这里缺少什么?
【问题讨论】:
你试过 $'listagem_'+item.simpleName 吗? @MartinFrey 是的,也不行。 listagem_ 是您要传递的单独变量吗?如果是,那么正确的 concat 将是 $'listagem_'+$item.simpleName @Aeseir 这不起作用。我得到错误:org.springframework.expression.spel.SpelParseException: EL1043E:(pos 22): Unexpected token. Expected 'rparen())' but was 'lcurly()'
.
listagem_ 你传入的变量也是如此,因为我以前在代码中没有见过它
【参考方案1】:
在我的例子中,我在变量中包含了用户角色名称。我终于设法让它工作了
<div th:if="$#authorization.expression('hasRole('''+objectDTO.roleName+''')')"></div>
这个想法来自这里(注意双单引号以转义名称)https://github.com/thymeleaf/thymeleaf-extras-springsecurity#using-the-expression-utility-objects
【讨论】:
【参考方案2】:我在字符串连接和 thymeleaf 中的 sec:authorize
属性方面遇到了类似问题,尽管情况不完全相同,但它可能对遇到此问题的其他人有用。
我想根据用户(主体)权限限制某些元素的可见性。在此示例中,我有一个页面显示客户详细信息,其他用户可以访问该页面,但只有“客户经理”可以看到电子邮件地址。当我对权限字符串进行硬编码时,一切正常:
<th:block sec:authorize="hasAuthority('VIEW_USER_EMAIL')">
...
</th:block>
我宁愿不要在我的模板上散布随机字符串,因为它们可能在某些时候被重构并且容易出错。我有一个包含public static final String
字段负载的类,称为Permission
。我本来希望使用enum
,但无法从@PreAuthorize
或@Secured
之类的注释中引用,我在应用程序see this question for more details 的其他地方使用了这些注释。
解决方案是从模板的表达式中引用我的Permission
类。这可以使用SpEL type operator T()
来实现。通过尝试错误直到最终成功,我在正确获取语法时遇到了很多麻烦:
<th:block sec:authorize="hasAuthority(T(com.example.app.Permission).VIEW_USER_EMAIL)">
...
</th:block>
这意味着如果Permission
类或VIEW_USER_EMAIL
字段不存在或有错字,将引发异常。与静默失败并可能拒绝访问相比,这是一个巨大的改进。
以下是我之前的一些尝试:
sec:authorize="hasAuthority('$T(com.example.app.Permission).VIEW_USER_EMAIL')"
sec:authorize="hasAuthority($T(com.example.app.Permission).VIEW_USER_EMAIL)"
sec:authorize="$hasAuthority('T(com.example.app.Permission).VIEW_USER_EMAIL')"
它看起来像 thymeleaf Spring Security Dialect 从您传入的任何表达式中剥离前导 $
和尾随 并尝试评估整个表达式,这就是为什么任何形式的连接都可能不起作用的原因。
供参考,这是我的Permission
课程:
public final class Permission
public static final String VIEW_USER_EMAIL = "VIEW_USER_EMAIL";
【讨论】:
我还遇到了另一种情况,我需要在表达式中使用变量并且无法转义它,解决方案是使用三个单引号:th:if="$#authorization.expression('hasAuthority(''' + someObject.permissionString + ''')')"
以上是关于在 sec:authorize thymeleaf 标记中连接字符串和变量的主要内容,如果未能解决你的问题,请参考以下文章
sec:authorize 在 thymeleaf 视图中为 isAuthenticated() 和 isAnonymous() 返回 true
sec:authorize 在 thymeleaf 视图中为 isAuthenticated() 和 isAnonymous() 返回 true
sec:authorize 在 Spring 启动应用程序中使用 Thymeleaf 时未按预期工作
thymeleaf.extras.springsecurity4 sec:authorize 不起作用
Spring Security - Thymeleaf - 我可以在 sec:authorize 标签中评估 SPEL 表达式吗?