问题
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.
- from my UI application itself (I have to load some settings and informations from database)
- For sign-in users (my UI application shows some contents related to current sign-in users and their roles)
- 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