FreeMarker template error with csrf - spring

It was necessary in the project to configure authentication with Spring Security + Freemarker. I have login page
<#-- #ftlvariable name="_csrf"
type="org.springframework.security.web.csrf.CsrfToken" -->
<#-- #ftlvariable name="error" type="java.util.Optional<String>" -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Log in</title>
</head>
<body>
<nav role="navigation">
<ul>
<li>Home</li>
</ul>
</nav>
<h1>Log in</h1>
<p>You can use: demo#localhost / demo</p>
<form role="form" action="/login" method="post">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<div>
<label for="email">Email address</label>
<input type="email" name="email" id="email" required autofocus/>
</div>
<div>
<label for="password">Password</label>
<input type="password" name="password" id="password" required/>
</div>
<div>
<label for="remember-me">Remember me</label>
<input type="checkbox" name="remember-me" id="remember-me"/>
</div>
<button type="submit">Sign in</button>
</form>
</body>
</html>
This page is handled by LoginController
#Controller
public class LoginController {
#RequestMapping(value = "/login", method = RequestMethod.GET)
public ModelAndView getLoginPage(#RequestParam Optional<String> error) {
return new ModelAndView("login", "error", error);
}
}
The configuration Spring Security
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#ComponentScan(basePackages = "projectpackages.janus")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/public/**").permitAll()
.antMatchers("/users/**").hasAuthority("ADMINISTRATOR")
.anyRequest().fullyAuthenticated()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error")
.usernameParameter("email")
.permitAll()
.and()
.logout().logoutUrl("/logout")
.deleteCookies("remember-me")
.logoutSuccessUrl("/")
.permitAll()
.and()
.rememberMe();
}
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
}
When I turn to the login page, freemarker throws an exception
FreeMarker template error:
The following has evaluated to null or missing:
==> _csrf [in template "login.ftl" at line 21, column 34]
----
FTL stack trace ("~" means nesting-related):
- Failed at: ${_csrf.parameterName} [in template "login.ftl" at line 21,
column 32]
----
Help to understand the possible reasons of this error. Search on the Internet did not succeed(

Related

spring security login page does not load resources

I have a problem with my web application done with spring security, it has a login page:
<%# page contentType="text/html;charset=UTF-8" language="java" %>
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%# taglib prefix = "form" uri = "http://www.springframework.org/tags/form"%>
<%# page isELIgnored="false"%>
........
<link rel="stylesheet" type="text/css" href="https://cdn.usebootstrap.com/bootstrap/4.4.1/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="resource/css/css-font-awesome.css">
<link rel="stylesheet" type="text/css" href="resource/css/css-util.css">
<link rel="stylesheet" type="text/css" href="resource/css/css-fonts.css">
<link rel="stylesheet" type="text/css" href="resource/css/css-custom.css">
<!--===============================================================================================-->
<meta name="robots" content="noindex, follow">
</head>
<body>
<c:if test="${not empty error}">
<div style="color: #ff0000;">Errore nel login controllare username / password</div>
</c:if>
<div class="container">
<div class="row">
<div class="form_login">
<form action="<c:url value="/performlogin"/>" method="post">
<div class="text-center"><img class="img-fluid" src="resource/images/images-logo-softpulizie.png"></div>
<hr>
<br>
<div class="form-group">
<input class="form-control" placeholder="Username" name="username" class="input form-control" id="userid" type="text">
</div>
<div class="form-group">
<input class="form-control" placeholder="Password" name="password" class="input form-control" id="pwd" type="password">
</div>
<div class="row">
<div class="col-8">
<span class="pt-2">Hai dimenticato la password?<br>
Clicca qui</span>
</div>
<div class="col-4 align-right">
<button type="submit" class="btn btn-primary" id="login">Login</button>
</div>
</div>
</form>
</div>
</div>
</div>
<!--===============================================================================================-->
<script src="resource/js/3.2.1-jquery.min.js"></script>
<script src="resource/js/js-bootstrap.min.js"></script>
</body>
</html>
and my configuration class that permits loading url and forwardin to login page is:
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
public void configAuthentication(AuthenticationManagerBuilder authBuilder) throws Exception {
authBuilder
.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("SELECT username, password, enabled FROM Utenti WHERE username = ?")
.authoritiesByUsernameQuery("SELECT username, role FROM Utenti WHERE username = ?");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.authorizeRequests().anyRequest().hasAnyRole("USER","ADMIN")
.and()
.authorizeRequests().antMatchers("/login**").permitAll()
.and()
.authorizeRequests().antMatchers("/resource**").permitAll()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/performlogin")
.usernameParameter("username")
.passwordParameter("password")
.defaultSuccessUrl("/home", true)
.failureUrl("/login?error=true")
.permitAll()
.and()
.logout()
.logoutUrl("/perform_logout")
.deleteCookies("JSESSIONID")
.permitAll();
}
}
but when loaded the login page it does not load all the css, javascript and images.
for the other pages displayed after the login the resources are loaded correctly.
The resources are all put in place under the resources folder under src/main/java of my maven project.
What could be the problem?
Thanks
I solved changing the authorize like this:
#Override
protected void configure(HttpSecurity http) throws Exception {
String[] staticResources = {
"/css/**",
"/scripts/**",
"/resource/images/images-logo-softpulizie.png"
};
http.csrf()
.disable()
.authorizeRequests()
.antMatchers(staticResources).permitAll()
.antMatchers("/login**").permitAll()
.antMatchers("/performlogin")
.hasAnyRole("USER", "ADMIN")
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/performlogin")
.usernameParameter("username")
.passwordParameter("password")
.defaultSuccessUrl("/home", true)
.failureUrl("/login?error=true")
.permitAll()
.and()
.logout()
.logoutUrl("/perform_logout")
.deleteCookies("JSESSIONID")
.permitAll();
}
Try removing the multiple .authorizeRequests()...and() and condense to:
.authorizeRequests(authorize -> authorize
.antMatchers("/login**", "/resource**").permitAll()
.anyRequest().hasAnyRole("USER","ADMIN")
}
...
Order does matter. Also, you don't want to call .authorizeRequests() multiple times.
You can read more on the latest way to do this (via .authorizeHttpRequests() which uses the new AuthorizationFilter in 5.5) here: Authorize HttpServletRequests with AuthorizationFilter.
If that doesn't work, feel free to update the question and we can see if something else is going on.

