How can I fix the CSRF related issues when it is enabled using Spring Boot with Spring Security? - spring

I have a spring boot application. I am using Spring Security. I am facing two issues when CSRF is enabled. Please find the below issues, code and screenshots. How can I fix this?
Whenever the server is restarted, the login always fails the first time. It gives 404 error. But it is successful the second time.
I am using customAuthenticationFailureHandler in case if login is
failed, I am setting the error message in session and redirecting it
to login jsp to display it. It was working fine before CSRF was
enabled. Now, it looks like the value stored in session is destroyed
Security configuration
#Override
protected void configure(HttpSecurity http) throws Exception {
//http.csrf().disable();
http
.authorizeRequests()
.antMatchers("/ui/static/assets/**").permitAll()
.antMatchers("/register", "/forgotPassword").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/LoginPage")
.loginProcessingUrl("/authenticate")
.permitAll()
.defaultSuccessUrl("/addDocument")
.failureHandler(customAuthenticationFailureHandler)
.and().exceptionHandling().accessDeniedPage("/Access_Denied")
.and().logout().permitAll().invalidateHttpSession(true);
}
CustomAuthenticationFailureHandler
#Component
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception)
throws IOException, ServletException {
String errMsg=exception.getMessage();;
request.getSession().setAttribute("loginErrorMessage", errMsg);
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.sendRedirect(request.getContextPath()+"/login?error");
}
}
Login.jsp
<c:set var="params" value="${requestScope['javax.servlet.forward.query_string']}"/>
<div class="account-content">
<c:if test="${params eq 'error' && loginErrorMessage ne null}">
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<strong>${loginErrorMessage}</strong>
</div>
</c:if>
<form action="${pageContext.servletContext.contextPath}/authenticate" class="form-horizontal" method="post" id="formLogin" data-parsley-validate="">
<sec:csrfInput />
<div class="form-group m-b-25">
<div class="col-12">
<label for="emailaddress">Email address<span class="text-danger">*</span></label>
<input class="form-control input-lg" type="email" name="username" id="username" placeholder="Enter your email" data-parsley-required="true">
</div>
</div>
<div class="form-group m-b-25">
<div class="col-12">
Forgot your password?
<label for="password">Password<span class="text-danger">*</span></label>
<input class="form-control input-lg" type="password" id="pwd" name="password" placeholder="Enter your password" data-parsley-required="true">
</div>
</div>
<div class="form-group account-btn text-center m-t-10">
<div class="col-12">
<button class="btn w-lg btn-rounded btn-lg btn-primary waves-effect waves-light"
id="signInBtn" type="submit" value="Next" >Sign In
<i class="fas fa-spinner fa-spin" id="loadingBtn" style="display:none;"></i></button>
</div>
</div>
</form>
<div class="clearfix"></div>
</div>

Related

How to not redirect page when authentication fail - JSP & Spring MVC

