spring-boot form login/logout not working (path not found)
java, spring-boot, spring-mvc, spring-security, thymeleaf
Solution
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();
}
}
Problem
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?