带有自定义 TokenGranter 的 Spring Security OAuth2 版本 2.0.+

Posted

技术标签:

【中文标题】带有自定义 TokenGranter 的 Spring Security OAuth2 版本 2.0.+【英文标题】:Spring Security OAuth2 with custom TokenGranter in version 2.0.+ 【发布时间】:2014-10-05 12:37:14 【问题描述】:

在以前的 OAuth2 版本中,可以通过将自定义令牌授予者添加到 <authorization-server> 元素中的 xml 配置中来添加自定义令牌授予者。

我想知道如何使用 AuthorizationServerConfigurerAdapter 使用 Java Config 扩展授权服务器,而不会丢失包含隐式、客户端凭据、刷新令牌和授权代码授予类型的默认配置。

第一次尝试是使用 @Component 创建 TokenGranter:

@Component("customTokenGranter")
public class CustomTokenGranter 
     //implementation

这会导致依赖解析异常,因为构造 Granter 所需的 tokenServices 无法自动装配。

第二次尝试使用configure方法

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception

    endpoints
        .tokenGranter(new CustomTokenGranter(endpoints.getTokenServices(),
                endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory()));


使用这个,默认的授权类型将不会被注册。

我还尝试了较低阶的第二个配置,但没有成功。 我还能做些什么来添加我的自定义授权类型?

【问题讨论】:

【参考方案1】:

您还需要添加默认值,例如使用CompositeTokenGranter:

        List<TokenGranter> tokenGranters = getTokenGranters(); // implementation up to you
        tokenGranter = new CompositeTokenGranter(tokenGranters);
        endpoints.tokenGranter(tokenGranter);

【讨论】:

我通过为原始 CompositeTokenGranter 开发一个装饰器解决了这个问题,该装饰器可以使用 endpoints.getTokenGranter() 的结果进行初始化。装饰器还有一个 add 方法来添加额外的授予者。然后在授予方法中,我首先尝试自定义授予者,然后委托给原始 CompositeTokenGranter。 仅供参考:文档中似乎有错误。 TokenGranter 必须使用 AuthorizationServerEndpointsConfigurer 进行配置 - 文档说它应该是 AuthorizationServerSecurityConfigurer。 您在致电endpoints.getTokenGranter() 时没有遇到问题吗?因为clientDetailsService(在endpoints内的这个阶段没有正确初始化。你也不能使用endpoints.clientDetailsService(clientDetailsService)来设置它,因为yooture.web.config.SecurityOauth2Configuration.AuthorizationServerConfiguration.configure(ClientDetailsServiceConfigurer)仍然会被调用并再次覆盖一些引用。 我相信 master 中不存在这个问题(它将是 2.0.8)。你可以试试看(github里有样例)。 您是否查看了集成测试 (github.com/spring-projects/spring-security-oauth/tree/master/…)?【参考方案2】:

由于对 ClientDetailService 的依赖使得从 getTokenGranter 方法获取默认授予者变得困难,我找不到执行此操作的方法。我从 AuthorizationServerEndpointsConfigurer#tokenGranter() 复制了代码,并将我的 clientDetailService 和其他 bean 直接传递给构造函数。请注意,我添加创建一个 DefaultOAuth2RequestFactory 以传递给授予者和端点:

public TokenGranter tokenGranter() 

            ClientDetailsService clientDetails = clientDetailsService;
            AuthorizationServerTokenServices tokenServices = tokenServices();
            AuthorizationCodeServices authorizationCodeServices = authorizationCodeServices();
            OAuth2RequestFactory requestFactory = requestFactory();

            List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>();

            tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices,
                    clientDetails, requestFactory));
            tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory));
            tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory));
            tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory));
            tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices,
                        clientDetails, requestFactory));
            tokenGranters.add(new CustomTokenGranter(authenticationManager, tokenServices(), clientDetailsService,
                    requestFactory));

            return new CompositeTokenGranter(tokenGranters);
    

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 

        endpoints
                .tokenServices(tokenServices())
                .tokenStore(tokenStore())
                .tokenEnhancer(tokenEnhancer())
                .authorizationCodeServices(authorizationCodeServices())
                .userApprovalHandler(userApprovalHandler())
                .authenticationManager(authenticationManager)
                .requestFactory(requestFactory())
                .tokenGranter(tokenGranter());
    

话虽如此,我最终删除了该代码并简单地添加了另一个 AuthenticationProvider,因为我的新授权类型无论如何都使用了 UsernamePasswordAuthenticationToken 的子类,这是密码授权默认使用的身份验证类型。

【讨论】:

【参考方案3】:

根据文档,我们有:

资助类型

AuthorizationEndpoint 支持的授权类型可以是 通过 AuthorizationServerEndpointsConfigurer 配置。默认 除密码外,所有授权类型均受支持(详见下文 如何打开它)。以下属性会影响授权类型:

authenticationManager:密码授权通过注入 身份验证管理器。 ......

见documentation。 所以你可以像这样注入 AuthenticationManager :

    @Configuration
    @EnableAuthorizationServer
    public class AuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter 

        @Autowired
        private AuthenticationManager authenticationManager;

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 
            endpoints
                    .authenticationManager(authenticationManager)
........

【讨论】:

【参考方案4】:

这是另一种方式。 Copied from here.

在此示例中,将名为 CustomTokenGranter 的新自定义 TokenGranter 添加到默认为 TokenGrantersCompositeTokenGranter。我喜欢这个例子,因为它使用AuthorizationServerEndpointsConfigurer 的公共方法getTokenGranter() 来检索默认的TokenGranter

@Configuration
@EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter 

    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 
        endpoints.authenticationManager(authenticationManager);
        endpoints.tokenGranter(tokenGranter(endpoints));
    

    private TokenGranter tokenGranter(final AuthorizationServerEndpointsConfigurer endpoints) 
        List<TokenGranter> granters = new ArrayList<TokenGranter>(Arrays.asList(endpoints.getTokenGranter()));
        granters.add(new CustomTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), "custom"));
        return new CompositeTokenGranter(granters);
    

【讨论】:

您好 AuthorizationServerEndpointsConfigurer 端点 TokenGranters 在启动时为空。你能告诉我如何解决这个问题吗? @NinjaDude 添加新列表。 我的意思是 endpoints.getTokenGranter() == null 当我启动我的 Spring Boot 应用程序时。我放了一个调试点并看到了它。所以本质上,只有 CustomTokenGranter 存在。缺少默认令牌授予者的其余部分(例如 refreshtoken、授权码授予者等)。 @NinjaDude List granters = endpoints.getTokenGranter() == null ?新 ArrayList() : new ArrayList(Arrays.asList(endpoints.getTokenGranter()));

以上是关于带有自定义 TokenGranter 的 Spring Security OAuth2 版本 2.0.+的主要内容,如果未能解决你的问题,请参考以下文章

spring boot 自定义登录页面

maven-assembly-plugin自定义打包

SpringVC 拦截器+自定义注解 实现权限拦截

如何在自定义 AuthenticationFailureHandler 中返回由具体派生 AuthenticationException 类型解析的不同错误消息

带有自定义控件和自定义 StringProperty 的 Proguard

带有自定义按钮的自定义 UIAlertView