Spring Security login action does not set-cookie

I have a spring boot project on which I have set up spring security to take care of the authentication, like is shown below (the different classes are on different files).
#Configuration
#EnableWebSecurity
class SecurityConfiguration(
private val userAuthenticationService: UserAuthenticationService)
: WebSecurityConfigurerAdapter(){
#Throws(Exception::class)
override fun configure(httpSecurity: HttpSecurity) {
httpSecurity
.cors()
.and()
.csrf().disable()
.authorizeRequests()
//.antMatchers("/#/login").permitAll()
.antMatchers(HttpMethod.POST,Constants.USERS_BASE_PATH).permitAll()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.formLogin()
//.loginPage("/#/login")
.loginProcessingUrl("/v0/login")
.permitAll()
.and()
.logout()
.logoutUrl("/v0/logout")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.permitAll();
}
override fun configure(auth: AuthenticationManagerBuilder?) {
auth?.userDetailsService(userAuthenticationService)
}
#Bean
fun getPasswordEncoder(): PasswordEncoder? {
return BCryptPasswordEncoder()
}
}
#Service
class UserAuthenticationService(private val userRepository: UserRepository) : UserDetailsService{
override fun loadUserByUsername(username: String?): UserDetails {
val user = userRepository.findByUsername(username!!)
return UserAuthenticationDetails(user.username, user.password)
}
}
The problem is when I perform the login action (below) it returns an HTML form to perform sign in with a 200 status code and the response does not contain the "Set-Cookie" header which would contain the cookie for following requests.
POST /v0/login HTTP/1.1
Host: localhost:8080
Authorization: Basic dXNlciAxOjEyMzQ1
Content-Type: application/x-www-form-urlencoded
username=user%201&password=12345
This is the HTML response message:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Please sign in</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<link href="https://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet" crossorigin="anonymous"/>
</head>
<body>
<div class="container">
<form class="form-signin" method="post" action="/v0/login">
<h2 class="form-signin-heading">Please sign in</h2>
<div class="alert alert-danger" role="alert">Result must not be null!</div> <p>
<label for="username" class="sr-only">Username</label>
<input type="text" id="username" name="username" class="form-control" placeholder="Username" required autofocus>
</p>
<p>
<label for="password" class="sr-only">Password</label>
<input type="password" id="password" name="password" class="form-control" placeholder="Password" required>
</p>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form>
</div>
</body></html>
I've tried several variations of the spring security configurations but i'm definitely missing something.
What does return a "Set-Cookie" header with the correct cookie is if I try any other endpoint and just send the Authorization header with basic authentication. But for some reason this does not happen on the login endpoint.
Am I just making the login HTTP request wrong? or do I have the wrong configurations?

