Spring-security-oauth2权限框架使用RedisTokenStore的坑和跨坑

探讨在Spring Security OAuth2框架中使用Redis存储令牌时遇到的序列化和反序列化异常,深入分析问题原因并提供自定义JdkSerializationStrategy的解决方案。
部署运行你感兴趣的模型镜像

RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);

在spring-security-oauth2鉴权框架中,使用redis存储令牌时,涉及序列化和反序列化,

RedisTokenStore内置JdkSerializationStrategy,通过ALLOWED_CLASSES只有限制了支持反序列化的类型,然后就是抛一堆异常如下:

org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload; nested exception is java.io.NotSerializableException: Not allowed to deserialize xx.xx.xx.xx.data.JsonSupportObject
    at org.springframework.security.oauth2.provider.token.store.redis.JdkSerializationStrategy.deserializeInternal(JdkSerializationStrategy.java:66)
    at org.springframework.security.oauth2.provider.token.store.redis.BaseRedisTokenStoreSerializationStrategy.deserialize(BaseRedisTokenStoreSerializationStrategy.java:21)
    at org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore.deserializeAuthentication(RedisTokenStore.java:92)
    at org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore.readAuthentication(RedisTokenStore.java:146)
    at org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore.readAuthentication(RedisTokenStore.java:134)
    at xx.xx.xx.xx.xx.xx.service.impl.SyncDefaultTokenServices.loadAuthentication(SyncDefaultTokenServices.java:231)
    at xx.xx.xx.xx.xx.xx.service.impl.SyncDefaultTokenServices$$FastClassBySpringCGLIB$$e5d5e38e.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:750)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88)
    at xx.xx.xx.xx.xx.xx.springboot.aop.aspect.LogWebStackAspect.arroundLogicMethod(LogWebStackAspect.java:67)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)
    at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
    at xx.xx.xx.xx.xx.SyncDefaultTokenServices$$EnhancerBySpringCGLIB$$400aeeb1.loadAuthentication(<generated>)
    at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager.authenticate(OAuth2AuthenticationManager.java:83)
    at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter(OAuth2AuthenticationProcessingFilter.java:150)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:94)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at xx.xx.xx.xx.xx.filter.CacheMemoryRequestFilter.doFilter(CacheMemoryRequestFilter.java:40)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1579)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.NotSerializableException: Not allowed to deserialize xx.xxx.xx.common.data.JsonSupportObject
    at org.springframework.security.oauth2.provider.token.store.redis.SaferObjectInputStream.resolveClass(SaferObjectInputStream.java:59)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1613)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2000)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1924)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1801)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2000)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1924)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1801)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
    at org.springframework.security.oauth2.provider.token.store.redis.JdkSerializationStrategy.deserializeInternal(JdkSerializationStrategy.java:64)
    ... 77 common frames omitted

接下来触发spring框架异常处理

Caused by: java.lang.IllegalStateException: getInputStream() has already been called for this request

问题原因:

系统中扩展了

DefaultExpiringOAuth2RefreshToken

DefaultOAuth2AccessToken,但是RedisTokenStore的序列化类JdkSerializationStrategy只支持如下几种持久化对象

static {
        List<String> classes = new ArrayList<String>();
        classes.add("java.lang.");
        classes.add("java.util.");
        classes.add("org.springframework.security.");
        ALLOWED_CLASSES = Collections.unmodifiableList(classes);
    }

解决方法:

将JdkSerializationStrategy和SaferObjectInputStream的源码复制出来放在同一个包自己玩。
            RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
            tokenStore.setSerializationStrategy(new JdkSerializationStrategy());
            return tokenStore;

个人认为这种方式粗暴且快。o(^▽^)o

您可能感兴趣的与本文相关的镜像

Llama Factory

Llama Factory

模型微调
LLama-Factory

LLaMA Factory 是一个简单易用且高效的大型语言模型(Large Language Model)训练与微调平台。通过 LLaMA Factory,可以在无需编写任何代码的前提下,在本地完成上百种预训练模型的微调

`spring-security-oauth2` `spring-security-oauth2-client` 是 Spring 框架中用于处理 OAuth 2.0 相关功能的不同模块,二者存在多方面的区别: ### 功能定位 - **`spring-security-oauth2`**:这是早期 Spring 提供的 OAuth 2.0 支持模块,它涵盖了 OAuth 2.0 服务端客户端的完整实现,既可以用来构建 OAuth 2.0 认证服务器资源服务器,也能实现客户端功能。不过该模块从 Spring Boot 2.0 开始不再进行主动维护。 - **`spring-security-oauth2-client`**:是 Spring SecurityOAuth 2.0 客户端提供的专门支持,专注于实现 OAuth 2.0 客户端功能,用于让应用程序能够作为客户端与外部的 OAuth 2.0 授权服务器进行交互,获取访问令牌等操作。 ### 依赖范围 - **`spring-security-oauth2`**:依赖范围较广,包含了构建整个 OAuth 2.0 生态系统的组件,如认证服务器所需的授权端点、令牌端点等的实现,以及资源服务器对令牌的验证等功能。 - **`spring-security-oauth2-client`**:依赖相对单一,主要关注客户端与授权服务器之间的交互流程,例如授权码模式、隐式模式、客户端凭证模式密码模式(在 OAuth 2.1 中部分模式有调整)下的授权请求、令牌获取等操作。 ### 使用场景 - **`spring-security-oauth2`**:适用于需要自行搭建 OAuth 2.0 认证服务器资源服务器的场景,例如企业内部需要构建一套完整的 OAuth 2.0 认证体系,对用户进行身份验证授权管理。 - **`spring-security-oauth2-client`**:适用于应用程序需要作为客户端去访问其他第三方 OAuth 2.0 授权服务器的场景,比如一个 Web 应用需要集成 Google、GitHub 等第三方登录功能。 ### 配置方式 - **`spring-security-oauth2`**:配置相对复杂,需要手动配置大量的 Bean 属性,例如配置授权服务器的令牌存储方式、客户端信息等。 ```java @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("client") .secret("{noop}secret") .authorizedGrantTypes("authorization_code", "refresh_token") .scopes("read", "write") .redirectUris("http://example.com"); } } ``` - **`spring-security-oauth2-client`**:配置较为简单,借助 Spring Boot 的自动配置特性,只需在配置文件中提供必要的客户端信息,如客户端 ID、客户端密钥、授权服务器的端点地址等,就能快速完成客户端的配置。 ```yaml spring: security: oauth2: client: registration: google: client-id: your-client-id client-secret: your-client-secret scope: openid, profile, email provider: google: authorization-uri: https://accounts.google.com/o/oauth2/v2/auth token-uri: https://www.googleapis.com/oauth2/v4/token ``` ### 版本维护 - **`spring-security-oauth2`**:已停止主动维护,官方推荐使用 Spring Security 5.x 及以上版本中提供的 OAuth 2.0 支持来替代。 - **`spring-security-oauth2-client`**:是 Spring Security 持续维护更新的模块,与 Spring 的最新版本保持同步,能够及时支持 OAuth 2.0 的新特性规范。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值