Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

spring-boot form login/logout not working (path not found)

I have the following spring-boot setup with form login and basic auth (which partly works - I can login to basic auth OK and the login form appears). I only created the login form because I could not even get the login to render unless I did that.

NOTE: Complete code here if it helps: https://github.com/azeckoski/lti_starter

Here is my Application config (the relevant part)

@Order(23) // MED
@Configuration
public static class FormLoginConfigurationAdapter extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/form").authorizeRequests().anyRequest().authenticated()
                .and().formLogin().permitAll().loginPage("/form/login").loginProcessingUrl("/form/login")
                .and().logout().logoutUrl("/form/logout").invalidateHttpSession(true).logoutSuccessUrl("/");
    }
}

@Order(45) // LOW
@Configuration
public static class BasicAuthConfigurationAdapter extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/basic").authorizeRequests().anyRequest().authenticated()
            .and().httpBasic();
    }
}

And the form login controller:

@Controller
@RequestMapping("/form")
public class FormController extends BaseController {

    @RequestMapping({"", "/"})
    public String home(HttpServletRequest req, Principal principal, Model model) {
        commonModelPopulate(req, principal, model);
        model.addAttribute("name", "form");
        model.addAttribute("canLogout", true);
        req.getSession().setAttribute("login", "form");
        return "home"; // name of the template
    }

    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String login(HttpServletRequest req) {
        log.info("login: " + req);
        return "login";
    }

    // Login form with error
    @RequestMapping(value = "/login", params = "error=true")
    public String loginError(HttpServletRequest req, Model model) {
        log.info("login-error: " + req);
        model.addAttribute("loginError", true);
        return "login";
    }
}

And my login form in thymeleaf:

<form th:action="@{/form/login}" method="post">
    <label for="username">Username</label>:
    <input type="text" id="username" name="username"/> <br/>
    <label for="password">Password</label>:
    <input type="password" id="password" name="password"/> <br/>
    <input type="submit" value="Login"/>
</form>

And my logout form in thymeleaf:

<div sec:authorize="isAuthenticated()">
    <form th:if="${canLogout}" th:action="@{/form/logout}" method="post">
        <input type="submit" value="Logout"/>
    </form>
    <div>User: <span sec:authentication="name">AZ</span></div>
    <div>Roles: <span sec:authentication="principal.authorities">[ROLE_USER, ROLE_ADMIN]</span></div>
    <div>Login: <span th:text="${session.login}"/></div>
</div>

However, the login (and logout) does not work.

When I try to access the login processing path (submit the login form) I get the following error in the log (with logging.level.org.springframework.security=DEBUG):

2014-07-14 11:10:41.635 DEBUG 30608 --- [qtp502953897-14] o.s.s.web.util.matcher.OrRequestMatcher : No matches found 2014-07-14 11:10:41.635 DEBUG 30608 --- [qtp502953897-14] o.s.security.web.FilterChainProxy : /form/login has no matching filters

and on screen:

There was an unexpected error (type=Bad Request, status=400). Parameter conditions "error=true" not met for actual request parameters: password={admin}, username={admin}

When I try to access the logout processing path (post to /form/logout) I get the following error in the log (with logging.level.org.springframework.security=DEBUG):

2014-07-14 11:13:21.731 DEBUG 30778 --- [qtp865931040-14] o.s.s.web.util.matcher.OrRequestMatcher : No matches found 2014-07-14 11:13:21.731 DEBUG 30778 --- [qtp865931040-14] o.s.security.web.FilterChainProxy : /form/logout has no matching filters 2014-07-14 11:13:21.738 WARN 30778 --- [qtp865931040-14] o.s.web.servlet.PageNotFound : Request method 'POST' not supported

and on screen:

There was an unexpected error (type=Method Not Allowed, status=405). Request method 'POST' not supported

From all the docs and examples I can find it would seem that this should work. It might be because I am trying to manage multiple types of authentication methods but I would think this is a fairly common use case. I have a tried a ton of variants with no progress so I am open to any ideas or suggestions.

Am I misunderstanding? Shouldn't there be a default login and logout processor created for me at /form/login and /form/logout?

like image 795
Aaron Zeckoski Avatar asked Oct 20 '22 05:10

Aaron Zeckoski


2 Answers

SOLUTION EXAMPLE

Here's an example which shows how it should be done (including the NoAuthConfigurationAdapter which will allow the security info to be accessed outside the /form path. Order is important.

@Order(23) // MED
@Configuration
public static class FormLoginConfigurationAdapter extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/form/**").authorizeRequests().anyRequest().authenticated()
                .and().formLogin().permitAll().loginPage("/form/login").loginProcessingUrl("/form/login")
                .and().logout().logoutUrl("/form/logout").invalidateHttpSession(true).logoutSuccessUrl("/");
    }
}

@Order(67) // LOWEST
@Configuration
public static class NoAuthConfigurationAdapter extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // this ensures security context info (Principal, sec:authorize, etc.) is accessible on all paths
        http.antMatcher("/**").authorizeRequests().anyRequest().permitAll();
    }
}
like image 184
Aaron Zeckoski Avatar answered Oct 23 '22 11:10

Aaron Zeckoski


You're almost a there I think, but there are 2 issues:

  • you haven't mapped '/form/login' to any of your security filters. The ant matcher that looks like it wants to do that is just matching '/form' (should be '/form/**'?)

  • you haven't set the login processing URL (path really). By default it is "/login" so if your form posted to "/login" instead of "/form/login" it would work. Or there is a method in the formLogin() config API to set the path.

like image 25
Dave Syer Avatar answered Oct 23 '22 09:10

Dave Syer