Springboot authentication issue

I have a spring boot application with spring security configured. I have redirected the login request to http://localhost:8000 where I'm running my front-end on a python server. Now when I try to post the login to my springboot application, it doesn't work. Even when I try from my postman, it says 405 error. How can I get this working. It works from /login if I put it as html in the same project but not from the python server or postman. What is the difference.
"message": "Request method 'POST' not supported",
"path": "/login"
Form Data
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Demo</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<!-- Add page specific code/html START -->
<div class="container">
<h1 th:text="#{welcome.message}"></h1>
<form class="form-signin" name="loginForm" th:action="#{/login}" action="/login" method="POST">
<h2 class="form-signin-heading">Please sign in</h2>
<label for="username" class="sr-only">Email address</label>
<input type="text" name="username" id="username" class="form-control" placeholder="Username" required="required" autofocus="autofocus" />
<label for="password" class="sr-only">Password</label>
<input type="password" name="password" id="password" class="form-control" placeholder="Password" required="required" />
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form>
</div> <!-- /container -->
</body>
</html>
HTML code hosted on photon server
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Assessment App</title>
<link href="../css/bootstrap.min.css" rel="stylesheet">
<link href="../css/main.css" rel="stylesheet">
</head>
<body>
<div class="container-fluid">
<div class="panel panel-default main-header">
<div class="panel-body">
<div class ="pull-left">Assessments</div>
</div>
</div>
<div class="row">
<div class="login-container col-md-4 col-md-offset-4 col-sm-10 col-sm-offset-1 col-xs-12 col-xs-offset-0">
<div class="panel panel-login">
<div class="panel-heading">
<div class="panel-title">Sign In</div>
</div>
<div class="panel-body">
<form id="loginform" class="form-horizontal" role="form">
<div class="input-group assessment-input-group">
<span class="input-group-addon"><i class="glyphicon glyphicon-user"></i></span>
<input id="login-username" type="text" class="form-control" name="username" value="" placeholder="Username">
</div>
<div class="input-group assessment-input-group">
<span class="input-group-addon"><i class="glyphicon glyphicon-lock"></i></span>
<input id="login-password" type="password" class="form-control" name="password" placeholder="Password">
</div>
<div class="form-group">
<div class="col-sm-12 controls">
<input class="btn btn-primary" type="submit" value="Login">
</div>
</div>
</form>
<div class="login-form-error-text hidden">Invalid credentials</div>
</div>
</div>
</div>
</div>
</div>
<script src="../javascript/jquery-3.3.1.min.js"></script>
<script src ="../javascript/bootstrap.min.js"></script>
<script src="../javascript/lodash.min.js"></script>
<script src="../javascript/login.js"></script>
</body>
</html>
Corresponding js
$(document).ready(function () {
$('#loginform').submit(function (event) {
event.preventDefault();
$.ajax({
url : 'http://localhost:8080/j_spring_security_check',
type : 'POST',
contentType : 'application/json',
data : JSON.stringify({ j_username : $('#login-username').val(), j_password : $('#login-password').val() }),
success : function () {
window.location.href = '../html/assessment.html';
},
error : function () {
event.preventDefault();
alert('failed');
}
});
});
$('.form-tab-header').on('click', function () {
$('.login-form-error-text').addClass('hidden');
$('.form-tab-header').removeClass('active');
$(this).addClass('active');
$('.form-horizontal').addClass('hidden');
$('.' + $(this).attr('id') + '-content').removeClass('hidden');
});
});
Security Config
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${ldap.urls}")
private String ldapUrls;
#Value("${ldap.base.dn}")
private String ldapBaseDn;
#Value("${ldap.user.dn.pattern}")
private String ldapUserDnPattern;
#Value("${ldap.enabled}")
private String ldapEnabled;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login**").permitAll()
.antMatchers("/assessments/**").fullyAuthenticated()
.antMatchers("/").permitAll()
.and()
.formLogin()
//.loginPage("http://htmlcode.s3-website.us-east-2.amazonaws.com")
.loginPage("http://localhost:8000")
.loginProcessingUrl("/j_spring_security_check")
.usernameParameter("j_username")
.passwordParameter("j_password")
//.loginPage("/login")
.failureUrl("/login?error")
.permitAll()
.and()
.logout()
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.permitAll();
}
#Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/register");
// .antMatchers("/assessments/**");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
if(Boolean.parseBoolean(ldapEnabled)) {
auth.ldapAuthentication()
.userDetailsContextMapper(userDetailsContextMapper())
.userDnPatterns(ldapUserDnPattern)
.contextSource()
.url(ldapUrls+ldapBaseDn);
}
}
#Bean
public UserDetailsContextMapper userDetailsContextMapper() {
return new LdapUserDetailsMapper() {
#Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
UserDetails details = super.mapUserFromContext(ctx, username, authorities);
return details;
}
};
}
#Bean
CorsFilter corsFilter() {
CorsFilter filter = new CorsFilter();
return filter;
}
}
You have forgotten to include csrf values.this is a security precaution mechanism to prevent cross site attacks. your have two options as a workaround :
1.Disabling CSRF:
as csrf is enabled by default, both POSTs and PUT Http methods are not allowed with CSRF enabled.
for disabling it you should add this to your security config
.csrf().disable()
for example you could have such thing:
http.
.csrf().disable().
authorizeRequests()
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.and()
.formLogin().loginPage("/login").failureUrl("/login?error")
.usernameParameter("username").passwordParameter("password")
.and()
.logout().logoutSuccessUrl("/login?logout")
.and()
.exceptionHandling().accessDeniedPage("/403");
2.Send csrf token values :
If you are using login page with login form, we need to always include the CSRF token in the login form as a hidden parameter manually in the code:
<input
type="hidden"
th:name="${_csrf.parameterName}"
th:value="${_csrf.token}" />
if you want to login by ajax you should also include these two parameters included:
first hold the values in some variables:
<script type="text/javascript">
var csrfParameter = '${_csrf.parameterName}';
var csrfToken = '${_csrf.token}';
</script>
then include those into
var jsonParams = {};
jsonParams['parentId'] = 1;
jsonParams[csrfParameter] = csrfToken;
// include other values pass ,user, etc.
$.ajax({
type: 'POST',
cache: false,
url: /login,
data: jsonParams,
dataType = 'json',
contentType = 'application/json',
...
});
More Information
https://www.baeldung.com/spring-security-csrf
Ajax POST results in a 405 (Method Not Allowed) - Spring MVC
https://matthewbusche.com/2016/08/06/using-csrf-with-spring-security-and-ajax-calls/
Spring Security - 405 Request Method 'POST' Not Supported
HTTP 405 Not Allowed - Spring Boot + Spring Security
405 Method Not Allowed for POST
Spring Boot + Security + Thymeleaf and CSRF token not injected automatically

