Spring OAuth2RestTemplate can't obtain new accessToken

旧街凉风 提交于 2019-12-25 11:26:05

问题


I am using spring-boot-oauth2 with authorization_code flow and @EnableZuulProxy .I followed the example from tut-spring-security-and-angular-js with oauth2. I set 60 seconds to access_token_validty for development purpose. Everythings work fine until access token is still valid. But when access token was expired (I waited a moment to expire it), I got exception as below..

WARN  SendErrorFilter:78 Error during filtering
com.netflix.zuul.exception.ZuulException: Filter threw Exception
    at com.netflix.zuul.FilterProcessor.processZuulFilter(FilterProcessor.java:227)
    at com.netflix.zuul.FilterProcessor.runFilters(FilterProcessor.java:157)
    at com.netflix.zuul.FilterProcessor.preRoute(FilterProcessor.java:133)
    at com.netflix.zuul.ZuulRunner.preRoute(ZuulRunner.java:105)
    at com.netflix.zuul.http.ZuulServlet.preRoute(ZuulServlet.java:125)
    at com.netflix.zuul.http.ZuulServlet.service(ZuulServlet.java:74)
    at org.springframework.web.servlet.mvc.ServletWrappingController.handleRequestInternal(ServletWrappingController.java:157)
    at org.springframework.cloud.netflix.zuul.web.ZuulController.handleRequest(ZuulController.java:44)
    at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:50)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:116)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:110)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:116)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at com.t3k.products.t3k_portal.backend.config.SecurityConfig$1.doFilterInternal(SecurityConfig.java:112)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:124)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter.doFilter(OAuth2ClientContextFilter.java:60)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter.doFilter(OAuth2ClientContextFilter.java:60)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Unknown Source)
Caused by: org.springframework.security.authentication.BadCredentialsException: Cannot obtain valid access token
    at org.springframework.cloud.security.oauth2.proxy.OAuth2TokenRelayFilter.getAccessToken(OAuth2TokenRelayFilter.java:99)
    at org.springframework.cloud.security.oauth2.proxy.OAuth2TokenRelayFilter.run(OAuth2TokenRelayFilter.java:79)
    at com.netflix.zuul.ZuulFilter.runFilter(ZuulFilter.java:112)
    at com.netflix.zuul.FilterProcessor.processZuulFilter(FilterProcessor.java:193)
    ... 102 more

I am using spring-boot version 1.5.4 and I'd also configured OAuth2RestTemplate as this github issue described. Any suggestions would be really appreciated.


回答1:


Was playing with the same tutorial and got the same issue while implementing of access tokens refreshing.

Caused by: org.springframework.security.authentication.BadCredentialsException: Cannot obtain valid access token
    at org.springframework.cloud.security.oauth2.proxy.OAuth2TokenRelayFilter.getAccessToken(OAuth2TokenRelayFilter.java:99) ~[spring-cloud-security-1.2.1.RELEASE.jar:1.2.1.RELEASE]

To be specific – when I got the error my test apps were modified to use usual tokens, not JWT, but I suppose it doesn’t matter for solving the problem.

To clarify the issue:

1) Added password grant for the client:

@Configuration
@EnableAuthorizationServer
protected static class AuthServerConfig
    extends AuthorizationServerConfigurerAdapter {

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.inMemory()
        .withClient("app0")
        .secret("secret0")
        .authorizedGrantTypes("authorization_code","refresh_token","password")
        .scopes("some_scope");
}

2) Executed the following request via curl (postman or another preferred utility could be used)

curl -X POST \
  http://localhost:9999/uaa/oauth/token \
  -H 'authorization: Basic YXBwMDpzZWNyZXQw' \
  -H 'content-type: multipart/form-data;' \
  -F grant_type=password \
  -F scope=some_scope \
  -F username=john \
  -F password=123

3) It returned

{
    "access_token": "cc5fff72-8f09-4770-87f5-f2237ce5978f",
    "token_type": "bearer",
    "refresh_token": "da55eb5c-6d31-4d70-9af8-bad8ce8a777f",
    "expires_in": 43199,
    "scope": "some_scope"
}

4) Took the value of refresh_token and used it at the next curl command

