将含有websocket的SSM项目部署在Weblogic上面,遇到websocket报错如下
java.lang.ClassCastException: org.springframework.session.web.http.SessionReposi toryFilter$SessionRepositoryRequestWrapper$HttpSessionWrapper cannot be cast to weblogic.servlet.security.internal.SessionSecurityData at weblogic.servlet.security.internal.SecurityModule.getCurrentUser(Secu rityModule.java:197) at weblogic.websocket.tyrus.TyrusServletFilter.doFilter(TyrusServletFilt er.java:167) at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.ja va:78) at com.codahale.metrics.servlet.AbstractInstrumentedFilter.doFilter(Abst ractInstrumentedFilter.java:104) at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.ja va:78) Truncated. see log file for complete stacktrace
直观看,是类型转换错误,发生在weblogic的对象中.
可以搜到的类似问题中,都在尝试将/websocket排除在web.xml中的filter之外,而这边遇到的项目,这么做不现实,所以开始分析该bug
思路:
1.DEBUG获取到出错的 filterChain,发现weblogic自行追加了一个fitler,叫TyrusServletFilter,错误在这个Filter中发生.
2.从weblogic的安装目录中找到TyrusServletFilter的jar包,引入到IDEA,准备反编译看源码
3.根据上面的报错,错误发生在SecurityModule中的getCurrentUser中,打断点DEBUG确定到具体行:
上图是TyrusServletFilter的反编译代码,红线处可看到,进入出错代码的条件
具体出错行,非常明了的强制转换,转换了request.getSession的结果
4.在该处进行DEBUG(居然真的能DEBUG),发现正常的时候,getCurrentUser的request参数是一个ServletRequestImpl对象(来自weblogic),而出错时,request参数是一个SecurityContextHolderAwareRequestWrapper对象,故确定问题是包装问题(即包装器的getSession并没有代理好其内部request的getSession方法)
解决方案:
确定了出错的包装类后,要重写并覆盖该类,代码如下:
该包装类原本并没有覆盖getSession方法,这里添加覆盖,加入和TyrusServletFilter中相同的判断逻辑,
如果符合条件,则就地一层一层拆包装,拆到ServletRequestImpl为止.再调用ServletRequestImpl的getSession返回出结果.
如果不符合条件,不做改变.
这大概是解决该问题的另一套思路了.
我这里由于使用了SpringSecurity,故要做修改的是SecurityContextHolderAwareRequestWrapper
如果使用了别的filter,那么可以覆盖其他的requestWrapper
当然最简单的方案还是在web.xml中将/websocket排除在所有filter外,如果不可以的话,再按我这个思路做改动