Springboot authentication issue with customer login

I have a spring boot application with spring security configured. I have redirected the login request to http://localhost:8000 where I'm running my front-end on a python server. Now when I try to post the login to my springboot application, it doesn't work. I looked into some posts online and changed the login path to /j_spring_security_check but it doesn't even seem to be trying to login as I don't see any logs in the console. Its taking me to login?error .Are there any other places where I can check the logs. Can I debug this somehow from some springboot classes.
Form Data
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Demo</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<!-- Add page specific code/html START -->
<div class="container">
<h1 th:text="#{welcome.message}"></h1>
<form class="form-signin" name="loginForm" th:action="#{/login}" action="/login" method="POST">
<h2 class="form-signin-heading">Please sign in</h2>
<label for="username" class="sr-only">Email address</label>
<input type="text" name="username" id="username" class="form-control" placeholder="Username" required="required" autofocus="autofocus" />
<label for="password" class="sr-only">Password</label>
<input type="password" name="password" id="password" class="form-control" placeholder="Password" required="required" />
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form>
</div> <!-- /container -->
</body>
</html>
HTML code hosted on photon server
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Assessment App</title>
<link href="../css/bootstrap.min.css" rel="stylesheet">
<link href="../css/main.css" rel="stylesheet">
</head>
<body>
<div class="container-fluid">
<div class="panel panel-default main-header">
<div class="panel-body">
<div class ="pull-left">Assessments</div>
</div>
</div>
<div class="row">
<div class="login-container col-md-4 col-md-offset-4 col-sm-10 col-sm-offset-1 col-xs-12 col-xs-offset-0">
<div class="panel panel-login">
<div class="panel-heading">
<div class="panel-title">Sign In</div>
</div>
<div class="panel-body">
<form id="loginform" class="form-horizontal" role="form">
<div class="input-group assessment-input-group">
<span class="input-group-addon"><i class="glyphicon glyphicon-user"></i></span>
<input id="login-username" type="text" class="form-control" name="username" value="" placeholder="Username">
</div>
<div class="input-group assessment-input-group">
<span class="input-group-addon"><i class="glyphicon glyphicon-lock"></i></span>
<input id="login-password" type="password" class="form-control" name="password" placeholder="Password">
</div>
<div class="form-group">
<div class="col-sm-12 controls">
<input class="btn btn-primary" type="submit" value="Login">
</div>
</div>
</form>
<div class="login-form-error-text hidden">Invalid credentials</div>
</div>
</div>
</div>
</div>
</div>
<script src="../javascript/jquery-3.3.1.min.js"></script>
<script src ="../javascript/bootstrap.min.js"></script>
<script src="../javascript/lodash.min.js"></script>
<script src="../javascript/login.js"></script>
</body>
</html>
Corresponding js
$(document).ready(function () {
$('#loginform').submit(function (event) {
event.preventDefault();
$.ajax({
url : 'http://localhost:8080/j_spring_security_check',
type : 'POST',
contentType : 'application/json',
data : JSON.stringify({ j_username : $('#login-username').val(), j_password : $('#login-password').val() }),
success : function () {
window.location.href = '../html/assessment.html';
},
error : function () {
event.preventDefault();
alert('failed');
}
});
});
$('.form-tab-header').on('click', function () {
$('.login-form-error-text').addClass('hidden');
$('.form-tab-header').removeClass('active');
$(this).addClass('active');
$('.form-horizontal').addClass('hidden');
$('.' + $(this).attr('id') + '-content').removeClass('hidden');
});
});
Security Config
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${ldap.urls}")
private String ldapUrls;
#Value("${ldap.base.dn}")
private String ldapBaseDn;
#Value("${ldap.user.dn.pattern}")
private String ldapUserDnPattern;
#Value("${ldap.enabled}")
private String ldapEnabled;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login**").permitAll()
.antMatchers("/assessments/**").fullyAuthenticated()
.antMatchers("/").permitAll()
.and()
.formLogin()
//.loginPage("http://htmlcode.s3-website.us-east-2.amazonaws.com")
.loginPage("http://localhost:8000")
.loginProcessingUrl("/j_spring_security_check")
.usernameParameter("j_username")
.passwordParameter("j_password")
//.loginPage("/login")
.failureUrl("/login?error")
.permitAll()
.and()
.logout()
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.permitAll();
}
#Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/register");
// .antMatchers("/assessments/**");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
if(Boolean.parseBoolean(ldapEnabled)) {
auth.ldapAuthentication()
.userDetailsContextMapper(userDetailsContextMapper())
.userDnPatterns(ldapUserDnPattern)
.contextSource()
.url(ldapUrls+ldapBaseDn);
}
}
#Bean
public UserDetailsContextMapper userDetailsContextMapper() {
return new LdapUserDetailsMapper() {
#Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
UserDetails details = super.mapUserFromContext(ctx, username, authorities);
return details;
}
};
}
#Bean
CorsFilter corsFilter() {
CorsFilter filter = new CorsFilter();
return filter;
}
}
I was finally able to fix this by removing JSON.stringfy in my post body of ajax request and setting the content type to application/x-www-form-urlencoded.

