Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flow Servlet should co-exist nicely with other Spring Servlets #2660

Closed
heruan opened this issue Oct 11, 2017 · 12 comments
Closed

Flow Servlet should co-exist nicely with other Spring Servlets #2660

heruan opened this issue Oct 11, 2017 · 12 comments
Assignees
Labels
Milestone

Comments

@heruan
Copy link
Member

heruan commented Oct 11, 2017

If I include Flow as a dependency in a Spring project, all paths are managed by the Flow Servlet (since urlPatterns = "/*") and this somehow takes precedence on more specific paths, e.g. /oauth2/token.

Having Flow co-existing with other Spring features should not require extra configuration.

@pleku pleku added the spring label Oct 17, 2017
@pleku
Copy link
Contributor

pleku commented Oct 17, 2017

We should really not fall into the same pit as with the FW Spring-addon, where it is impossible to use e.g. reverse proxies with Vaadin & Spring.
vaadin/spring#30

@heruan
Copy link
Member Author

heruan commented Oct 17, 2017

Absolutely. I don't know the internals, but we have several Spring apps in production working behind reverse proxies without issues. If the /* is to blame, I assumed Spring would give priority to more specific mappings first… or at least this seems reasonable.

@denis-anisimov
Copy link
Contributor

I've done some investigation and I don't really see any issue with Spring add-on.
Here is what I have:

@SpringBootApplication
@Configuration
@ComponentScan
public class TestServletInitializer extends SpringBootServletInitializer {

    @Bean
    public ServletRegistrationBean anotherServlet() {
        ServletRegistrationBean registration = new ServletRegistrationBean(
                new MyServlet(), "/a/*");
        return registration;
    }

    public static class MyServlet extends HttpServlet {

        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            resp.getWriter().println("Hello");
        }
    }
}

So a vaadin servlet instance works on "/" url mapping and a MyServlet instance works on "/a/" mapping.
And they coexist well. I'm able to see that the MyServlet instance is dispatching the requests to "/a/" properly.

So could you provide a more specific usecase if you have an issue with Spring add-on?

@heruan
Copy link
Member Author

heruan commented Oct 23, 2017

I'm not using SpringBootServletInitializer, but for example I have:

@Configuration
@EnableAuthorizationServer
class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
// ...
}

which maps various url, e.g. /oauth/authorize, /oauth/token and so on and servlets seem correctly registered at deploy:

10:23:32,382 INFO  [org.springframework.security.oauth2.provider.endpoint.FrameworkEndpointHandlerMapping] (ServerService Thread Pool -- 81) Mapped "{[/oauth/authorize]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.authorize(java.util.Map<java.lang.String, java.lang.Object>,java.util.Map<java.lang.String, java.lang.String>,org.springframework.web.bind.support.SessionStatus,java.security.Principal)
10:23:32,383 INFO  [org.springframework.security.oauth2.provider.endpoint.FrameworkEndpointHandlerMapping] (ServerService Thread Pool -- 81) Mapped "{[/oauth/authorize],methods=[POST],params=[user_oauth_approval]}" onto public org.springframework.web.servlet.View org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.approveOrDeny(java.util.Map<java.lang.String, java.lang.String>,java.util.Map<java.lang.String, ?>,org.springframework.web.bind.support.SessionStatus,java.security.Principal)
10:23:32,384 INFO  [org.springframework.security.oauth2.provider.endpoint.FrameworkEndpointHandlerMapping] (ServerService Thread Pool -- 81) Mapped "{[/oauth/token],methods=[GET]}" onto public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.OAuth2AccessToken> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.getAccessToken(java.security.Principal,java.util.Map<java.lang.String, java.lang.String>) throws org.springframework.web.HttpRequestMethodNotSupportedException
10:23:32,385 INFO  [org.springframework.security.oauth2.provider.endpoint.FrameworkEndpointHandlerMapping] (ServerService Thread Pool -- 81) Mapped "{[/oauth/token],methods=[POST]}" onto public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.OAuth2AccessToken> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(java.security.Principal,java.util.Map<java.lang.String, java.lang.String>) throws org.springframework.web.HttpRequestMethodNotSupportedException
10:23:32,385 INFO  [org.springframework.security.oauth2.provider.endpoint.FrameworkEndpointHandlerMapping] (ServerService Thread Pool -- 81) Mapped "{[/oauth/check_token]}" onto public java.util.Map<java.lang.String, ?> org.springframework.security.oauth2.provider.endpoint.CheckTokenEndpoint.checkToken(java.lang.String)

