Запрос "OPTIONS/logout" не соответствует "POST/logout

Я изучаю Spring Cloud и Spring OAuth2, разложив три взаимосвязанных приложения в этом примере GitHub. Когда я открываю конечную точку /oauth/revoke-token в authserver приложении, а затем вызываю ее из ui app с http://localhost:9999/uaa/logout, журнал отладки для authserver app дает следующее сообщение об ошибке при отклонении запроса на выход:

Request 'OPTIONS /logout' doesn't match 'POST /logout

Какие конкретные изменения необходимо внести в код пример приложений GitHub, чтобы обеспечить глобальный выход из системы, ui приложение вызывает функцию выхода из hello.js


НАЧАЛЬНЫЕ УСИЛИЯ:


Изменения, которые я сделал до сих пор, включают:

Добавьте следующее определение @ Bean в AuthserverApplication.java:

@Bean
public TokenStore tokenStore() {return new InMemoryTokenStore();}

Добавьте следующий класс контроллера в пакет demo приложения authserver:

@Configuration
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    TokenStore tokenStore;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore);
    }

    @RequestMapping(value = "/oauth/revoke-token", method = RequestMethod.GET)
    @ResponseStatus(HttpStatus.OK)
    public void logout(HttpServletRequest request) {
        String authHeader = request.getHeader("Authorization");
        if (authHeader != null) {
            String tokenValue = authHeader.replace("Bearer", "").trim();
            OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue);
            tokenStore.removeAccessToken(accessToken);
        }
    }
}

Измените метод logout() hello.js в приложении ui, чтобы стать следующим:

self.logout = function() {
    $http.post('http://localhost:9999/uaa/logout', {}).finally(function() {
        $rootScope.authenticated = false;
        $location.path("/");
    });
}

Но когда пользователь нажимает кнопку выхода в браузере и вызывает вызов http://localhost:9999/uaa/logout, журнал отладки для authserver приложение выводит следующий результат:

2016-04-18 15:34:07.142 DEBUG 313 --- [io-9999-exec-10] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/css/**']
2016-04-18 15:34:07.142 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/logout'; against '/css/**'
2016-04-18 15:34:07.142 DEBUG 313 --- [io-9999-exec-10] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/js/**']
2016-04-18 15:34:07.142 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/logout'; against '/js/**'
2016-04-18 15:34:07.142 DEBUG 313 --- [io-9999-exec-10] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/images/**']
2016-04-18 15:34:07.142 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/logout'; against '/images/**'
2016-04-18 15:34:07.142 DEBUG 313 --- [io-9999-exec-10] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/**/favicon.ico']
2016-04-18 15:34:07.142 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/logout'; against '/**/favicon.ico'
2016-04-18 15:34:07.142 DEBUG 313 --- [io-9999-exec-10] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/error']
2016-04-18 15:34:07.142 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/logout'; against '/error'
2016-04-18 15:34:07.143 DEBUG 313 --- [io-9999-exec-10] o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
2016-04-18 15:34:07.143 DEBUG 313 --- [io-9999-exec-10] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/login']
2016-04-18 15:34:07.143 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/logout'; against '/login'
2016-04-18 15:34:07.143 DEBUG 313 --- [io-9999-exec-10] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/oauth/authorize']
2016-04-18 15:34:07.143 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/logout'; against '/oauth/authorize'
2016-04-18 15:34:07.143 DEBUG 313 --- [io-9999-exec-10] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/oauth/confirm_access']
2016-04-18 15:34:07.143 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/logout'; against '/oauth/confirm_access'

2016-04-18 15:34:07.143 DEBUG 313 --- [io-9999-exec-10] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/logout']
2016-04-18 15:34:07.143 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/logout'; against '/logout'
2016-04-18 15:34:07.143 DEBUG 313 --- [io-9999-exec-10] o.s.s.web.util.matcher.OrRequestMatcher  : matched

2016-04-18 15:34:07.143 DEBUG 313 --- [io-9999-exec-10] o.s.security.web.FilterChainProxy        : /logout at position 1 of 12 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2016-04-18 15:34:07.143 DEBUG 313 --- [io-9999-exec-10] o.s.security.web.FilterChainProxy        : /logout at position 2 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2016-04-18 15:34:07.143 DEBUG 313 --- [io-9999-exec-10] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2016-04-18 15:34:07.143 DEBUG 313 --- [io-9999-exec-10] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
2016-04-18 15:34:07.143 DEBUG 313 --- [io-9999-exec-10] o.s.security.web.FilterChainProxy        : /logout at position 3 of 12 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2016-04-18 15:34:07.143 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.se[email protected]5790c1b4
2016-04-18 15:34:07.143 DEBUG 313 --- [io-9999-exec-10] o.s.security.web.FilterChainProxy        : /logout at position 4 of 12 in additional filter chain; firing Filter: 'CsrfFilter'
2016-04-18 15:34:07.144 DEBUG 313 --- [io-9999-exec-10] o.s.security.web.FilterChainProxy        : /logout at position 5 of 12 in additional filter chain; firing Filter: 'LogoutFilter'

