【问题标题】:how override spring framework beans?如何覆盖spring框架bean?
【发布时间】:2020-06-02 04:04:37
【问题描述】:

我想自定义spring security提供的OAuth授权服务器的一些代码。负责生成 /oauth/authorize 的代码是一个名为 AuthorizationEndpoint 的 bean。在 AuthorizationServerEndpointsConfiguration 类中,以下代码创建了 AuthorizationEndpoint 类的 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 的类。现在我已经在这个新类中粘贴了相同的代码。

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;
    }

当我尝试创建这个新类的 bean 时,遇到以下错误:

应用程序启动失败


说明:

bean 'authorizationEndpoint',定义在 org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration, 无法注册。具有该名称的 bean 已经 在类路径资源中定义 [com/example/demo/AuthorizationServerConfig.class] 和覆盖是 已禁用。

行动:

考虑重命名其中一个 bean 或通过设置启用覆盖 spring.main.allow-bean-definition-overriding=true

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

“org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint”

如何强制 spring 使用我的 bean?

【问题讨论】:

  • 我不确定.getClass() 是否是最好的测试方法。之前的错误表明您正在更换 bean。无论如何,您可以排除 Autoconfiguration 类,您可以在其中找到带有 @SpringBootApplication(exclude=<Configurationclass>.class) 的 spring 代码,但这意味着您必须自己实现整个配置类。
  • 嗨@PeMa。谢谢你的建议。这当然是一种考虑的方式。但我发现有一种简单的方法可以覆盖框架端点。
  • 关于 bean 被覆盖的问题,我认为实际上我自己的 bean 被框架 bean 覆盖了。反之亦然

标签: spring oauth-2.0 authorization


【解决方案1】:

你需要一个Configuration

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();
    }
}

【讨论】:

【解决方案2】:

我红色了一篇关于覆盖 bean 的文章,它看起来如此混乱和不可预测。 read here 最好避免这样做。禁用框架 bean 的解决方案在于排除创建它的配置类。但这意味着我们必须自己实现这个洞。

@SpringBootApplication(exclude=<AuthorizationServerEndpointsConfiguration>.class)

但是覆盖框架端点的解决方案要容易得多。我们所要做的就是创建一个带有 /oauth/authorize 映射的控制器

自定义 UI 使用了大部分授权服务器端点 主要是通过机器,但有一些资源需要 UI 和那些是 /oauth/confirm_access 的 GET 和 HTML 来自 /oauth/error 的响应。它们是使用白标提供的 框架中的实现,因此大多数真实世界的实例 授权服务器将希望提供自己的,以便他们可以 控制样式和内容。您需要做的就是提供一个 带有 @RequestMappings 的 Spring MVC 控制器用于这些端点,以及 框架默认值将在调度程序中采用较低的优先级。 在 /oauth/confirm_access 端点中,您可以期待一个 AuthorizationRequest 绑定到携带所有所需数据的会话 寻求用户的批准(默认实现是 WhitelabelApprovalEndpoint 所以在那里寻找一个起点 复制)。您可以从该请求中获取所有数据并呈现它 不管你喜欢什么,然后用户需要做的就是 POST 回 /oauth/authorize 包含有关批准或拒绝的信息 授予。请求参数直接传递给 AuthorizationEndpoint 中的 UserApprovalHandler 以便您可以解释 数据或多或少随你喜欢。默认的 UserApprovalHandler 取决于您是否在您的 AuthorizationServerEndpointsConfigurer(在这种情况下,它是 ApprovalStoreUserApprovalHandler)与否(在这种情况下,它是 TokenStoreUserApprovalHandler)。标准批准处理程序接受 以下:

阅读更多here

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-07
    • 2017-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-07
    • 1970-01-01
    相关资源
    最近更新 更多