如何将 OAuth 2 令牌映射到资源服务器中的 UserDetails 对象?

How do I map OAuth 2 token to UserDetails object in a resource server?

我有 2 个独立的 Spring 引导应用程序,一个用作 OAuth 2 授权服务器,另一个用作资源服务器。我在我的资源服务器中使用 Spring 的 RemoteTokenServices 来检查来自授权服务器的令牌。现在,我试图在我的资源服务器应用程序中定义受保护的控制器代码,但我不确定如何将 UserDetails class 映射到通过 OAuth 2 机制提供的身份验证主体。

我已经使用自定义 TokenEnhancer 设置了我的授权服务器,它向令牌添加了更多详细信息,以便 /oauth/check_token?token=<token> returns 具有自定义字段,我想将其映射到我的资源服务器控制器。

在授权服务器也是资源服务器的更单一的设置中,我可以这样定义使用经过身份验证的主体的控制器方法:

//User implements UserDetails
public Map<String, Object> getResource(@AuthenticationPrincipal User user) {
    //code that uses the user object
}

但是,这在更分布式的方法中似乎并不那么直接。映射失败,user 参数最终成为空对象。我尝试使用以下方法:

public Map<String, Object> getResource(Authentication authentication) {
    //code that uses the authentication object
}

虽然上面的代码成功映射了身份验证详细信息,但它并没有为我提供直接访问我通过前面提到的 TokenEnhancer 设置的自定义字段的方法。我似乎无法从 Spring 文档中找到与此相关的任何内容。

您是否启用了 AuthenticationPrincipalArgumentResolver,就像关注 xml 一样?

<mvc:annotation-driven>
        <mvc:argument-resolvers>
                <bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver" />
        </mvc:argument-resolvers>
</mvc:annotation-driven>

并且你需要实现UserDetails到return你自己的CustomerUser object,然后你可以使用注释直接获取主体。

为了解决这个问题,让我先了解一下架构背景。通过@AuthenticationPrincipal自动映射的UserDetails对象来自活动Authentication对象的principal字段。资源服务器控制器可以访问 OAuth2Authencation 对象,它是 Spring OAuth2 安全框架的 Authentication 的专用实例,只需将其声明为方法参数的一部分即可。

public void controllerMethod(OAuth2Authentication authentication) {
    //controller definition
}

知道了这一点,现在的问题就转移到如何确保Authentication对象中的getPrincipal()方法是我自定义的UserDetailsclass的一个实例了。我在资源服务器应用程序中使用的 RemoteTokenServices 使用 AccessTokenConverter 的实例来解释授权服务器发送的令牌详细信息。默认情况下,它使用 DefaultAccessTokenConverter,它只是将身份验证主体设置为用户名,即 String。该转换器利用 UserAuthenticationConverter 将来自授权服务器的数据转换为 Authentication 的实例。这是我需要定制的:

DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
tokenConverter.setUserTokenConverter(new DefaultUserAuthenticationConverter() {

    @Override
    public Authentication extractAuthentication(Map<String, ?> map) {
        Authentication authentication = super.extractAuthentication(map);
        // User is my custom UserDetails class
        User user = new User();
        user.setSpecialKey(map.get("specialKey").toString());
        return new UsernamePasswordAuthenticationToken(user,
                authentication.getCredentials(), authentication.getAuthorities());
    }

});
tokenServices.setAccessTokenConverter(tokenConverter);

完成所有这些设置后,@AuthenticationPrincipal 机制现在可以正常工作了。