I have a JSP form as below,
<form method="post" id="loginForm" action="<c:url value='/login'/>">
<fieldset>
<label class="block clearfix">
<span class="block">
<input type="text" class="form-control"
placeholder='Username'
name="username"
required="required"
maxlength="50"/>
<i class="icon-user"></i>
</span>
</label>
<label class="block clearfix">
<span class="block">
<input type="password" class="form-control"
placeholder='Password'
required="required"
name="password" maxlength="50"/>
</span>
</label>
<div>
<c:if test="${userNameRequired == true}">
<br/>
<div class="alert alert-block alert-danger">
<button class="close" data-dismiss="alert" type="button">
<i class="icon-remove"></i>
</button>
<strong>
<i class="icon-remove"></i>
Error!
</strong>
Please enter your Email.
</div>
<c:remove var="userNameRequired" scope="session"/>
</c:if>
<c:if test="${passwordRequired == true}">
<br/>
<div class="alert alert-block alert-danger">
<button class="close" data-dismiss="alert" type="button">
<i class="icon-remove"></i>
</button>
<strong>
<i class="icon-remove"></i>
Error!
</strong>
Please enter your Password.
</div>
<c:remove var="passwordRequired" scope="session"/>
</c:if>
<c:if test="${invalidCredentials == true}">
<br/>
<div class="alert alert-block alert-danger">
<button class="close" data-dismiss="alert" type="button">
<i class="icon-remove"></i>
</button>
<strong>
<i class="icon-remove"></i>
Error!
</strong>
Invalid Credentials.
</div>
<c:remove var="invalidCredentials" scope="session"/>
</c:if>
<c:if test="${userNotExists == true}">
<br/>
<div class="alert alert-block alert-danger">
<button class="close" data-dismiss="alert" type="button">
<i class="icon-remove"></i>
</button>
<strong>
<i class="icon-remove"></i>
Error!
</strong>
Invalid Credentials.
</div>
<c:remove var="userNotExists" scope="session"/>
</c:if>
</div>
<div class="clearfix">
<button type="submit"
class="btn btn-block btn-primary"
value='Login'>
</button>
</div>
</fieldset>
</form>
When authentication fails, it should show a message as invalid credentials or respective message on the same page, but it is redirecting to a new page as below,
There are no redirects added in my authenticate method which is triggered when login is clicked. Below is the code,
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) {
String userName = obtainUsername(request);
String password = obtainPassword(request);
if (userName == null || userName.isEmpty()) {
request.getSession().setAttribute("userNameRequired", true);
throw new BadCredentialsException("Email field should not be empty.");
}
if (password == null || password.isEmpty()) {
request.getSession().setAttribute("passwordRequired", true);
throw new BadCredentialsException("Password field should not be empty.");
}
UsernamePasswordAuth authRequest = new UsernamePasswordAuth (
userName, password);
setDetails(request, authRequest);
try{
return this.getAuthenticationManager().authenticate(authRequest);
}catch(BadCredentialsException ex){
request.getSession().setAttribute("invalidCredentials", true);
throw new ex;
}
}
I'm new to JSP's and Spring MVC so hard time debugging & understanding. Any help is much appreciated.
Thank you.
It looks like you created a subclass of AbstractAuthenticationProcessingFilter which has a method setAuthenticationFailureHandler to set field failureHandler value. so you should create a implementation of AuthenticationFailureHandler and invoke method setAuthenticationFailureHandler
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
//write the authentication failure message to response
response.getWriter().write(exception.getMessage());
}
}
authFilter.setAuthenticationFailureHandler(new MyAuthenticationFailureHandler());
authFilter is subclass of AbstractAuthenticationProcessingFilter
Let me see if i understand.
When you click the submit button, it redirects always even if the credentials are incorrect?
It is gonna redirect you everytime you click the button because it is submitting you to the "action="<c:url value='/login'/>" attribute you wrote in "<form>" tag.
Buttons inside a form always sends you to the action location.
To avoid this, i recommend you to use ajax to request and listen the response without redirecting or reloading the page.
Or you can redirect back to the form explicitly in your validation side when the credentials are wrong.
I hope i were helpful.

404 Page not found after login in custom login page

I am new in Spring and I'm trying to implement a custom login page. I am able to redirect to the custom login page but it goes to error 404 when I hit the Submit button in my page. The url went to : http://localhost:12345/login?username=abcd&password=qwer
Here is my code of the htm, controller and also the security config:
SecurityConfig.java
http
.authorizeRequests()
.antMatchers("/main",
"/main#/home",
"/login**",
"/resources/**",
"/css/**",
"/images/**",
"/js/**",
"/std/**",
"/system/**",
"/favicon.ico",
"/rest/**",
"/rest/user/**" ).permitAll()
.anyRequest().authenticated()
.and().csrf().disable()
.formLogin()
.loginPage("/main#/login").permitAll()
.defaultSuccessUrl("/main#/home")
.failureUrl("/main#/page/b").permitAll();
LoginController.Java
#RestController
#RequestMapping(value = "/rest/user")
public class LoginController
{
#RequestMapping(value = "login")
public String login(Model model, String error, String logout) {
if (error != null)
model.addAttribute("errorMsg", "Your username and password are invalid.");
if (logout != null)
model.addAttribute("msg", "You have been logged out successfully.");
return "login";
}
}
login-content.htm
<div class="col-md-6 col-md-offset-3">
<h2>Login</h2>
<form name="form" action="/login" ng-controller="LoginController" role="form">
<div class="form-group" ng-class="{ 'has-error': form.username.$dirty && form.username.$error.required }">
<label for="username">Username</label>
<input type="text" name="username" id="username" class="form-control" ng-model="user.username" required />
<span ng-show="form.username.$dirty && form.username.$error.required" class="help-block">Username is required</span>
</div>
<div class="form-group" ng-class="{ 'has-error': form.password.$dirty && form.password.$error.required }">
<label for="password">Password</label>
<input type="password" name="password" id="password" class="form-control" ng-model="user.password" required />
<span ng-show="form.password.$dirty && form.password.$error.required" class="help-block">password is required</span>
</div>
<input type="submit" ng-click="login(user)" value="Submit">
</form>
Thanks!
At the end I have modified the "action" in the Login-content.htm to another word (e.g. perform_login) and also added .loginProcessingUrl("/perform_login").permitAll() in the SecurityConfig.java.
Besides, the another problem is Spring Security will need to have a specific POST method in order to authenticate it. I have added method="POST" in the HTM page.
Thanks!