2016-04-18 15:34:07.144 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'OPTIONS /logout' doesn't match 'POST /logout
2016-04-18 15:34:07.144 DEBUG 313 --- [io-9999-exec-10] o.s.security.web.FilterChainProxy        : /logout at position 6 of 12 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'

2016-04-18 15:34:07.144 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'OPTIONS /logout' doesn't match 'POST /login
2016-04-18 15:34:07.144 DEBUG 313 --- [io-9999-exec-10] o.s.security.web.FilterChainProxy        : /logout at position 7 of 12 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2016-04-18 15:34:07.144 DEBUG 313 --- [io-9999-exec-10] o.s.security.web.FilterChainProxy        : /logout at position 8 of 12 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2016-04-18 15:34:07.144 DEBUG 313 --- [io-9999-exec-10] o.s.security.web.FilterChainProxy        : /logout at position 9 of 12 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2016-04-18 15:34:07.144 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.a.AnonymousAuthenticationFilter  : Populated SecurityContextHolder with anonymous token: 'org.sprin[email protected]9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.sprin[email protected]957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
2016-04-18 15:34:07.144 DEBUG 313 --- [io-9999-exec-10] o.s.security.web.FilterChainProxy        : /logout at position 10 of 12 in additional filter chain; firing Filter: 'SessionManagementFilter'
2016-04-18 15:34:07.144 DEBUG 313 --- [io-9999-exec-10] o.s.security.web.FilterChainProxy        : /logout at position 11 of 12 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2016-04-18 15:34:07.144 DEBUG 313 --- [io-9999-exec-10] o.s.security.web.FilterChainProxy        : /logout at position 12 of 12 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2016-04-18 15:34:07.144 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /logout; Attributes: [authenticated]
2016-04-18 15:34:07.144 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.sprin[email protected]9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.sprin[email protected]957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
2016-04-18 15:34:07.144 DEBUG 313 --- [io-9999-exec-10] o.s.s.access.vote.AffirmativeBased       : Voter: org.sp[email protected]539015a, returned: -1
2016-04-18 15:34:07.145 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.a.ExceptionTranslationFilter     : Access is denied (user is anonymous); redirecting to authentication entry point

org.springframework.security.access.AccessDeniedException: Access is denied
    at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83) ~[spring-security-core-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:232) ~[spring-security-core-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:123) ~[spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:90) ~[spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114) ~[spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:122) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:48) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:205) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:120) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:96) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53) [spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    ...  
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_45]

2016-04-18 15:34:07.146 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.util.matcher.AndRequestMatcher   : Trying to match using Ant [pattern='/**', GET]
2016-04-18 15:34:07.146 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'OPTIONS /logout' doesn't match 'GET /**
2016-04-18 15:34:07.146 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.util.matcher.AndRequestMatcher   : Did not match
2016-04-18 15:34:07.146 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.s.HttpSessionRequestCache        : Request not saved as configured RequestMatcher did not match
2016-04-18 15:34:07.146 DEBUG 313 --- [io-9999-exec-10] o.s.s.w.a.ExceptionTranslationFilter     : Calling Authentication entry point.
2016-04-18 15:34:07.146 DEBUG 313 --- [io-9999-exec-10] o.s.s.web.DefaultRedirectStrategy        : Redirecting to 'http://localhost:9999/uaa/login'
2016-04-18 15:34:07.147 DEBUG 313 --- [io-9999-exec-10] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2016-04-18 15:34:07.147 DEBUG 313 --- [io-9999-exec-10] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed

Какие другие конкретные изменения кода необходимо изменить в примерах приложений GitHub, чтобы включить приложение ui для запуска глобального выход пользователя из всех приложений?

Примечание. Очевидно, что /uaa/logout - это другой URL, чем /oauth/revoke-token. Тем не менее, внутренние действия Spring Security и OAuth в этой ситуации не ясны без ответа на этот OP.


Предложения @StuXnet:


Содержимое закладки Firefox Developer Tools Network для запроса:

Запрос http : // localhost:9999/uaa/login с методом OPTIONS был отклонен с ошибкой 403.

Необработанные заголовки request:

Host: localhost:9999
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: http://localhost:8080
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type,x-requested-with
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache

Необработанные заголовки response:

Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
Content-Length: 20
Date: Mon, 18 Apr 2016 23:45:46 GMT
Server: Apache-Coyote/1.1
X-Application-Context: application:9999

Затем я изменил метод config (http) LoginConfig следующим образом:

@Override
protected void configure(HttpSecurity http) throws Exception {
    // @formatter:off
    http
        .formLogin().loginPage("/login").permitAll()
        .and()
        .requestMatchers().antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access", "/logout", "/oauth/revoke-token")
        .and()
        .authorizeRequests()
        .antMatchers(HttpMethod.OPTIONS,"/logout").permitAll()
        .anyRequest().authenticated();
        // @formatter:on
}

Это привело к следующей новой распечатке в журналах загрузки Spring Boot DEBUG:

2016-04-18 19:22:06.202 DEBUG 5319 --- [io-9999-exec-10] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/css/**']
2016-04-18 19:22:06.202 DEBUG 5319 --- [io-9999-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/logout'; against '/css/**'
2016-04-18 19:22:06.202 DEBUG 5319 --- [io-9999-exec-10] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/js/**']
2016-04-18 19:22:06.202 DEBUG 5319 --- [io-9999-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/logout'; against '/js/**'
2016-04-18 19:22:06.202 DEBUG 5319 --- [io-9999-exec-10] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/images/**']
2016-04-18 19:22:06.202 DEBUG 5319 --- [io-9999-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/logout'; against '/images/**'
2016-04-18 19:22:06.202 DEBUG 5319 --- [io-9999-exec-10] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/**/favicon.ico']
2016-04-18 19:22:06.202 DEBUG 5319 --- [io-9999-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/logout'; against '/**/favicon.ico'
2016-04-18 19:22:06.202 DEBUG 5319 --- [io-9999-exec-10] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/error']
2016-04-18 19:22:06.202 DEBUG 5319 --- [io-9999-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/logout'; against '/error'
2016-04-18 19:22:06.202 DEBUG 5319 --- [io-9999-exec-10] o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
2016-04-18 19:22:06.203 DEBUG 5319 --- [io-9999-exec-10] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request '/logout' matched by universal pattern '/**'
2016-04-18 19:22:06.203 DEBUG 5319 --- [io-9999-exec-10] o.s.security.web.FilterChainProxy        : /logout has an empty filter list
2016-04-18 19:22:06.204 DEBUG 5319 --- [io-9999-exec-10] .s.o.p.e.FrameworkEndpointHandlerMapping : Looking up handler method for path /logout
2016-04-18 19:22:06.205 DEBUG 5319 --- [io-9999-exec-10] .s.o.p.e.FrameworkEndpointHandlerMapping : Did not find handler method for [/logout]

И следующий заголовок request в Firefox:

Host: localhost:9999
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: http://localhost:8080
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type,x-requested-with
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache

Наряду со следующим заголовком response в Firefox:

Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
Content-Length: 20
Date: Tue, 19 Apr 2016 02:22:06 GMT
Server: Apache-Coyote/1.1
X-Application-Context: application:9999

Затем я попытался изменить метод configure(http) LoginConfig, чтобы стать:

@Override
protected void configure(HttpSecurity http) throws Exception {
    // @formatter:off
    http
        .formLogin().loginPage("/login").permitAll()
        .and()
        .requestMatchers().antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access", "/logout", "/oauth/revoke-token")
        .and()
        .authorizeRequests()
        .anyRequest().authenticated()
        // @formatter:on
        .and()
        .csrf()
        .ignoringAntMatchers("/logout");
}

Но результатом является следующий Spring Boot DEBUG журнал для authserver приложения:

2016-04-19 10:12:13.545 DEBUG 4593 --- [nio-9999-exec-2] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/css/**']
2016-04-19 10:12:13.546 DEBUG 4593 --- [nio-9999-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/logout'; against '/css/**'
2016-04-19 10:12:13.546 DEBUG 4593 --- [nio-9999-exec-2] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/js/**']
2016-04-19 10:12:13.546 DEBUG 4593 --- [nio-9999-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/logout'; against '/js/**'
2016-04-19 10:12:13.546 DEBUG 4593 --- [nio-9999-exec-2] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/images/**']
2016-04-19 10:12:13.546 DEBUG 4593 --- [nio-9999-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/logout'; against '/images/**'
2016-04-19 10:12:13.546 DEBUG 4593 --- [nio-9999-exec-2] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/**/favicon.ico']
2016-04-19 10:12:13.546 DEBUG 4593 --- [nio-9999-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/logout'; against '/**/favicon.ico'
2016-04-19 10:12:13.546 DEBUG 4593 --- [nio-9999-exec-2] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/error']
2016-04-19 10:12:13.546 DEBUG 4593 --- [nio-9999-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/logout'; against '/error'
2016-04-19 10:12:13.546 DEBUG 4593 --- [nio-9999-exec-2] o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
2016-04-19 10:12:13.546 DEBUG 4593 --- [nio-9999-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request '/logout' matched by universal pattern '/**'
2016-04-19 10:12:13.546 DEBUG 4593 --- [nio-9999-exec-2] o.s.security.web.FilterChainProxy        : /logout has an empty filter list
2016-04-19 10:12:13.547 DEBUG 4593 --- [nio-9999-exec-2] .s.o.p.e.FrameworkEndpointHandlerMapping : Looking up handler method for path /logout
2016-04-19 10:12:13.548 DEBUG 4593 --- [nio-9999-exec-2] .s.o.p.e.FrameworkEndpointHandlerMapping : Did not find handler method for [/logout]

ВОСПРОИЗВЕДЕНИЕ ПРОБЛЕМЫ НА ВАШУ МАШИНА:


Чтобы воспроизвести проблему на вашей собственной машине, вы можете:

1.) git клонировать исходное примерное приложение по ссылке вверху OP, а затем внести изменения, показанные выше, или

2.) Загрузите zipped-версию приложения, которая существует в моем devbox, включая все изменения из OP в этой ссылке для обмена файлами, а затем:

2.a.) Отменить приложение.

2.b.) Перейдите в окно терминала до oauth2/resource и mvn spring-boot:run. Затем перейдите во второе окно терминала к oauth2/authserver и введите mvn spring-boot:run. Затем перейдите в третье окно терминала на oauth2/ui и введите mvn spring-boot:run.

2.c.) Перейдите в веб-браузер на http : // localhost:8080, затем нажмите login, затем введите user для username и password для password. После того как вы прошли проверку подлинности, нажмите кнопку logout, чтобы воспроизвести ошибку 403. Используйте вкладку "Сеть" инструментов разработчика браузера, чтобы проверить активность браузера. Просмотрите журналы Spring Boot в терминале, на котором запущено приложение authserver, чтобы увидеть действие Spring.

