如何在 Rest 模板中重用用户 (authorization_code) 的 oauth2 令牌

p.s*_*eef 2 spring spring-security spring-data-rest spring-boot spring-security-oauth2

我有3个应用程序

  1. 前端应用程序
  2. OAuth2认证服务器
  3. REST API(RepositoryRestResources)

我的用户必须先登录才能使用前端应用程序。这是通过 SSO 实现的。他们会收到一个令牌,该令牌在被允许进入之前由客户验证。

我想重用这个令牌来发出 api 请求。我的 REST api 应用程序使用相同的 SSO 登录(它是前端客户端的资源)进行保护,但我不知道如何“添加授权标头”以在我用于 api 请求的 RestTemplate 中使用。

我像这样创建我的restTemplate:

public static RestTemplate build()
    {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.registerModule(new Jackson2HalModule());
        mapper.registerModule(new JavaTimeModule());
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setSupportedMediaTypes(MediaType.parseMediaTypes("application/hal+json"));
        converter.setObjectMapper(mapper);
        return new RestTemplate(Arrays.asList(converter));
    }
Run Code Online (Sandbox Code Playgroud)

我的资源服务器配置:

@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter
{

    @Value("${resource.id}")
    private String resourceId;

    @Override
    public void configure(HttpSecurity http) throws Exception
    {
        http
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                .anyRequest().authenticated()
                .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception
    {
        resources.resourceId(resourceId);
    }


    @Bean
    public static TokenEnhancer tokenEnhancer()
    {
        return new JwtTokenEnhancer();
    }


    @Bean
    public static JwtAccessTokenConverter accessTokenConverter()
    {
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("keystore.jks"), "somesecret".toCharArray());
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();

        converter.setKeyPair(keyStoreKeyFactory.getKeyPair("pair"));
        return converter;
    }

    @Bean
    public static TokenStore tokenStore()
    {
        return new JwtTokenStore(accessTokenConverter());
    }

}
Run Code Online (Sandbox Code Playgroud)

p.s*_*eef 5

我使用拦截器修复了它,并从安全上下文中手动添加令牌。

RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add(new OAuthInterceptor());
Run Code Online (Sandbox Code Playgroud)

其中拦截器定义为:

public class OAuthInterceptor implements ClientHttpRequestInterceptor
{

    @Autowired
    private AuthenticationHolder holder;

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException
    {
        if (holder.getToken() == null)
        {
            //throw new IOException("Token not set");
            System.out.println("##################### Token not set! ###################");
        }
        else
        {
            System.out.println("##################### Token found: " + holder.getToken());
            HttpHeaders headers = request.getHeaders();
            headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + holder.getToken());
        }

        return execution.execute(request, body);
    }
}
Run Code Online (Sandbox Code Playgroud)

我使用在客户端应用程序中实现的接口:

public interface AuthenticationHolder
{
    String getToken();
}

@Bean
public AuthenticationHolder getAuthenticationHolder()
{
    return () ->
    {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if(authentication != null && authentication.getDetails() instanceof OAuth2AuthenticationDetails)
        {
            return ((OAuth2AuthenticationDetails) authentication.getDetails()).getTokenValue();
        }
        return null;
    };
}
Run Code Online (Sandbox Code Playgroud)