Spring Security整合Freemarker时Session对象为空或缺失问题求助
在Freemarker中正确使用Session对象及基于用户角色控制HTML元素
用户尝试通过Session获取Spring Security上下文时遇到引用错误,代码及报错信息如下:
模板代码
<#assign known = Session.SPRING_SECURITY_CONTEXT?? > <#if known> <#assign user = Session.SPRING_SECURITY_CONTEXT.authentication.principal name = user.getUsername() isAdmin = user.isAdmin() currentUserId = user.getId() > <#else> <#assign name = "unknown" isAdmin = false currentUserId = -1 > </#if>
错误信息
freemarker.core.InvalidReferenceException: The following has evaluated to null or missing:
==> Session [in template "parts/security.ftlh" at line 2, column 9]
一、正确使用Session对象的解决方案
出现Session引用为空的核心原因是Freemarker默认不会直接暴露HttpSession对象,需通过以下方式修正:
1. 切换为小写session变量访问
多数Spring环境中,Freemarker默认暴露小写的session变量而非大写Session,直接替换变量名即可:
<#assign known = session.SPRING_SECURITY_CONTEXT?? >
2. 通过Spring Security内置变量简化代码
Spring Security整合Freemarker时,会自动将authentication对象暴露到模板中,无需手动从Session获取,代码可简化为:
<#if authentication??> <#assign user = authentication.principal name = user.username <!-- 等价于user.getUsername(),Freemarker支持直接访问属性 --> isAdmin = user.admin currentUserId = user.id > <#else> <#assign name = "unknown" isAdmin = false currentUserId = -1 > </#if>
3. 配置RequestContext暴露Session
若上述方法无效,可在Spring配置中开启RequestContext支持,通过requestContextAttribute访问Session:
@Bean public FreeMarkerConfigurer freeMarkerConfigurer() { FreeMarkerConfigurer configurer = new FreeMarkerConfigurer(); configurer.setTemplateLoaderPath("/WEB-INF/templates/"); Properties props = new Properties(); // 暴露request上下文变量,命名为rc props.setProperty("requestContextAttribute", "rc"); return configurer; }
模板中通过rc.session访问:
<#assign known = rc.session.SPRING_SECURITY_CONTEXT?? >
二、基于用户角色隐藏HTML元素的其他方法
除手动判断角色外,还有更简洁的方案:
1. 使用Spring Security标签库
引入Spring Security标签库依赖后,可在Freemarker中直接使用角色授权指令:
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> </dependency>
模板中使用:
<#assign security=JspTaglibs["http://www.springframework.org/security/tags"]> <!-- 仅ADMIN角色可见 --> <@security.authorize access="hasRole('ADMIN')"> <button>管理员专属操作</button> </@security.authorize> <!-- 仅已登录用户可见 --> <@security.authorize access="isAuthenticated()"> <span>欢迎,${authentication.name}</span> </@security.authorize>
2. 自定义Freemarker角色指令
若需要更灵活的逻辑,可自定义指令封装角色判断:
public class RoleAuthorizeDirective implements TemplateDirectiveModel { @Override public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); String role = params.get("role").toString(); // 判断用户是否拥有目标角色 boolean hasRole = auth != null && auth.getAuthorities().stream() .anyMatch(a -> a.getAuthority().equals("ROLE_" + role)); if (hasRole && body != null) { body.render(env.getOut()); } } }
配置到Freemarker后,模板中使用:
<@roleAuthorize role="ADMIN"> <div>管理员专属内容</div> </@roleAuthorize>
内容的提问来源于stack exchange,提问作者user2749570
相关产品推荐
相关产品推荐