how to set custom login page instead of spring security default login page

I integrate Angular with Spring Boot, I want to set my own login
page, but when I run my application it shows spring security default
login page. I change set every thing in my configuration file but it
still shows spring security default login page.
How do I set the custom login page?
SecurityConfig
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("nilmani").password("{noop}akj#159")
.roles("USER");
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.component.html")
.loginProcessingUrl("/perform_login")
.defaultSuccessUrl("/dashboard.component.html", true);
//.failureUrl("/login.html?error=true")
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
login.component.html
<div class="app-body">
<main class="main d-flex align-items-center">
<div class="container">
<div class="row">
<div class="col-md-8 mx-auto">
<div class="card-group">
<div class="card p-4">
<div class="card-body">
<form>
<h1>Login</h1>
<p class="text-muted">Sign In to your account</p>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text"><i class="icon-user"></i></span>
</div>
<input type="text" class="form-control" placeholder="Username" [(ngModel)]="username" [ngModelOptions]="{standalone: true}" autocomplete="username" required>
</div>
<div class="input-group mb-4">
<div class="input-group-prepend">
<span class="input-group-text"><i class="icon-lock"></i></span>
</div>
<input type="password" class="form-control" placeholder="Password"[(ngModel)]="password" [ngModelOptions]="{standalone: true}" autocomplete="current-password" required>
</div>
<div class="row">
<div class="col-6">
<button type="button" (click)="dologin()" class="btn btn-primary px-4">Login</button>
</div>
<div class="col-6 text-right">
<button type="button" class="btn btn-link px-0">Forgot password?</button>
</div>
</div>
</form>
</div>
</div>
<div class="card text-white bg-primary py-5 d-md-down-none" style="width:44%">
<div class="card-body text-center">
<div>
<h2>Sign up</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
<button type="button" class="btn btn-primary active mt-3">Register Now!</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
Your SecurityConfig is fine. I did it the same way. You have to make sure that you have a controller which accepts this url and delivers the correct page. And normally you did not define a html page! A correct url might simply be /login.
So your SecurityConfig looks like:
.formLogin()
.loginPage("/login")
And your controller for example:
#Controller
#RequestMapping(value="/login")
#Scope(value="session")
public class LoginController extends AbstractWebController {
#GetMapping
public ModelAndView loginView() {
ModelAndView mav = this.getModelAndView("login");
return mav;
}
// further code goes here
}
This controller e.g. returns a jsp page which you can style according to your needs with Angular or whatever.

