Spring 引导和安全升级到 2.7.0 抛出 org.springframework.security.core.context.SecurityContextImpl;本地 class 不兼容:

Spring Boot and Security upgrade to 2.7.0 throws org.springframework.security.core.context.SecurityContextImpl; local class incompatible:

当我将 Spring 引导项目从 2.6.7 升级到 2.7.0 时,我看到以下错误。我推测错误是在发现与版本 5.6.x 不兼容的 Bean 时发生的。但是当我检查 Spring Security starter 时,我看到它使用 Spring Security 5.7

Caused by: org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is java.io.InvalidClassException: org.springframework.security.core.context.SecurityContextImpl; local class incompatible: stream classdesc serialVersionUID = 560, local class serialVersionUID = 570

完整堆栈跟踪:

nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is java.io.InvalidClassException: org.springframework.security.core.context.SecurityContextImpl; local class incompatible: stream classdesc serialVersionUID = 560, local class serialVersionUID = 570
    at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:47) ~[spring-core-5.3.20.jar:5.3.20]
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:192) ~[spring-core-5.3.20.jar:5.3.20]
    at org.springframework.session.jdbc.JdbcIndexedSessionRepository.deserialize(JdbcIndexedSessionRepository.java:628) ~[spring-session-jdbc-2.7.0.jar:2.7.0]
    at org.springframework.session.jdbc.JdbcIndexedSessionRepository.access00(JdbcIndexedSessionRepository.java:133) ~[spring-session-jdbc-2.7.0.jar:2.7.0]
    at org.springframework.session.jdbc.JdbcIndexedSessionRepository$SessionResultSetExtractor.lambda$extractData[=14=](JdbcIndexedSessionRepository.java:898) ~[spring-session-jdbc-2.7.0.jar:2.7.0]
    at org.springframework.session.jdbc.JdbcIndexedSessionRepository.get(JdbcIndexedSessionRepository.java:650) ~[spring-session-jdbc-2.7.0.jar:2.7.0]
    at org.springframework.session.jdbc.JdbcIndexedSessionRepository$JdbcSession.getAttribute(JdbcIndexedSessionRepository.java:728) ~[spring-session-jdbc-2.7.0.jar:2.7.0]
    at org.springframework.session.PrincipalNameIndexResolver.resolveIndexValueFor(PrincipalNameIndexResolver.java:46) ~[spring-session-core-2.7.0.jar:2.7.0]
    at org.springframework.session.SingleIndexResolver.resolveIndexesFor(SingleIndexResolver.java:48) ~[spring-session-core-2.7.0.jar:2.7.0]
    at org.springframework.session.DelegatingIndexResolver.resolveIndexesFor(DelegatingIndexResolver.java:49) ~[spring-session-core-2.7.0.jar:2.7.0]
    at org.springframework.session.jdbc.JdbcIndexedSessionRepository$JdbcSession.lambda$save(JdbcIndexedSessionRepository.java:840) ~[spring-session-jdbc-2.7.0.jar:2.7.0]
    at org.springframework.transaction.support.TransactionOperations.lambda$executeWithoutResult[=14=](TransactionOperations.java:68) ~[spring-tx-5.3.20.jar:5.3.20]
    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.20.jar:5.3.20]
    at org.springframework.transaction.support.TransactionOperations.executeWithoutResult(TransactionOperations.java:67) ~[spring-tx-5.3.20.jar:5.3.20]
    at org.springframework.session.jdbc.JdbcIndexedSessionRepository$JdbcSession.save(JdbcIndexedSessionRepository.java:837) ~[spring-session-jdbc-2.7.0.jar:2.7.0]
    at org.springframework.session.jdbc.JdbcIndexedSessionRepository$JdbcSession.access0(JdbcIndexedSessionRepository.java:665) ~[spring-session-jdbc-2.7.0.jar:2.7.0]
    at org.springframework.session.jdbc.JdbcIndexedSessionRepository.save(JdbcIndexedSessionRepository.java:422) ~[spring-session-jdbc-2.7.0.jar:2.7.0]
    at org.springframework.session.jdbc.JdbcIndexedSessionRepository.save(JdbcIndexedSessionRepository.java:133) ~[spring-session-jdbc-2.7.0.jar:2.7.0]
    at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.commitSession(SessionRepositoryFilter.java:226) ~[spring-session-core-2.7.0.jar:2.7.0]
    at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.access0(SessionRepositoryFilter.java:193) ~[spring-session-core-2.7.0.jar:2.7.0]
    at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:145) ~[spring-session-core-2.7.0.jar:2.7.0]
    at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:82) ~[spring-session-core-2.7.0.jar:2.7.0]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) ~[spring-web-5.3.20.jar:5.3.20]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:711) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:461) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:385) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:313) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:403) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:249) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.catalina.core.StandardHostValve.throwable(StandardHostValve.java:344) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:769) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:890) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1743) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]

好的,事实证明 Spring 使用版本 2 创建的会话。6.x 与 2.7.0 不兼容。为了解决这个问题,所有现有的用户会话都应该失效并要求他们重新登录。这可以通过两种方式完成。

  1. 推出 Spring Boot 2.7.0 版本更改在下班时间,当最低用户登录数时。
  2. 或者设置一个特定的停机时间并使用 Spring Boot 2.7.0
  3. 部署新版本

无论如何让用户知道他们需要重新登录