Configuring Spring Security To Work With Two Different Login Pages

One of the situations in which we may need two login pages is when we have one page for administrators of an application and a different page for normal users.
Each http element will have a different login page and a different login processing URL
I have got this Spring boot security config for allowing login for multiple page.
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
#Configuration
#Order(1)
public static class App1ConfigurationAdapter extends WebSecurityConfigurerAdapter {
public App1ConfigurationAdapter() {
super();
}
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
private DataSource dataSource;
#Value("${admin-users-query}")
private String usersQuery;
#Value("${admin-roles-query}")
private String rolesQuery;
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.
jdbcAuthentication()
.usersByUsernameQuery(usersQuery)
.authoritiesByUsernameQuery(rolesQuery)
.dataSource(dataSource)
.passwordEncoder(bCryptPasswordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/admin*")
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/admin/temp/login")
.failureUrl("//admin/temp?error=loginError")
.defaultSuccessUrl("/")
.usernameParameter("email")
.passwordParameter("password")
.permitAll()
.and()
.logout()
.logoutUrl("/admin/temp/logout")
.logoutSuccessUrl("/admin/temp")
.and()
.exceptionHandling()
.accessDeniedPage("/403").and().csrf();
}
}
#Configuration
#Order(2)
public static class App2ConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
private DataSource dataSource;
#Value("${user-users-query}")
private String usersQuery;
#Value("${user-roles-query}")
private String rolesQuery;
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.
jdbcAuthentication()
.usersByUsernameQuery(usersQuery)
.authoritiesByUsernameQuery(rolesQuery)
.dataSource(dataSource)
.passwordEncoder(bCryptPasswordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/user*")
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/user/temp/login")
.failureUrl("/user/temp/?error=loginError")
.defaultSuccessUrl("/user/temp")
.usernameParameter("username")
.passwordParameter("password")
.permitAll()
.and()
.logout()
.logoutUrl("/user/temp/logout")
.logoutSuccessUrl("/user/temp/login")
.and()
.exceptionHandling()enter code here
.accessDeniedPage("/403").and().csrf();
}
}
#Configuration
#Order(3)
public static class guestSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().permitAll();
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring().antMatchers("/js/**", "/js/***", "/js/****");
}
}
Admin login form /admin/temp/login
<form th:action="#{/admin/temp/login}" method="post">
<div class="input-group mb-3">
<span class="input-group-addon"><i class="icon-envelope"></i></span>
<input type="text" class="form-control" placeholder="Email" name="email" data-validation="required" />
</div>
<div class="input-group mb-4">
<span class="input-group-addon"><i class="icon-lock"></i></span>
<input type="password" class="form-control" placeholder="Password" name="password" data-validation="required" />
</div>
<div class="row">
<div class="col-6">
<input type="submit" class="btn btn-primary px-4" value="Login">
</div>
<div class="col-6 text-right">
Forgot password?
</div>
</div>
</form>
User login form /user/temp/login
<form th:action="#{/user/temp/login}" method="post">
<div class="input-group mb-3">
<span class="input-group-addon"><i class="icon-envelope"></i></span>
<input type="text" class="form-control" placeholder="Email" name="username" data-validation="required" />
</div>
<div class="input-group mb-4">
<span class="input-group-addon"><i class="icon-lock"></i></span>
<input type="password" class="form-control" placeholder="Password" name="password" data-validation="required" />
</div>
<div class="row">
<div class="col-6">
<input type="submit" class="btn btn-primary px-4" value="Login">
</div>
<div class="col-6 text-right">
Forgot password?
</div>
</div>
</form>
When I Submit the form I get
**Code: 405
(Method Not Allowed)**
When I use single form the form get submted.
Does anyone has an idea on whats happening her?
As someone already answered you need to add
.loginPage("/login.html")
.loginProcessingUrl("/admin/temp/login")
The default URL where the Spring Login will POST to trigger the authentication process is /login which used to be /j_spring_security_check before Spring Security 4.
By default Spring assumes that URL to validate the credentials is /login. To change it you should set up loginProcessingUrl for both formLogin configurations.
It should be something like:
.formLogin()
.loginPage("/user/temp/login")
.failureUrl("/user/temp/?error=loginError")
.defaultSuccessUrl("/user/temp")
.usernameParameter("username")
.passwordParameter("password")
.loginProcessingUrl("/user/temp/login") # missing line

Resources