用户的OAuth2令牌进入RestTemplate

时间:2019-09-01 16:00:22

标签: java spring spring-security oauth-2.0

如何正确获取用户的会话oauth2令牌?

我使用spring-security-oauth2-autoconfigure实现了OAuth2授权/资源服务器。

我实现了一个客户端应用程序,该应用程序使用授权服务器登录用户并获取其访问令牌。登录阶段运行良好,因此可以获取登录数据(使用oauth2过滤器的访问令牌)。客户端应用请求中的Principal正确显示了授权服务器填充的所有权限。

我想使用客户端应用程序作为代理,以使用请求呼叫的用户的给定访问令牌发送休息请求。

我已经尝试使用@EnableOAuth2Client,但这不起作用。尝试自动连线时,OAuth2RestTemplate为空。

我不得不重新实现RestTemplate的请求范围的bean,该bean从tokenValue获得SecurityContext。这行得通,但我觉得这不干净。这种行为很常见,所以我应该错过一些事情。

application.yml

spring:
  application.name: client
  security:
    oauth2:
      client:
        registration:
          myclient:
            client-id: client-id
            client-secret: client-secret
            redirect-uri: http://localhost:8081/login/oauth2/code/
            authorization-grant-type: authorization_code

        provider:
          myclient:
            authorization-uri: http://localhost:8090/oauth/authorize
            token-uri: http://localhost:8090/oauth/token
            user-info-uri: http://localhost:8090/me
            user-name-attribute: name

安全配置

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http.authorizeRequests()
            .antMatchers("/", "/login").permitAll()
            .antMatchers("/admin/**").hasRole("ADMIN")
            .anyRequest().authenticated()
        .and().oauth2Login()
        .and().oauth2Client()
        ;
        // @formatter:on

    }

    @Bean
    @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
    @Qualifier("oauth2RestTemplate")
    public RestTemplate oauth2RestTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getInterceptors().add(new ClientHttpRequestInterceptor() {

            @Override
            public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
                    throws IOException {

                Authentication auth = SecurityContextHolder.getContext().getAuthentication();

                if (auth != null && auth.isAuthenticated() && auth instanceof OAuth2AuthenticationToken) {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> details = (Map<String, Object>) ((OAuth2AuthenticationToken) auth).getPrincipal().getAttributes().get("details");
                    String tokenValue = (String) details.get("tokenValue");

                    if (tokenValue != null) {
                        request.getHeaders().add("Authorization", "Bearer " + tokenValue);
                    }
                }

                return execution.execute(request, body);
            }
        });


        return restTemplate;
    }
}

在WebController中

    private @Autowired @Qualifier("oauth2RestTemplate") RestTemplate oauth2RestTemplate;

    @GetMapping("/remote")
    public Map<String, Object> remote() {
        @SuppressWarnings("unchecked")
        Map<String, Object> resp = oauth2RestTemplate.getForObject(URI.create("http://localhost:8090/api/test"), Map.class);

        return resp;
    }

它有效,但是我不认为自己应该配置RestTemplate

1 个答案:

答案 0 :(得分:0)

不幸的是,您必须定义OAuth2RestTemplate。但是,这是一个更干净的实现。

@Bean
public OAuth2RestTemplate oauth2RestTemplate() {

    ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();
    resourceDetails.setAccessTokenUri(format("%s/oauth/token", authServerUrl));
    resourceDetails.setClientId("client_id");
    resourceDetails.setClientSecret("client_secret");
    resourceDetails.setGrantType("client_credentials");
    resourceDetails.setScope(asList("read", "write"));

    DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext();
    return new OAuth2RestTemplate(resourceDetails, clientContext);
}

在这种情况下,您的资源服务器将使用您自己的凭据代表您与授权服务器通信。