3.) Импортируйте три папки в eclipse (или другую среду IDE) в качестве существующих проектов maven или откройте файлы кода с помощью текстового редактора для редактирования, прежде чем повторять перезапуск и повторный тест на шаге 2.

Что еще я могу предоставить, чтобы помочь изолировать решение?

Ответ 1

Оставляя в стороне вопрос о том, почему вы, возможно, захотите это сделать, и является ли это хорошей идеей: ваш JS-клиент отправляет POST на конечную точку на другом сервере, поэтому вы сталкиваетесь с двумя проблемами: совместное использование ресурсов Cross-Origin (CORS ) и Cross-Site Request Forgery (CSRF), оба из которых заблокированы по умолчанию на вашем сервере Auth, поскольку использует Spring MVC и Spring Безопасность.

Проблема CORS может быть решена различными способами, включая подход, который вы использовали, чтобы пробить дыру через конфигурацию безопасности с помощью совпадения запросов permitAll(). Существует гораздо лучшая интеграция между Spring MVC и Spring безопасностью с помощью HttpSecurity.cors(). Ссылка на руководство пользователя: http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors. Простой пример из учебника (сервер ресурсов ванили):

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors()
        ...;
}

Что это значит, это включить интеграцию с объявленными концами MVC с помощью @CrossOrigin. На самом деле конечная точка, которую вы пытаетесь выполнить POST, не такая, которую вы написали, и это не конечная точка Spring MVC, поэтому вам может потребоваться вместо этого использовать cors().configurationSource(...).

Проблема CSRF также легко решить различными способами. В учебном пособии, в котором вы начали, содержатся явные примеры, показывающие, как это сделать для Angular JS (но не в приложении, которое вы используете, потому что учебное пособие не связано с выходом из поставщика единого входа). В этом случае мы используем функции HttpSecurity.csrf(). Ссылка на руководство пользователя: http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#csrf. Простой пример из учебника в приложении пользовательского интерфейса:

@Override
public void configure(HttpSecurity http) throws Exception {
    http
        ...
        .csrf()
            .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}