简体   繁体   English

Spring Boot OAuth2隐式流+表单登录和请求方法'POST'不支持错误

[英]Spring Boot OAuth2 implicit flow + form login and Request method 'POST' not supported error

In my Spring Boot application I'm trying to configure OAuth2 implicit flow. 在我的Spring Boot应用程序中,我试图配置OAuth2隐式流。 For this purpose I'm trying to configure custom login form. 为此,我正在尝试配置自定义登录表单。

This is my config: 这是我的配置:

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/login").setViewName("login");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
    }

}

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private SocialAuthenticationSuccessHandler socialAuthenticationSuccessHandler;

    @Autowired
    private DBUserDetailsService userDetailsService;

    @Value("${social.postLogin.url}")
    private String postLoginUrl;

    @Override
    public void configure(WebSecurity web) throws Exception {
        // Spring Security ignores request to static resources such as CSS or JS
        // files.
        web.ignoring().antMatchers("/static/**");
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // @formatter:off
        http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);

        // Set a custom successHandler on the SocialAuthenticationFilter
        final SpringSocialConfigurer socialConfigurer = new SpringSocialConfigurer();
        socialConfigurer.addObjectPostProcessor(new ObjectPostProcessor<SocialAuthenticationFilter>() {
            @Override
            public <O extends SocialAuthenticationFilter> O postProcess(O socialAuthenticationFilter) {
                socialAuthenticationFilter.setAuthenticationSuccessHandler(socialAuthenticationSuccessHandler);
                socialAuthenticationFilter.setPostLoginUrl(postLoginUrl);
                return socialAuthenticationFilter;
            }
        });

        http
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
            .authorizeRequests()

            .antMatchers("/oauth/authorize").authenticated()
            //Anyone can access the urls
            .antMatchers("/auth/**").permitAll()
            .antMatchers("/actuator/health").permitAll()
            .antMatchers("/actuator/**").hasAuthority("PERMISSION_READ_ACTUATOR_DATA")
            .antMatchers("/login").permitAll()
            .anyRequest().authenticated()
        .and()
            .formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/login")
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
        //Adds the SocialAuthenticationFilter to Spring Security's filter chain.
        .and()
            // apply the configuration from the socialConfigurer (adds the SocialAuthenticationFilter)
            .apply(socialConfigurer);
        // @formatter:on
    }

    /**
     * Configures the authentication manager bean which processes authentication
     * requests.
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

}

@Configuration
public class OAuth2ServerConfig {

    private static final String RESOURCE_ID = "restservice";

    @Autowired
    private DBUserDetailsService userDetailsService;

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter() {

            @Override
            public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
                DBUserDetails user = (DBUserDetails) authentication.getUserAuthentication().getPrincipal();
                final Map<String, Object> additionalInfo = new HashMap<>();
                additionalInfo.put("user_id", user.getUser().getId());
                ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
                OAuth2AccessToken enhancedToken = super.enhance(accessToken, authentication);
                return enhancedToken;
            }

        };

        converter.setSigningKey("123");

        DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
        DefaultUserAuthenticationConverter userTokenConverter = new DefaultUserAuthenticationConverter();
        userTokenConverter.setUserDetailsService(userDetailsService);
        accessTokenConverter.setUserTokenConverter(userTokenConverter);

        converter.setAccessTokenConverter(accessTokenConverter);

        return converter;
    }

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

    @Configuration
    @EnableAuthorizationServer
    protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

        @Autowired
        @Qualifier("authenticationManagerBean")
        private AuthenticationManager authenticationManager;

        @Autowired
        private TokenStore tokenStore;

        @Autowired
        private TokenEnhancer tokenEnhancer;

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            // @formatter:off
            endpoints
                .tokenStore(tokenStore)
                .tokenEnhancer(tokenEnhancer)
                .authenticationManager(this.authenticationManager);
            // @formatter:on
        }

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            // @formatter:off
            clients
                .inMemory()
                    .withClient("clientapp")
                        .authorizedGrantTypes("password","refresh_token")
                        //.authorizedGrantTypes("implicit")
                        .authorities("ROLE_CLIENT")
                        .scopes("read", "write")
                        .resourceIds(RESOURCE_ID)
                        .secret("123456")
                    .and()
                    .withClient("clientapp1")
                        .authorizedGrantTypes("implicit")
                        .scopes("read", "write")
                        .autoApprove(true);
            // @formatter:on
        }

    }

    @Configuration
    @EnableResourceServer
    protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

        @Autowired
        private ResourceServerTokenServices tokenService;

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            // @formatter:off
            resources           
                .resourceId(RESOURCE_ID)
                .tokenServices(tokenService);
            // @formatter:on
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            // @formatter:off
            http
                .authorizeRequests()
                    .antMatchers("/profile/*").authenticated()
                    .and().csrf()
                    .disable().sessionManagement().sessionCreationPolicy(STATELESS);
            // @formatter:on
        }

    }

}

login.html page Thymeleaf template: login.html页面Thymeleaf模板:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <title>Spring Security Example </title>
    </head>
    <body>
        <div th:if="${param.error}">
            Invalid username and password.
        </div>
        <div th:if="${param.logout}">
            You have been logged out.
        </div>
        <form th:action="@{/login}" method="post">
            <div><label> User Name : <input type="text" name="username"/> </label></div>
            <div><label> Password: <input type="password" name="password"/> </label></div>
            <div><input type="submit" value="Sign In"/></div>
        </form>
    </body>
</html>

Maven artifact versions: Maven工件版本:

<spring.boot.version>1.4.0.RELEASE</spring.boot.version>
<spring-security-core.version>4.1.3.RELEASE</spring-security-core.version>
<spring-security-oauth2.version>2.0.11.RELEASE</spring-security-oauth2.version>

Right now, when I'm trying to access following url: 现在,当我尝试访问以下网址时:

http://localhost:8080/oauth/authorize?response_type=implicit&client_id=clientapp1

I'm successfully redirected to my login page at http://localhost:8080/login but when I enter username/password and press "Sign in" button I'm getting following error: 我已成功重定向到http://localhost:8080/login登录页面,但是当我输入用户名/密码并按“登录”按钮时,出现以下错误:

Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.
Sat Sep 24 21:19:44 EEST 2016
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'POST' not supported

What am I doing wrong and how to fix this issue ? 我在做什么错以及如何解决此问题?

UPDATED 更新

In debug I can see the following output: 在调试中,我可以看到以下输出:

DispatcherServlet with name 'dispatcherServlet' processing POST request for [/login]
2016-09-25 10:04:43 [http-nio-8080-exec-2] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping -
                Looking up handler method for path /login
2016-09-25 10:04:43 [http-nio-8080-exec-2] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping -
                Did not find handler method for [/login]
2016-09-25 10:04:43 [http-nio-8080-exec-2] DEBUG o.s.w.s.h.SimpleUrlHandlerMapping -
                Mapping [/login] to HandlerExecutionChain with handler [org.springframework.web.servlet.mvc.ParameterizableViewController@c85e70] and 1 interceptor
2016-09-25 10:04:43 [http-nio-8080-exec-2] DEBUG o.s.w.s.m.a.ResponseStatusExceptionResolver -
                Resolving exception from handler [org.springframework.web.servlet.mvc.ParameterizableViewController@c85e70]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported
2016-09-25 10:04:43 [http-nio-8080-exec-2] DEBUG o.s.w.s.m.s.DefaultHandlerExceptionResolver -
                Resolving exception from handler [org.springframework.web.servlet.mvc.ParameterizableViewController@c85e70]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported
2016-09-25 10:04:43 [http-nio-8080-exec-2] WARN  o.s.web.servlet.PageNotFound -
                Request method 'POST' not supported
2016-09-25 10:04:43 [http-nio-8080-exec-2] DEBUG o.s.s.w.h.writers.HstsHeaderWriter -
                Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@16580a4
2016-09-25 10:04:43 [http-nio-8080-exec-2] DEBUG o.s.web.servlet.DispatcherServlet -
                Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2016-09-25 10:04:43 [http-nio-8080-exec-2] DEBUG o.s.web.servlet.DispatcherServlet -
                Successfully completed request

Also, there is another question about this issue with no answer provided usernamepasswordauthenticationfilter not getting invoked in spring security with oauth2 and formlogin 另外,还有一个关于此问题的问题,没有提供答案, 用户名passwordauthenticationfilter不会在osecurity2和formlogin的Spring Security中被调用

I had this same problem today, your answer didn't help me, but I found a solution to my problem. 今天我遇到了同样的问题,您的回答并没有帮助我,但是我找到了解决问题的方法。

Hopefully this helps someone else in the same position. 希望这可以帮助处于相同位置的其他人。

The problem for my app is that I'm using Basic Auth to cover most of my web application's protected resources, but I'm using OAuth2 in order to protect a public API that I'm allowing people to connect to. 我的应用程序的问题在于,我正在使用基本身份验证来覆盖我的大多数Web应用程序受保护的资源,但是我正在使用OAuth2来保护允许人们连接的公共API。

So this caused my application to have two separate public void configure(HttpSecurity http) methods in two separate configuration files. 因此,这导致我的应用程序在两个单独的配置文件中具有两个单独的public void configure(HttpSecurity http)方法。

The solution was to add an Order annotation to the configuration classes. 解决方案是在配置类中添加Order注释。

So my main configuration class that used Basic Auth had Order(1) assigned to it, and the ResourceServerConfigurerAdapter had an Order(2) annotation assigned to it. 因此,我使用基本身份验证的主要配置类已为其分配了Order(1) ,而ResourceServerConfigurerAdapter已为其分配了Order(2)注释。

For example: 例如:

@EnableWebSecurity
@Order(1)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
  /// ... my normal configuration information
  protected void configure(HttpSecurity http) {
    // my basic auth config
  }
}

@Configuration
@Order(2)
public class OAuth2ServerConfig
{
  @Configuration
  @EnableResourceServer
  @Order(3)
  protected static class Oauth2ServerConfig extends ResourceServerConfigurerAdapter
  {
    @Override
    public void configure(HttpSecurity http) throws Exception
    {
      http.authorizeRequests()
      .antMatchers("/oauth/**").permitAll()
      .antMatchers("/api/v1/**").access("#oauth2.hasScope('read')");
    }
  }


  @Configuration
  @EnableAuthorizationServer
  protected static class AuthorizationServer extends AuthorizationServerConfigurerAdapter
  {
    // authorization server settings
  }
}

It was my issue with a wrong configuration of ResourceServer. 我的问题是ResourceServer的配置错误。

With a following configuration everything is working fine: 使用以下配置,一切运行正常:

@Configuration
@EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    @Autowired
    private ResourceServerTokenServices tokenService;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        // @formatter:off
        resources           
            .resourceId(RESOURCE_ID)
            .tokenServices(tokenService);
        // @formatter:on
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http                
            .antMatcher("/api/**" )
            .authorizeRequests().anyRequest().authenticated()
            .and()
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(STATELESS);
        // @formatter:on
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM