【问题标题】:Spring OAuth2RestTemplate can't obtain new accessTokenSpring OAuth2RestTemplate 无法获取新的 accessToken
【发布时间】:2018-01-07 20:05:16
【问题描述】:

我正在使用带有 authorization_code flow@EnableZuulProxy 的 spring-boot-oauth2。我遵循了 tut-spring-security-and-angular-js with oauth2 中的示例。 出于开发目的,我将 access_token_validty 设置为 60 秒。一切正常,直到访问令牌仍然有效。但是当访问令牌过期时(我等了一会儿让它过期),我得到了如下异常..

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

我使用的是 spring-boot 版本 1.5.4,并且我还将 OAuth2RestTemplate 配置为this github 问题描述。任何建议将不胜感激。

【问题讨论】:

    标签: spring-security-oauth2 netflix-zuul spring-oauth2


    【解决方案1】:

    在执行访问令牌刷新时,正在使用相同的教程并遇到相同的问题。

    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]
    

    具体来说——当我收到错误时,我的测试应用被修改为使用常用令牌,而不是 JWT,但我认为这对于解决问题并不重要。

    澄清问题:

    1) 为客户端添加密码授权:

    @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) 通过 curl 执行以下请求(可以使用邮递员或其他首选实用程序)

    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) 它返回了

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

    4) 获取 refresh_token 的值并在下一个 curl 命令中使用它

    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) 得到答案:

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

    使用错误描述,一个适当的解决方案和解释很容易found on GitHub

    简单来说就是需要将 UserDetailsS​​ervice 注入到 AuthorizationServerConfiguration 并添加到 AuthorizationServerEndpointsConfigurer 配置中。示例:

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private UserDetailsService userDetailsService;
    
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints)
            throws Exception {
        endpoints
            .authenticationManager(authenticationManager)
            .userDetailsService(userDetailsService);
    
    }
    

    如果修改后出现如下错误

    ***************************
    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.
    

    这意味着身份验证服务器代码缺少明确定义的 UserDetailsS​​ervice(就像问题中提到的原始教程一样)

    为了测试目的,可以像这样指定内存中的 UserDetailsS​​ervice bean

    @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】:

      OAuth 服务器调用用户详细信息查询,为此,您需要为 UserDetailsS​​ervice 注入一个 bean。在我的情况下,我能够通过 LdapUserDetailsS​​ervice 来解决这个问题,因为我的用户详细信息保存在 LDAP 中。

      【讨论】:

        【解决方案3】:

        作为tut-spring-security-and-angular-js的例子,我也有3个应用UIAPIAuth。我的 UI 应用程序有 3 个场景来从我的 API 应用程序中获取数据。

        1. 来自我的 UI 应用程序本身(我必须从数据库加载一些设置和信息)
        2. 对于登录用户(我的UI应用程序显示了一些与当前登录用户及其角色相关的内容)
        3. 用于客户端(某些页面使用 JQuery ajax 调用加载数据。例如:使用JQuery DataTable 显示表格格式)

        对于案例 1:我使用 spring-oauth2 password-flow 和管理员帐户。所以我创建了获取刷新令牌和访问令牌作为

        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);
            }
        
        }
        

        以下是从我的 API 服务器获取应用程序设置的示例代码

        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));
        }
        

        对于案例 2: 我将 @EnableZuulProxy 用作 tut-spring-security-and-angular-js

        注意:不要将您的 zuul 映射公开。见this answer

        对于案例 3:我使用 spring-oauth2 的 Oauth2RestTemplate 作为

        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;
            }
        }
        

        我们必须将OAuth2RestOperations配置为我们自己。

        @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 类

        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;
            }
        
        }
        

        下面是使用Oauth2RestService类的示例代码

        @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";
            }
        
        }
        

        另一个 api 调用

        .....
        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));
        

        如果不是为单个用户并发获取访问令牌,一切都可以正常工作。

        例如,我对每个请求都有一些过滤器,这些过滤器在执行客户端请求之前从我的 API 服务器获取一些数据。但是我有来自客户端的 JQuery ajax 调用方法,这些方法是异步运行的。在这种情况下,我的过滤器也会异步运行,当我的 password-flow 管理员帐户的访问令牌已过期时,这会出错。 请参阅线程Concurrency problems refreshing OAuth2 tokens。所以我在现有已过期时获取新访问令牌的方法中添加了synchronized

        希望对你有一点帮助。如果您从我的代码中发现了一些弱点或代码异味,请不要费心纠正我。

        【讨论】:

          猜你喜欢
          • 2017-02-01
          • 1970-01-01
          • 2016-12-10
          • 2013-11-08
          • 2020-02-10
          • 1970-01-01
          • 1970-01-01
          • 2018-06-17
          • 1970-01-01
          相关资源
          最近更新 更多