0

I am using Spring Boot 2.1.4.Release, Primefaces 6.2, Java 8.

I am using Spring Security for signing in via the page "login.xhtml". All the pages of my application include a menu component(Menu.xhtml). Menu component has the functions such as logout, locale settings etc. In other words, the locale of the pages are set from the Menu.xhtml.

The problem is, when the locale settings are changed in the login page, a "POST /login.xhtml" request is made, which ends with UsernamePasswordAuthenticationFilter doing an authentication check. However, I want to stay on the login page after updating the locale settings. What I wonder is how to avoid this post request.

Here is the related parts of the WebSecurity implementation:



@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final UserDetailServiceImp userDetailsService;

    @Autowired
    public SecurityConfig(UserDetailServiceImp userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // require all requests to be authenticated except for the resources
        http.authorizeRequests()
                .antMatchers("/user/**").hasAuthority(MediaJuryConstants.USER_ROLE)
                .antMatchers("/javax.faces.resource/**").permitAll()
                .antMatchers("/favicon.ico").permitAll()
                .anyRequest().authenticated()
               .and().authorizeRequests().antMatchers("/").anonymous();

        // login
        http.formLogin()
                .loginPage("/login" + MediaJuryConstants.PREFIX)
                .usernameParameter("loginForm:username")
                .passwordParameter("loginForm:password")
                .successHandler(myAuthenticationSuccessHandler())
                .permitAll()
                .failureUrl("/error" + MediaJuryConstants.PREFIX);
        // logout
        http.logout().logoutSuccessUrl("/login" + MediaJuryConstants.PREFIX);
        http.csrf().disable();
    }

...
}

Here is the p:selectOneMenu implementation used for selecting the locale in the Menu.xhtml:


<h:body>

    <h:form id="langForm" styleClass="language-form">
        <p:selectOneMenu value="#{language.localeCode}"  valueChangeListener="#{languageChangeController.valueChanged}"
        onchange="submit()">
            <f:selectItems value="#{language.languageUtils.locales}" var="locale"
                           itemValue="#{locale}" itemLabel="#{msg['layout.'.concat(locale)]}"/>
        </p:selectOneMenu>
    </h:form>
</h:body>

I have a custom layout.xhtml that sets the global component for all pages. Here is the part where the Menu.xhtml is placed:

<h:body>
    <div class="wrap">
        <div class="header">
            <div class="content ui-widget-content">
                <ui:include src="tiles/Menu.xhtml" />
            </div>
        </div>
    </div>
</h:body>

I am not providing the login.xhtml or the LanguageController since I do not think it is useful. But if demanded I can.

Here is my log. In this case the username and password fields are empty:

17:49:34.368 [http-nio-8080-exec-10] DEBUG o.s.s.w.FilterChainProxy - /login.xhtml at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
17:49:34.368 [http-nio-8080-exec-10] DEBUG o.s.s.w.FilterChainProxy - /login.xhtml at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
17:49:34.368 [http-nio-8080-exec-10] DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository - HttpSession returned null object for SPRING_SECURITY_CONTEXT
17:49:34.368 [http-nio-8080-exec-10] DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@797fe12e. A new one will be created.
17:49:34.368 [http-nio-8080-exec-10] DEBUG o.s.s.w.FilterChainProxy - /login.xhtml at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
17:49:34.368 [http-nio-8080-exec-10] DEBUG o.s.s.w.FilterChainProxy - /login.xhtml at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
17:49:34.368 [http-nio-8080-exec-10] DEBUG o.s.s.w.u.m.OrRequestMatcher - Trying to match using Ant [pattern='/logout', GET]
17:49:34.368 [http-nio-8080-exec-10] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Request 'POST /login.xhtml' doesn't match 'GET /logout'
17:49:34.368 [http-nio-8080-exec-10] DEBUG o.s.s.w.u.m.OrRequestMatcher - Trying to match using Ant [pattern='/logout', POST]
17:49:34.368 [http-nio-8080-exec-10] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/login.xhtml'; against '/logout'
17:49:34.368 [http-nio-8080-exec-10] DEBUG o.s.s.w.u.m.OrRequestMatcher - Trying to match using Ant [pattern='/logout', PUT]
17:49:34.368 [http-nio-8080-exec-10] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Request 'POST /login.xhtml' doesn't match 'PUT /logout'
17:49:34.368 [http-nio-8080-exec-10] DEBUG o.s.s.w.u.m.OrRequestMatcher - Trying to match using Ant [pattern='/logout', DELETE]
17:49:34.368 [http-nio-8080-exec-10] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Request 'POST /login.xhtml' doesn't match 'DELETE /logout'
17:49:34.368 [http-nio-8080-exec-10] DEBUG o.s.s.w.u.m.OrRequestMatcher - No matches found
17:49:34.368 [http-nio-8080-exec-10] DEBUG o.s.s.w.FilterChainProxy - /login.xhtml at position 5 of 11 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
17:49:34.368 [http-nio-8080-exec-10] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/login.xhtml'; against '/login.xhtml'
17:49:34.368 [http-nio-8080-exec-10] DEBUG o.s.s.w.a.UsernamePasswordAuthenticationFilter - Request is to process authentication
17:49:34.368 [http-nio-8080-exec-10] DEBUG o.s.s.a.ProviderManager - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
17:49:34.368 [http-nio-8080-exec-10] DEBUG o.s.o.j.JpaTransactionManager - Creating new transaction with name [net.comerge.mediajury.service.UserService.findByEmail]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
17:49:34.369 [http-nio-8080-exec-10] DEBUG o.s.o.j.JpaTransactionManager - Opened new EntityManager [SessionImpl(1298794356<open>)] for JPA transaction
17:49:34.369 [http-nio-8080-exec-10] DEBUG o.h.e.t.i.TransactionImpl - On TransactionImpl creation, JpaCompliance#isJpaTransactionComplianceEnabled == false
17:49:34.369 [http-nio-8080-exec-10] DEBUG o.h.e.t.i.TransactionImpl - begin
17:49:34.369 [http-nio-8080-exec-10] DEBUG o.s.o.j.JpaTransactionManager - Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@773cf27f]
17:49:34.369 [http-nio-8080-exec-10] DEBUG o.h.q.c.i.CriteriaQueryImpl - Rendered criteria query -> select generatedAlias0 from User as generatedAlias0 where generatedAlias0.email=:param0
17:49:34.370 [http-nio-8080-exec-10] DEBUG o.h.SQL - select user0_.id as id1_11_, user0_.email as email2_11_, user0_.locale as locale3_11_, user0_.password as password4_11_, user0_.state as state5_11_, user0_.verification_key as verifica6_11_ from user user0_ where user0_.email=?
17:49:34.371 [http-nio-8080-exec-10] DEBUG o.s.o.j.JpaTransactionManager - Initiating transaction commit
17:49:34.371 [http-nio-8080-exec-10] DEBUG o.s.o.j.JpaTransactionManager - Committing JPA transaction on EntityManager [SessionImpl(1298794356<open>)]
17:49:34.371 [http-nio-8080-exec-10] DEBUG o.h.e.t.i.TransactionImpl - committing
17:49:34.371 [http-nio-8080-exec-10] DEBUG o.s.o.j.JpaTransactionManager - Closing JPA EntityManager [SessionImpl(1298794356<open>)] after transaction
17:49:34.371 [http-nio-8080-exec-10] DEBUG o.s.s.a.d.DaoAuthenticationProvider - User '' not found
17:49:34.371 [http-nio-8080-exec-10] DEBUG o.s.s.w.a.UsernamePasswordAuthenticationFilter - Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials
org.springframework.security.authentication.BadCredentialsException: Bad credentials
    at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:151)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:175)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:200)
    at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:94)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    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:334)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
    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.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
    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.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)
    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.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
    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.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
    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:200)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)
