Oauth2中refresh_token刷新token异常No AuthenticationProvider found for PreAuthenticatedAuthenticationToken

报错如下:

org.springframework.security.authentication.ProviderNotFoundException: No AuthenticationProvider found for org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
	at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:227) ~[spring-security-core-5.2.5.RELEASE.jar:5.2.5.RELEASE]
	at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:501) ~[spring-security-config-5.2.5.RELEASE.jar:5.2.5.RELEASE]
	at com.test.oauth.service.impl.CustomTokenServices.refreshAccessToken(CustomTokenServices.java:141) ~[classes/:na]
	at com.test.oauth.service.impl.CustomTokenServices$$FastClassBySpringCGLIB$$a7dec4f7.invoke(<generated>) ~[classes/:na]
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771) ~[spring-aop-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749) ~[spring-aop-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.transaction.interceptor.TransactionInterceptor$$Lambda$1311/534220233.proceedWithInvocation(Unknown Source) ~[na:na]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:367) ~[spring-tx-5.2.7.RELEASE.jar:5.2.7.RELEASE]
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) ~[spring-tx-5.2.7.RELEASE.jar:5.2.7.RELEASE]
......

解决方案:

只需把PreAuthenticatedAuthenticationProvider添加到authenticationManager中就可以了。可通过AuthenticationManagerBuilder添加到authenticationManager中。

这里记录下两种添加方式:
第一种:在WebSecurityConfigurerAdapter.class里添加

import com.test.oauth.service.impl.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;

@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 2)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomUserDetailsService customUserDetailsService;
    @Bean
    public PreAuthenticatedAuthenticationProvider preAuthenticatedAuthenticationProvider() {
        PreAuthenticatedAuthenticationProvider preAuthenticatedAuthenticationProvider = new PreAuthenticatedAuthenticationProvider();
        preAuthenticatedAuthenticationProvider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<>(customUserDetailsService));
        return preAuthenticatedAuthenticationProvider;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(preAuthenticatedAuthenticationProvider());
    }
}

第二种:在GlobalAuthenticationConfigurerAdapter.class里添加

import com.test.oauth.service.impl.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;

@Configuration
public class GlobalAuthenticationConfig extends GlobalAuthenticationConfigurerAdapter {

    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    @Bean
    public PreAuthenticatedAuthenticationProvider preAuthenticatedAuthenticationProvider() {
        PreAuthenticatedAuthenticationProvider preAuthenticatedAuthenticationProvider = new PreAuthenticatedAuthenticationProvider();
        preAuthenticatedAuthenticationProvider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<>(customUserDetailsService));
        return preAuthenticatedAuthenticationProvider;
    }

    @Override
    public void init(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(preAuthenticatedAuthenticationProvider());
    }

}

然后再自己实现CustomUserDetailsService.loadUserByUsername(String userName)

import com.test.oauth.domain.User;
import com.test.oauth.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;

import java.util.Collections;

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) {
        User user = userService.queryUserByName(username);
        if (user == null) {
            throw new AuthenticationServiceException("username.notFound");
        }
        return new org.springframework.security.core.userdetails.User(user.getName(), user.getPassword(), Collections.emptyList());
    }
}

 注意:如果有多个provider不能分开在WebSecurityConfigurerAdapter子类WebSecurityConfig 和 GlobalAuthenticationConfigurerAdapter子类GlobalAuthenticationConfig里配,

因为,AuthenticationManager是以最后一次WebSecurityConfigurerAdapter类调用authenticationManager() 为准的。每次调用authenticationManager()方法都是按@order配置的顺序调用一次本类或子类的configure(AuthenticationManagerBuilder auth)方法,并根据属性disableLocalConfigureAuthenticationBldr为true时调用GlobalAuthenticationConfigurerAdapter的init(AuthenticationManagerBuilder auth)装配builder并赋值AuthenticationManager,为false时使用子类的configure(AuthenticationManagerBuilder auth)装配的builder赋值AuthenticationManager。

authenticationManager()方法源码如下:(在org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter里

// 按@Order配置的顺序加载AuthenticationManager,以最后一次为准
protected AuthenticationManager authenticationManager() throws Exception {
        if (!this.authenticationManagerInitialized) {
            // 调用WebSecurityConfigurerAdapter本类或其子类的configure,
            // 子类的configure装配的builder会放到this.localConfigureAuthenticationBldr对象里
            // 本类的configure不装配builder,而是设置disableLocalConfigureAuthenticationBldr属性,让GlobalAuthenticationConfigurerAdapter装配
            this.configure(this.localConfigureAuthenticationBldr);
            if (this.disableLocalConfigureAuthenticationBldr) {
                // 会调用GlobalAuthenticationConfigurerAdapter的init方法装配builder
                this.authenticationManager = this.authenticationConfiguration.getAuthenticationManager();
            } else {
                // 使用子类装配的builder
                this.authenticationManager = (AuthenticationManager)this.localConfigureAuthenticationBldr.build();
            }

            this.authenticationManagerInitialized = true;
        }

        return this.authenticationManager;
    }

具体BUG原因分析请参考避坑指南(一):Spring Security Oauth2中refresh_token刷新access_token异常_银河架构师的博客-CSDN博客_oauth2 refreshtoken 用法

  • 2
    点赞
  • 1
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:书香水墨 设计师:CSDN官方博客 返回首页
评论

打赏作者

L9009121314

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值