curl -X POST \
  http://localhost:9999/uaa/oauth/token \
  -H 'authorization: Basic YXBwMDpzZWNyZXQw' \
  -H 'content-type: multipart/form-data;' \
  -F grant_type=refresh_token \
  -F scope=some_scope \
  -F refresh_token=da55eb5c-6d31-4d70-9af8-bad8ce8a777f

5) Got the answer:

{"error":"server_error","error_description":"UserDetailsService is required."}

Using the error description an appropriate solution and explanation was easily found on GitHub

Simply put, it's necessary to inject UserDetailsService to AuthorizationServerConfiguration and add it to AuthorizationServerEndpointsConfigurer configuration. Example:

@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;

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

}

If after this modification the following error occurs

***************************
APPLICATION FAILED TO START
***************************

Description:

Field userDetailsService in demo.AuthserverApplication$AuthServerConfig required a bean of type 'org.springframework.security.core.userdetails.UserDetailsService' that could not be found.


Action:

Consider defining a bean of type 'org.springframework.security.core.userdetails.UserDetailsService' in your configuration.

It means that the auth server code lacks explicitly defined UserDetailsService (as it is at the original tutorial mentioned at the question)

For testing purposes in-memory UserDetailsService bean could be specified like this

@EnableResourceServer
public class AuthserverApplication extends WebMvcConfigurerAdapter {

    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("john").password("123").roles("USER").build());
        manager.createUser(User.withUsername("ivan").password("123").roles("USER").build());
        return manager;
    }



回答2:


The OAuth server invokes a user details query and for that matter it is required that you have a bean injected for the UserDetailsService. In my case I were able to get thru this by a LdapUserDetailsService as my user details are kept in LDAP.




回答3:


As the example of tut-spring-security-and-angular-js, I also have 3 applications UI,API and Auth. My UI application has 3 scenarios to fetch datas from my API application.

  1. from my UI application itself (I have to load some settings and informations from database)
  2. For sign-in users (my UI application shows some contents related to current sign-in users and their roles)
  3. for client-side (some pages load datas with JQuery ajax calls.eg:show tabular format with JQuery DataTable)

For case-1: I use spring-oauth2 password-flow with an administrator account. So I created to fetch refresh-token and access-tokens as

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.joda.JodaModule;
import com.jayway.restassured.RestAssured;
import com.jayway.restassured.config.ObjectMapperConfig;
import com.jayway.restassured.mapper.factory.Jackson2ObjectMapperFactory;
import com.jayway.restassured.response.Response;
import com.jayway.restassured.specification.RequestSpecification;

@Configuration
public class RestServiceConfig implements InitializingBean {


    private static final Logger applicationLogger = LogManager.getLogger("applicationLogs." + RestServiceConfig.class.getName());

    private static String applicationResourceUrl;

    private static String authServerTokenURI;

    private static String authServerClientId;

    private static String authServerClientSecret;

    private static String rootUserName;

    private static String rootUserSecret;

    @Value("${security.oauth2.client.access-token-uri}")
    public void setAuthServerTokenURI(String url) {
        authServerTokenURI = url;
    }

    @Value("${security.oauth2.client.client-id}")
    public void setAuthServerClientId(String clientId) {
        authServerClientId = clientId;
    }

    @Value("${security.oauth2.client.client-secret}")
    public void setAuthServerClientSecret(String secret) {
        authServerClientSecret = secret;
    }

    @Value("${root-user.name}")
    public void setRootUserName(String name) {
        rootUserName = name;
    }

    @Value("${root-user.secret}")
    public void setRootUserSecret(String secret) {
        rootUserSecret = secret;
    }

    @Value("${api-server.application}/api/")
    public void setApplicationResourceUrl(String url) {
        applicationResourceUrl = url;
    }

    private static String refreshToken;
    private static String accessToken;


    public static final String createURL(String path) {
        applicationLogger.info("Create REST service URI ==> " + applicationResourceUrl + path);
        return applicationResourceUrl + path;

    }

    public static final Response processed(String url, HashMap<String, ?> headers, Object body, HttpMethod method) {
        RequestSpecification request = getRestTemplate().contentType("application/json");
        if (headers != null) {
            request.headers(headers);
        }
        Response response = null;
        switch (method) {
        case POST: {
            response = request.body(body).post(createURL(url));
            break;
        }
        case PUT: {
            response = request.body(body).put(createURL(url));
            break;
        }
        case GET: {
            response = request.get(createURL(url));
            break;
        }
        case DELETE: {
            response = request.body(body).delete(createURL(url));
            break;
        }
        default:
            break;

        }
        // if access-token has expired, obtained new one and resend current
        // request
        if (response.getStatusCode() == 401) {
            accessToken = obtainAccessToken(refreshToken);
            return processed(url, headers, body, method);
        }
        return response;

    }

    public static <T> T parse(Response response, Class<T> responseType) {
        if (response.getStatusCode() != 200 || response.body().asString().equals("")) {
            return null;
        }
        return response.getBody().as(responseType);
    }

    private static final RequestSpecification getRestTemplate() {
        return RestAssured.given().header("Authorization", "Bearer " + accessToken);
    }

    private static final String obtainRootUserRefreshToken() {
        applicationLogger.info("----- Generation New refresh-token for Root User -----");
        final Map<String, String> params = new HashMap<String, String>();
        params.put("grant_type", "password");
        params.put("client_id", authServerClientId);
        params.put("username", rootUserName);
        params.put("password", rootUserSecret);
    // @formatter:off
        final Response response = RestAssured.given()
                .auth().preemptive().basic(authServerClientId, authServerClientSecret)
                .and()
                    .with().params(params)
                    .when().post(authServerTokenURI);
    // @formatter:on
        String refreshToken = response.jsonPath().getString("refresh_token");
        applicationLogger.info("Obtained New Refresh Token ==> " + refreshToken);
        Assert.notNull(refreshToken, "Failed for fetching Refresh Token. This problem can be caused by failed to connect Authetication server.");
        return refreshToken;
    }

    private synchronized final static String obtainAccessToken(String refreshToken) {
        applicationLogger.info("----- Generation New access-token for Root User with refresh-token[" + refreshToken + "] -----");
        final Map<String, String> params = new HashMap<String, String>();
        params.put("grant_type", "refresh_token");
        params.put("client_id", authServerClientId);
        params.put("refresh_token", refreshToken);
    // @formatter:off
        final Response response = RestAssured.given()
            .auth().preemptive().basic(authServerClientId, authServerClientSecret)
            .with().params(params)
            .when().post(authServerTokenURI);
    // @formatter:on
        String accessToken = response.jsonPath().getString("access_token");
        Assert.notNull(accessToken, "Failed for fetching Access Token. This problem can be caused by failed to connect Authetication server.");
        return accessToken;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // RestAssured.defaultParser = Parser.JSON;
        RestAssured.config = RestAssured.config().objectMapperConfig(ObjectMapperConfig.objectMapperConfig().jackson2ObjectMapperFactory(new Jackson2ObjectMapperFactory() {
            @SuppressWarnings("rawtypes")
            @Override
            public ObjectMapper create(Class cls, String charset) {
                // create custom objectMapper to parse Joda DateTime for RestAssured
                ObjectMapper objectMapper = new ObjectMapper();
                objectMapper.registerModule(new JodaModule());
                objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
                objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
                return objectMapper;
            }
        }));
        refreshToken = obtainRootUserRefreshToken();
        accessToken = obtainAccessToken(refreshToken);
    }

}

Below is sample code to fetch application's settings from my API server

public List<Setting> loadApplicationSettings() {
    // @formatter:off
    SettingCriteria criteria = new SettingCriteria();
    criteria.setApplicationId(1234l);
    criteria.setStatus(Status.ACTIVE);
    final Response response = RestServiceConfig.processed(
            "settings/search/list", 
            null, 
            criteria,HttpMethod.POST);
        // @formatter:on        
    return Arrays.asList(response.getBody().as(Setting[].class));
}

For case-2: I used @EnableZuulProxy as tut-spring-security-and-angular-js.

Note: Don't make your zuul mapping as public. See this answer.

For case-3: I used spring-oauth2's Oauth2RestTemplate as

import java.net.URI;
import java.net.URISyntaxException;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Conditional;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.client.OAuth2RestOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;


@Service
public class OAuth2RestService {
    private static final Logger serviceLogger = LogManager.getLogger("serviceLogs." + OAuth2RestService.class.getName());

    @Autowired
    private OAuth2RestOperations restTemplate;

    public <T> T get(String url, Class<T> responseType, HttpHeader... headers) {
        return processRestService(url, HttpMethod.GET, null, responseType, headers);
    }

    public <T> T post(String url, Object body, Class<T> responseType, HttpHeader... headers) {
        return processRestService(url, HttpMethod.POST, body, responseType, headers);
    }

    public <T> T put(String url, Object body, Class<T> responseType, HttpHeader... headers) {
        return processRestService(url, HttpMethod.PUT, body, responseType, headers);
    }

    public <T> T delete(String url, Object body, Class<T> responseType, HttpHeader... headers) {
        return processRestService(url, HttpMethod.DELETE, body, responseType, headers);
    }

    private <T> T processRestService(String url, HttpMethod method, Object body, Class<T> responseType, HttpHeader... headers) {
        serviceLogger.info("Request URL [ " + url + " ] | Request Method [ " + method.name() + " ] | Expected Response-Type [ " + responseType + " ]");
        try {
            MultiValueMap<String, String> headerInfos = null;
            if (headers != null && headers.length > 0) {
                headerInfos = new LinkedMultiValueMap<String, String>();
                for (HttpHeader header : headers) {
                    headerInfos.add(header.getName(), header.getValue().toString());
                }
            }
            RequestEntity<Object> requestEntity = new RequestEntity<Object>(body, headerInfos, method, new URI(RestServiceConfig.createURL(url)));
            ResponseEntity<T> response = restTemplate.exchange(requestEntity, responseType);
            if (response.getStatusCode() == HttpStatus.OK) {
                return response.getBody();
            }
        }
        catch (URISyntaxException e) {
            e.printStackTrace();
        }
        return null;
    }
}

We have to configure OAuth2RestOperations as ourself.

@Configuration
public class AppConfig {

    @Bean
    public RequestContextListener requestContextListener() {
        return new RequestContextListener();
    }

    @Bean
    @Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)
    public OAuth2RestOperations oAuth2RestOperations(OAuth2ProtectedResourceDetails details, OAuth2ClientContext oauth2ClientContext) {
        OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(details, oauth2ClientContext);
        return oAuth2RestTemplate;
    }

}

HttpHeader class

public class HttpHeader {
    private String name;
    private Object value;

    public HttpHeader(String name, Object value) {
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }

}

Below is sample codes to use Oauth2RestService class

@Controller
@RequestMapping("/company")
public class CompanyController extends BaseController {

    @Autowired
    private OAuth2RestService restService;

    @GetMapping
    public String company(Model model) {
        CompanyCriteria criteria = new CompanyCriteria();
        criteria.setWithStaticContent(true);
        criteria.setWithSubKey(true);
        List<CompanyBean> companies = Arrays.asList(restService.post("company/search/list", criteria, CompanyBean[].class));
        model.addAttribute("companies", companies);
        return "home_page";
    }

}

Another api call

.....
UserCriteria criteria = new UserCriteria();
criteria.setEmail(email);
HashMap<String, Object> myHeaders = new HashMap<>();
myHeaders.put("key", "value");
restService.put("user/search", criteria, Long.class, new HttpHeader("someHeaders", myHeaders));

Everythings work fine if not in case to fetch access-token for a single user concurrenty.

For example, I have some filters for every requests these are fetch some datas from my API server before execute client's requests. But I have JQuery ajax call methods from client side and these are run asynchronously. In this case, my filters also run async and this make error when access-token of my adminstrator account for password-flow has expired. See the thread Concurrency problems refreshing OAuth2 tokens. So I added synchronized at the method that fetch new access-token while existing has expired.

I hope some little helps for you. If you found some weakpoints or codesmells from my codes, don't be bother to correct me.



来源:https://stackoverflow.com/questions/45429629/spring-oauth2resttemplate-cant-obtain-new-accesstoken

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!