Problem is: every access to those URLs is managed by Flow which return its HTML with an error message, e.g.

Could not navigate to 'oauth/authorize'

Removing Flow, the other servlets start working again.

@denis-anisimov
Copy link
Contributor

But do you use Spring Boot or it's a Web MVC application ?

@heruan
Copy link
Member Author

heruan commented Oct 23, 2017

Spring Boot.

@denis-anisimov
Copy link
Contributor

Just for the reference:
I'm able to reproduce this with the following code.

@SpringBootApplication
@EnableAuthorizationServer
@Configuration
@EnableWebSecurity
public class TestServletInitializer {

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

}

Here is the addition in pom.xml to make it work

<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
</dependency>

Apparently the mapping is registered in some specific way (or may be with a low priority) when oauth is enabled in this way. Because as I said: if I register a custom servlet via the ServletRegistrationBean it works as it should.

@denis-anisimov
Copy link
Contributor

And by the way.
The issue has no any relation to Vaadin servlet at all.
Here is the code that also overrides all the oauth stuff:

@SpringBootApplication(exclude = SpringBootAutoConfiguration.class)
@EnableAuthorizationServer
@Configuration
@EnableWebSecurity
public class TestServletInitializer {

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

    @Bean
    public ServletRegistrationBean anotherServlet() {
        ServletRegistrationBean registration = new ServletRegistrationBean(
                new MyServlet(), "/*");
        return registration;
    }

    public static class MyServlet extends HttpServlet {

        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            resp.getWriter().println("Hello");
        }
    }

}

So it's about the way how any servlet is registered and how oauth enables its mappings.

@heruan
Copy link
Member Author

heruan commented Oct 23, 2017

Thank you @denis-anisimov for the investigation, now it's all much clear. The same happens with other Spring "starter" mappings, e.g. with graphql-spring-boot which maps /graphiql but yields when a wildcard mapping (such as Vaadin's) is present.

So I investigated further on the internals of both the OAuth and GraphQL plugins and it seems the URLs that stop working are those mapped via @RequestMapping so it's like the Spring MVC servlet itself yields all its mappings to Vaadin's.

I see a couple of options here (just as out of my mind, probably not realistic):

  1. use/extend the same Spring MVC servlet, hooking up Vaadin routes to it so that @Route("vaadin") behaves as @RequestMapping("vaadin");
  2. find a way to lower the priority of Vaadin servlet so that existing MVC mapping still work.

Last chance is obviously to explicitly map Vaadin Spring servlet to something not root, e.g. /ui/* or separate to standalone applications, of course.

@denis-anisimov
Copy link
Contributor

Yes, it uses @RequestMapping as at it's said in EnableAuthorizationServer's javadoc it's supposed to work with DispatcherServlet.

So we need to find a way to workaround somehow this.

FW8 Spring add-on solves somehow this issue, so it should be possible to do.

@Artur-
Copy link
Member

Artur- commented Oct 24, 2017

The FW8 Spring add-on maps the vaadin servlet to /vaadinServlet, see vaadin/spring@d1e8294#diff-2439dc83bbdeb8f1cfd44d05a7eebbdfR53.

@denis-anisimov
Copy link
Contributor

Yeah, I know.
But then there are a lot of complicated code...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants