OAuth2ClientContext недоступен при обработке / ошибка - PullRequest
0 голосов
/ 06 июня 2019

Я устанавливаю весеннее облачное приложение с OAuth2 sso. Я установил приложение весенней загрузки с именем user-service и предоставил сервер аутентификации OAuth2.

Я также настроил другое приложение весенней загрузки с именем demo-service в качестве приложения сервера ресурсов и успешно получил доступ к демо-службе в сценарии, когда мои контроллеры не выдают никаких исключений.

Но когда мои контроллеры выдают исключение, я получу 401 Несанкционированный ответ (сказал, что я предоставил неверный токен, но журнал показал, что мой контроллер работал правильно и выдает исключение) вместо 500 Внутренняя ошибка ответ с сообщением об исключении.

Я вошел в исходный код spring-security-oauth2 и spring-boot-autoconfigure-oauth2. Я обнаружил, что если мои контроллеры выдают исключение, это исключение будет перехвачено встроенным сервером tomcat и перенаправлено в / error (что определено в application.yml со свойством server.error.path), но даже если я установлю это / error needn для проверки подлинности (я подтвердил, что могу получить доступ с / error без каких-либо учетных данных, таких как access_token), я все еще не могу получить внутреннюю ошибку 500.

Я искал в Google и обнаружил проблему в Github: Issue 84 . После «решения» проблемы они предложили добавить / error, чтобы не проходить аутентификацию, но это не работает для меня.

Версия Spring-cloud, которую я использую, - GreenWich.RELEASE, с подключаемыми модулями зависимости Maven предоставил spring-security-oauth2: 2.3.3.RELEASE и spring-boot-autoconfigure-oauth2: 2.1.0.M4. Вот некоторые связанные конфигурации:

Зависимости, которые я добавил в pom.xml

<dependencies>
        <!-- Spring Boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- Spring Boot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Spring Cloud -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter</artifactId>
        </dependency>
        <!-- Eureka -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>LATEST</version>
        </dependency>
        <!-- Security -->
        <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>
        <!-- OAuth2 -->
        <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <!-- MyBatis -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>LATEST</version>
        </dependency>
        <!-- MySQL-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.1.3.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

Основной класс:

@SpringBootApplication
@EnableDiscoveryClient
@MapperScan(basePackages = {"demo.mapper"})
public class DemoServiceApplication{

    public static void main(String[] args) {
        SpringApplication.run(DemoServiceApplication.class, args);
    }

}

класс конфигурации сервера ресурсов:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Autowired
    private ServerProperties serverProperties;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/error").permitAll()
                .anyRequest().authenticated();
    }
}

простой контроллер, который выдаст исключение:

@RestController
public class DemoController{
    @GetMapping
    public void testMethod(){
    // this can be run correctly and get the right principal information from user-service

 //System.out.println(SecurityContextHolder.getContext().getPrincipal());
        throw new RuntimeException("some message wanted to see in 500 response");
    }    
}

application.yml. Я думаю, что если я смогу получить доступ с помощью методов контроллера, то, вероятно, в конфигурации yml нет ошибок.

server:
  port: 8002
spring:
  application:
    name: demo-service
  datasource:
    # some jdbc configuration
mybatis:
  #some mybatis configuration
eureka:
  #eureka configuration

security:
  oauth2:
    client:
      access-token-uri: http://localhost:8000/oauth/token
      client-id: webclient
      client-secret: webclientsecret
    resource:
      user-info-uri: http://localhost:8000/user/current
      prefer-token-info: false
      service-id: user-service
logging:
  level:
    org: debug

Если я отправлю запрос / error напрямую, я смогу получить правильный ответ json (который был сгенерирован в BaseErrorController) Но если я отправлю на свой контроллер и буду перенаправлен на / error, я получу следующее:

{
    "error": "invalid_token",
    "error_description": "<ACCESS_TOKEN>"
}

и необычный вывод журнала:

DEBUG - [T2] o.a.coyote.http11.Http11InputBuffer      : Received [GET / HTTP/1.1
Authorization: Bearer <ACCESS_TOKEN>
User-Agent: PostmanRuntime/7.13.0
Accept: */*
Cache-Control: no-cache
Host: localhost:8002
cookie: JSESSIONID=<JSESSIONID>
accept-encoding: gzip, deflate
Connection: close

]
...
ERROR - [T2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: some message wanted to see in 500 response] with root cause

java.lang.RuntimeException: some message wanted to see in 500 response
    at demo.controller.DemoController.testMethod(DemoController.java:53) ~[classes/:na]
    ....

DEBUG - [T2] o.a.c.c.C.[Tomcat].[localhost]           : Processing ErrorPage[errorCode=0, location=/error]
...
DEBUG - [T2] o.s.security.web.FilterChainProxy        : /error at position 5 of 11 in additional filter chain; firing Filter: 'OAuth2AuthenticationProcessingFilter'
DEBUG - [T2] o.s.b.a.s.o.r.UserInfoTokenServices      : Getting user info from: http://localhost:8000/user/current
 WARN - [T2] o.s.b.a.s.o.r.UserInfoTokenServices      : Could not fetch user details: class org.springframework.beans.factory.BeanCreationException, Error creating bean with name 'scopedTarget.oauth2ClientContext': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
DEBUG - [T2] o.s.b.a.s.o.r.UserInfoTokenServices      : userinfo returned error: Could not fetch user details
DEBUG - [T2] p.a.OAuth2AuthenticationProcessingFilter : Authentication request failed: error="invalid_token", error_description="<ACCESS_TOKEN>"
DEBUG - [T2] o.s.b.a.audit.listener.AuditListener     : AuditEvent [timestamp=2019-06-06T04:59:03.634Z, principal=access-token, type=AUTHENTICATION_FAILURE, data={type=org.springframework.security.authentication.BadCredentialsException, message=<ACCESS_TOKEN>}]
DEBUG - [T2] s.s.o.p.e.DefaultOAuth2ExceptionRenderer : Written [error="invalid_token", error_description="<ACCESS_TOKEN>"] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@9b22a11]
DEBUG - [T2] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
DEBUG - [T2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    :  Disabling the response for further output

1 Ответ

0 голосов
/ 06 июня 2019

Я решил проблему, добавив Bean RequestContextListener в контейнер Spring:

@Bean
@ConditionalOnMissingBean(RequestContextListener.class)
public RequestContextListener requestContextListener(){
    return new ReqeustContextListener();
}

и я остановил JVM в режиме отладки, произвел поиск в памяти и обнаружил, что существует только один RequestContextListener. Но почему в веб-приложении Spring Boot будет отсутствовать RequestContextListener?

...