在开发的时候遇到一个十分诡异的情况,shiro打开了记住我的功能,请求的cookie也带上了,但是却总是抛出用户未登录的异常。无奈打开了所有的日志,发现每次请求都会抛出下面这个WARN
排查调试之后我发现我遇到的序列化失败的原因就是因为使用了undertow当服务器,解决方法也很简单,换回tomcat就可以了。。。最简单粗暴的方法。
下面列出我抛出的警告和发现问题的大致过程。
2019-07-10 20:19:05 WARN org.apache.shiro.mgt.AbstractRememberMeManager(onRememberedPrincipalFailure:449) - There was a failure while trying to retrieve remembered principals. This could be due to a configuration problem or corrupted principals. This could also be due to a recently changed encryption key, if you are using a shiro.ini file, this property would be 'securityManager.rememberMeManager.cipherKey' see: http://shiro.apache.org/web.html#Web-RememberMeServices. The remembered identity will be forgotten and not used for this request.
2019-07-10 20:19:05 WARN org.apache.shiro.mgt.DefaultSecurityManager(getRememberedIdentity:617) - Delegate RememberMeManager instance of type [org.apache.shiro.web.mgt.CookieRememberMeManager] threw an exception during getRememberedPrincipals().
org.apache.shiro.io.SerializationException: Unable to deserialize argument byte array.
at org.apache.shiro.io.DefaultSerializer.deserialize(DefaultSerializer.java:82) ~[shiro-core-1.4.0.jar:1.4.0]
at org.apache.shiro.mgt.AbstractRememberMeManager.deserialize(AbstractRememberMeManager.java:507) ~[shiro-core-1.4.0.jar:1.4.0]
at org.apache.shiro.mgt.AbstractRememberMeManager.convertBytesToPrincipals(AbstractRememberMeManager.java:421) ~[shiro-core-1.4.0.jar:1.4.0]
at org.apache.shiro.mgt.AbstractRememberMeManager.getRememberedPrincipals(AbstractRememberMeManager.java:386) ~[shiro-core-1.4.0.jar:1.4.0]
at org.apache.shiro.mgt.DefaultSecurityManager.getRememberedIdentity(DefaultSecurityManager.java:612) [shiro-core-1.4.0.jar:1.4.0]
at org.apache.shiro.mgt.DefaultSecurityManager.resolvePrincipals(DefaultSecurityManager.java:500) [shiro-core-1.4.0.jar:1.4.0]
at org.apache.shiro.mgt.DefaultSecurityManager.createSubject(DefaultSecurityManager.java:346) [shiro-core-1.4.0.jar:1.4.0]
at org.apache.shiro.subject.Subject$Builder.buildSubject(Subject.java:845) [shiro-core-1.4.0.jar:1.4.0]
at org.apache.shiro.web.subject.WebSubject$Builder.buildWebSubject(WebSubject.java:148) [shiro-web-1.4.0.jar:1.4.0]
at org.apache.shiro.web.servlet.AbstractShiroFilter.createSubject(AbstractShiroFilter.java:292) [shiro-web-1.4.0.jar:1.4.0]
at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:359) [shiro-web-1.4.0.jar:1.4.0]
at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125) [shiro-web-1.4.0.jar:1.4.0]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109) [spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) [spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) [spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:64) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:132) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) [undertow-core-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46) [undertow-core-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60) [undertow-core-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43) [undertow-core-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) [undertow-core-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) [undertow-core-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104) [undertow-servlet-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:336) [undertow-core-1.4.25.Final.jar:1.4.25.Final]
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830) [undertow-core-1.4.25.Final.jar:1.4.25.Final]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_171]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_171]
at java.lang.Thread.run(Thread.java:748) [?:1.8.0_171]
Caused by: java.io.StreamCorruptedException: invalid type code: 00
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1599) ~[?:1.8.0_171]
at java.io.ObjectInputStream.readArray(ObjectInputStream.java:1948) ~[?:1.8.0_171]
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1565) ~[?:1.8.0_171]
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2285) ~[?:1.8.0_171]
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2209) ~[?:1.8.0_171]
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2067) ~[?:1.8.0_171]
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1571) ~[?:1.8.0_171]
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431) ~[?:1.8.0_171]
at java.util.HashSet.readObject(HashSet.java:341) ~[?:1.8.0_171]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_171]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_171]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_171]
at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_171]
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1158) ~[?:1.8.0_171]
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2176) ~[?:1.8.0_171]
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2067) ~[?:1.8.0_171]
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1571) ~[?:1.8.0_171]
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431) ~[?:1.8.0_171]
at java.util.HashMap.readObject(HashMap.java:1409) ~[?:1.8.0_171]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_171]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_171]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_171]
at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_171]
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1158) ~[?:1.8.0_171]
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2176) ~[?:1.8.0_171]
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2067) ~[?:1.8.0_171]
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1571) ~[?:1.8.0_171]
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2285) ~[?:1.8.0_171]
at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:561) ~[?:1.8.0_171]
at org.apache.shiro.subject.SimplePrincipalCollection.readObject(SimplePrincipalCollection.java:295) ~[shiro-core-1.4.0.jar:1.4.0]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_171]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_171]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_171]
at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_171]
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1158) ~[?:1.8.0_171]
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2176) ~[?:1.8.0_171]
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2067) ~[?:1.8.0_171]
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1571) ~[?:1.8.0_171]
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431) ~[?:1.8.0_171]
at org.apache.shiro.io.DefaultSerializer.deserialize(DefaultSerializer.java:77) ~[shiro-core-1.4.0.jar:1.4.0]
... 57 more
尝试了其给的官网方案,并没有什么用处。无奈只能打开调试,由于是解码的地方出错,在排除掉序列化后的数据格式错误的猜想之后,剩下的可能性也只有拿到的数据出错了。
十分神奇的是我用tomcat当服务器时候就不会,而是用undertow就会出现这种情况,暂时没找出为什么使用undertow会出现这个问题,没有精力再继续往上调试了,在这里先记录一下。
2020.4.22 重新整理
通过调试,发现shiro加载要序列化的类的时候,不同服务器使用的class loader 是不一样的。
当使用tomcat的时候,classloader为TomcatEmbeddedWebappClassLoader
,而使用undertow的时候,classloader为AppClassLoader
,前者可以找到[C
(char数组类型,原生类型),而后者不行,会抛出ClassNotFound错误,因此导致cookie还原登录信息失败。
由于系统的ClassLoader的loadClass无法加载原生类型,而forName可以,因此我认为可以对shiro源码的ClassUtil类的代码进行一点修改,使用Class.forName函数(PS:修改ClassUtil后的shiro-lang包)。
官方的1.6.1、2.0.0版本已修复
private static abstract class ExceptionIgnoringAccessor implements ClassLoaderAccessor {
public Class loadClass(String fqcn) {
Class clazz = null;
ClassLoader cl = getClassLoader();
if (cl != null) {
try {
// 使用forName加载
clazz = Class.forName(fqcn,false,cl);
// clazz = cl.loadClass(fqcn);
} catch (ClassNotFoundException e) {
if (log.isTraceEnabled()) {
log.trace("Unable to load clazz named [" + fqcn + "] from class loader [" + cl + "]");
}
}
}
return clazz;
}
// 省略其余代码
}