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?
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();
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With