spring security login always ends with failure url [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 4 years ago.
Improve this question
I have a problem with proper configuration of spring security, registering works well, restricting access works well but when it comes to logging I get always a failure url result, can anyone tell me where the problem is?
Here is my SecurityConfiguration:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
private DataSource dataSource;
#Value("${spring.queries.users-query}")
private String usersQuery;
#Value("${spring.queries.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.authorizeRequests().antMatchers("/manage/**").hasAuthority("USER").antMatchers("/").permitAll()
.antMatchers("/business_login").permitAll().antMatchers("/business_password_recovery").permitAll()
.antMatchers("/business_register").permitAll().anyRequest().authenticated().and().csrf().disable()
.formLogin().loginPage("/business_login").defaultSuccessUrl("/manage")
.loginProcessingUrl("/process_business_login").usernameParameter("username").passwordParameter("password")
.failureUrl("/business_login").usernameParameter("email").passwordParameter("password").and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/").and()
.exceptionHandling().accessDeniedPage("/access-denied");
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**", "/templates/**",
"/boomerang/**");
}
}
this is my login form:
<form autocomplete="off" class="form-default" method="POST" role="form" action="/process_business_login">
<div class="row">
<div class="col-12">
<div class="form-group login-form-input">
<input type="text" name="username" placeholder="email"
class="form-control form-control-lg login-form-input">
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="form-group has-feedback">
<input type="password" name="password"
placeholder="password"
class="form-control form-control-lg login-form-input">
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="checkbox danger-checkbox">
<input type="checkbox" id="chkRemember"> <label
class="light-gray-color" for="chkRemember">Remember
me</label>
</div>
</div>
</div>
<button type="submit"
class="btn btn-styled btn-lg btn-block btn-danger mt-4 no-border-radius danger-button-login">LOG
IN</button>
</form>
Right there you can see a part of my application.properties file, here I define queries for spring security, they look like this:
spring.queries.users-query=select email, password, active from business_user where email=?
spring.queries.roles-query=select u.email, r.role from business_user u inner join user_role ur on(u.id_business_user=ur.id_business_user) inner join role r on(ur.id_role=r.id_role) where u.email=?
The login always redirects to failure url site, where do I make a mistake?
Your configuration states
.usernameParameter("email")
but the parameter name from the login form states:
<input type="text" name="username" placeholder="email"
class="form-control form-control-lg login-form-input">
So there is a mismatch. I'm thinking that you want to name your form correctly,
instead of
name="username"
you have
name="email"
or you change your configuration from
.usernameParameter("email")
to
.usernameParameter("username")
or remove it, since .usernameParameter("username") is the default.

Forbidden Error when login in SpringBoot app

I have a basic SpringBoot app. using Spring Initializer, JPA, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR file.
This is my config file:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(publicMatchers()).permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").defaultSuccessUrl("/menu/config")
.failureUrl("/login?error").permitAll()
.and()
.logout().permitAll();
}
my Thymeleaf template:
<form id="loginForm" th:action="#{/login}" method="post">
<div class="row">
<div class="col-md-6 col-md-offset-3 text-center">
<div th:if="${param.error}" class="alert alert-danger alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">x</span>
</button>
<p th:text="#{login.error.message}" />
</div>
<div th:if="${param.logout}" class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">x</span>
</button>
<p th:text="#{login.logout.success}" />
</div>
</div>
</div>
<div class="input_label"><i class="fa fa-user"></i><input type="text" id="usernameId" name="username" th:attr="placeholder=#{login.user.placeholder}" value="peris" /></div>
<div class="input_label"><i class="fa fa-key"></i><input type="password" name="password" value="peris"/></div>
<input type="submit" value="LOGIN" />
</form>
and my Login controller:
#Controller
public class LoginController {
public static final Logger LOG = LoggerFactory.getLogger(LoginController.class);
/** The login view name */
public static final String LOGIN_VIEW_NAME = "login/login";
#RequestMapping(value={ "/", "/login", "/elCor/login"}, method = {RequestMethod.GET})
public String login() {
LOG.info(serverContextPath + "/" + LOGIN_VIEW_NAME);
return serverContextPath + "/" + LOGIN_VIEW_NAME;
}
}
Evefything is OK using the browser, but when I use the mobile, I log in, I go back using the browser button, then I try to log in again but I have this error:
2018-06-28 08:56 [http-nio-5678-exec-2] ERROR c.t.w.c.AppErrorController - getErrorAttributes(request, true) --> {timestamp=Thu Jun 28 08:56:48 CEST 2018, status=403, error=Forbidden, message=Forbidden, path=/elCor/login}
I found the same problem in the computer browser but just once, and I can't not reproduce the problem.. I am trying to guess it
Try adding csrf tokens for login request.
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>

Resources