17:49:34.372 [http-nio-8080-exec-10] DEBUG o.s.s.w.a.UsernamePasswordAuthenticationFilter - Updated SecurityContextHolder to contain null Authentication
17:49:34.372 [http-nio-8080-exec-10] DEBUG o.s.s.w.a.UsernamePasswordAuthenticationFilter - Delegating to authentication failure handler org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler@1599e2f9
17:49:34.372 [http-nio-8080-exec-10] DEBUG o.s.s.w.a.SimpleUrlAuthenticationFailureHandler - Redirecting to /error.xhtml
17:49:34.372 [http-nio-8080-exec-10] DEBUG o.s.s.w.DefaultRedirectStrategy - Redirecting to '/error.xhtml'

When the language is changed using the selectOneMenu in login page, a POST /login.xhtml request is made which is caught by Spring Security and interpreted as a login attempt. I wanted to ask, where this POST request is made exactly and if there is a way to avoid POST method and do a GET method. FYI, the code does not enter valueChangeListener in the described case.

I would really appreciate some advice. Thank you in advance!

Kukeltje
  • 12,223
  • 4
  • 24
  • 47
idil
  • 11
  • 3
  • _"I wanted to ask, where this POST request is made exactly "_ do a view source on the client and you know – Kukeltje May 03 '19 at 16:26
  • Oh and the form is a jsf one, mot a PF one. And when you try a `f:selectOneMenu`, dos it work? Debug, narrow down... – Kukeltje May 03 '19 at 16:32
  • Thank you for your answer. I updated the form as you have suggested to use the namespace `h="http://java.sun.com/jsf/html"` for the selectOneMenu. However, still a POST login.xhtml request is being made. To clarify my question, 1.The form results in this POST request 2. Normally the Menu.xhtml works fine. What I want to know is if I can change the form such that a GET login.xhtml request will be made instead of a POST request. If I can achieve this, the UsernamePasswordAuthenticationFilter will not be triggered and there will be no failed login attempt. – idil May 06 '19 at 08:44
  • Oh, yes, I meant the h:selectOneMenu, sorry. 1: Yes, as designed. 2: No, and even if it somehow could, it would only be a workaround on how you designed your page. Just check for a submission of a specific form in the loginfilter. See the http request in the browser on how what info is there and what can be used. See the duplicate but assign an id to the form and check that... – Kukeltje May 06 '19 at 08:52
  • Possible duplicate of [How to determine which form has been submited and to validate them in a servlet](https://stackoverflow.com/questions/14905475/how-to-determine-which-form-has-been-submited-and-to-validate-them-in-a-servlet) – Kukeltje May 06 '19 at 08:54
  • Thank you. Since there is no other comments, I wrote an answer based on your comment. – idil May 14 '19 at 09:44

1 Answers1

0

I could not find a solution on the xhtml basis but as Kukeltje suggested a custom implementation of the UsernamePasswordAuthenticationFilter solves the issue.

The form component is as given,

  <h:form method="get" id="langForm" styleClass="language-form">
        <p:selectOneMenu value="#{languageController.localeCode}"  valueChangeListener="#{languageChangeController.valueChanged}"
        onchange="submit()">
            <f:selectItems value="#{languageController.languageUtils.locales}" var="locale"
                           itemValue="#{locale}" itemLabel="#{msg['layout.'.concat(locale)]}"/>
        </p:selectOneMenu>
    </h:form>

Selecting an item results in a POST request of the current page we are on login.xhtml:POST /login.xhtml. This request enters the doFilter of the UsernamePasswordAuthenticationFilter. The method does an authentication check if requiresAuthentication method returns true. Thus, overriding this method is sufficient.

public class CustomUsernamePasswordFilter extends UsernamePasswordAuthenticationFilter {

    @Override
    protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
        if (request.getParameter("langForm") != null) {
            return false;
        }
        return super.requiresAuthentication(request, response);
    }
}

langForm is the id of the form introduced in the xhtml.

idil
  • 11
  • 3