Grails 2升级至5后部署Tomcat9出现Shiro SecurityManager异常
Grails5升级后Tomcat9部署Shiro异常排查方案
问题根因
Tomcat9的类加载机制和Grails嵌入式Tomcat存在差异,导致Shiro的SecurityManager在容器启动阶段还未完成初始化就被调用,虽然应用功能能正常运行,但初始化过程中会抛出org.apache.shiro.UnavailableSecurityManagerException。核心矛盾是容器环境下Spring上下文的加载顺序和嵌入式Tomcat不同,部分组件提前触发了SecurityManager的获取操作。
修复步骤
1. 确认Shiro插件版本适配
Grails5和Java11对Shiro插件版本有明确要求,别沿用Grails2时期的旧插件。打开项目的build.gradle,检查Shiro依赖版本,建议使用适配Grails5的4.x系列稳定版:
implementation "org.grails.plugins:shiro:4.0.0" // 版本以实际兼容情况为准,可参考Grails官方插件库
2. 延迟SecurityManager初始化
在grails-app/conf/spring/resources.groovy中手动声明SecurityManager Bean,并设置延迟初始化,避免容器启动时就强制加载:
import org.apache.shiro.web.mgt.DefaultWebSecurityManager beans = { securityManager(DefaultWebSecurityManager) { realms = ref('yourCustomRealm') // 替换为项目中实际的Realm Bean名称 lazyInit = true } }
这样SecurityManager会在第一次被实际调用时才完成初始化,避开容器启动的早期阶段。
3. 排查Tomcat类加载隔离问题
FreeBSD上的Tomcat可能存在类加载隔离配置,导致Shiro核心类被容器类加载器和应用类加载器重复加载,引发SecurityManager实例无法共享:
- 检查Tomcat的
conf/catalina.properties文件,查看shared.loader配置项是否包含Shiro相关Jar包,如有则删除该配置,确保所有Shiro依赖都存在于应用的WEB-INF/lib目录中。
4. 修正SecurityManager获取逻辑
如果代码中直接使用SecurityUtils.getSecurityManager()静态方法,改成从Spring上下文获取实例,避免因调用时机过早导致的异常:
import org.apache.shiro.mgt.SecurityManager; import org.springframework.web.context.support.WebApplicationContextUtils; // 在Web请求上下文内获取SecurityManager SecurityManager securityManager = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext()) .getBean(SecurityManager.class); SecurityUtils.setSecurityManager(securityManager);
5. 对比依赖树排查冲突
分别查看嵌入式Tomcat和外部Tomcat部署时的依赖差异,找出潜在冲突:
- 执行
./gradlew dependencies生成完整依赖树,检查是否有旧版本Shiro被间接引入。 - 用
./gradlew dependencyInsight --dependency org.apache.shiro:shiro-core命令定位具体依赖来源,排除冲突版本。
内容的提问来源于stack exchange,提问作者susi
相关产品推荐
相关产品推荐

