如何覆盖 spring 框架 bean?

how override spring framework beans?

我想自定义spring安全提供的OAuth授权服务器的部分代码。负责生成 /oauth/authorize 的代码是一个名为 AuthorizationEndpoint 的 bean。在 AuthorizationServerEndpointsConfiguration class 中,以下代码创建 AuthorizationEndpoint class:

的 bean
@Bean
public AuthorizationEndpoint authorizationEndpoint() throws Exception {
    AuthorizationEndpoint authorizationEndpoint = new AuthorizationEndpoint();
    FrameworkEndpointHandlerMapping mapping = getEndpointsConfigurer().getFrameworkEndpointHandlerMapping();
    authorizationEndpoint.setUserApprovalPage(extractPath(mapping, "/oauth/confirm_access"));
    authorizationEndpoint.setProviderExceptionHandler(exceptionTranslator());
    authorizationEndpoint.setErrorPage(extractPath(mapping, "/oauth/error"));
    authorizationEndpoint.setTokenGranter(tokenGranter());
    authorizationEndpoint.setClientDetailsService(clientDetailsService);
    authorizationEndpoint.setAuthorizationCodeServices(authorizationCodeServices());
    authorizationEndpoint.setOAuth2RequestFactory(oauth2RequestFactory());
    authorizationEndpoint.setOAuth2RequestValidator(oauth2RequestValidator());
    authorizationEndpoint.setUserApprovalHandler(userApprovalHandler());
    return authorizationEndpoint;
}

我想用一个新的自定义 bean 覆盖它。我创建了一个扩展 AuthorizationEndpoint 的 class。现在我已经在这个新的 class.

中粘贴了相同的代码
public class AuthorizationEndpointCustom extends AuthorizationEndpoint {

正在创建 bean:

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    AuthorizationServerEndpointsConfiguration asec;


    @Bean
//  @Order(value = Ordered.LOWEST_PRECEDENCE)
    @Primary
    public AuthorizationEndpoint authorizationEndpoint () {

        AuthorizationEndpointCustom authorizationEndpoint = new AuthorizationEndpointCustom();
        FrameworkEndpointHandlerMapping mapping = asec.getEndpointsConfigurer().getFrameworkEndpointHandlerMapping();
        authorizationEndpoint.setUserApprovalPage(extractPath(mapping, "/oauth/confirm_access"));
        authorizationEndpoint.setProviderExceptionHandler(asec.getEndpointsConfigurer().getExceptionTranslator());
        authorizationEndpoint.setErrorPage(extractPath(mapping, "/oauth/error"));
        authorizationEndpoint.setTokenGranter(asec.getEndpointsConfigurer().getTokenGranter());
        authorizationEndpoint.setClientDetailsService(clientDetailsService);
        authorizationEndpoint.setAuthorizationCodeServices(asec.getEndpointsConfigurer().getAuthorizationCodeServices());
        authorizationEndpoint.setOAuth2RequestFactory(asec.getEndpointsConfigurer().getOAuth2RequestFactory());
        authorizationEndpoint.setOAuth2RequestValidator(asec.getEndpointsConfigurer().getOAuth2RequestValidator());
        authorizationEndpoint.setUserApprovalHandler(asec.getEndpointsConfigurer().getUserApprovalHandler());

        return authorizationEndpoint;
    }

    private String extractPath(FrameworkEndpointHandlerMapping mapping, String page) {
        String path = mapping.getPath(page);
        if (path.contains(":")) {
            return path;
        }
        return "forward:" + path;
    }

当我尝试为这个新 class 创建一个 bean 时,我遇到了以下错误:

APPLICATION FAILED TO START


Description:

The bean 'authorizationEndpoint', defined in org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration, could not be registered. A bean with that name has already been defined in class path resource [com/example/demo/AuthorizationServerConfig.class] and overriding is disabled.

Action:

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

通过将建议的配置添加到 application.properties,错误消失了。但新 bean 不会替换框架 bean。在我的代码的另一部分中,我从 applicationContext 访问了 AuthorizationEndpoint。我调用了这个对象的 .getClass() ,它是框架中的同一个 bean:

"org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint"

如何强制 spring 使用我的 bean?

你需要一个Configurationclass

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public AuthorizationEndpoint authorizationEndpoint() {
        if(...) return new AuthorizationEndpoint();
        else return new AuthorizationEndpointCustom();
    }
}

我红了一篇关于重写 beans 的文章,它看起来很乱而且不可预测。 read here 最好避免这样做。禁用框架 bean 的解决方案在于排除创建它的配置 class。但这意味着我们必须自己实现漏洞。

@SpringBootApplication(exclude=<AuthorizationServerEndpointsConfiguration>.class)

但是覆盖框架端点的解决方案很多 easier.all 我们必须做的是创建一个控制器映射 /oauth/authorize

Customizing the UI Most of the Authorization Server endpoints are used primarily by machines, but there are a couple of resource that need a UI and those are the GET for /oauth/confirm_access and the HTML response from /oauth/error. They are provided using whitelabel implementations in the framework, so most real-world instances of the Authorization Server will want to provide their own so they can control the styling and content. All you need to do is provide a Spring MVC controller with @RequestMappings for those endpoints, and the framework defaults will take a lower priority in the dispatcher. In the /oauth/confirm_access endpoint you can expect an AuthorizationRequest bound to the session carrying all the data needed to seek approval from the user (the default implementation is WhitelabelApprovalEndpoint so look there for a starting point to copy). You can grab all the data from that request and render it however you like, and then all the user needs to do is POST back to /oauth/authorize with information about approving or denying the grant. The request parameters are passed directly to a UserApprovalHandler in the AuthorizationEndpoint so you can interpret the data more or less as you please. The default UserApprovalHandler depends on whether or not you have supplied an ApprovalStore in your AuthorizationServerEndpointsConfigurer (in which case it is an ApprovalStoreUserApprovalHandler) or not (in which case it is a TokenStoreUserApprovalHandler). The standard approval handlers accept the following:

阅读更多 here

还有一个与此主题相关的问题: