Forbidden Error when login in SpringBoot app - spring

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}"/>

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.

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.

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

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>

Spring Security returns 403 on any request

I created two users with ADMIN and USER roles, but every time I try to login server return 403.
WebSecurityConfig:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/admin/**")
.access("hasAnyAuthority('ADMIN','USER')")
.and().formLogin().loginPage("/login").failureUrl("/login?error")
.usernameParameter("username")
.passwordParameter("password")
.and().logout().logoutSuccessUrl("/login?logout")
.and().csrf().disable();
}
my UserService which maps my users from db:
#Transactional(readOnly = true)
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDao.findByUserName(username);
org.springframework.security.core.userdetails.User.UserBuilder builder = null;
if (user != null) {
builder = org.springframework.security.core.userdetails.User.withUsername(username);
builder.disabled(!user.isEnabled());
builder.password(user.getPassword());
String[] authorities = user.getUserRole()
.stream().map(a -> a.getRole()).toArray(String[]::new);
builder.authorities(authorities);
} else {
throw new UsernameNotFoundException("User not found.");
}
return builder.build();
}
csrf is disabled. I also use hasAnyUthority* method so I don't need ROLE_ prefix.
I use spring security 5
My login.html
<head>
<meta charset="utf-8"/>
<link rel="stylesheet" type="text/css" href="resources/style.css"/>
</head>
<body>
<div class="container">
<header>
<h1>Login</h1>
</header>
<div class="alert alert-error" th:if="${error != null}">
<div>
<strong>Okay, Houston, we've had a problem here.</strong>
</div>
</div>
<div class="alert alert-error" th:if="${logout != null}">
<div>
<strong>Okay, Houston, you're logged out successfully .</strong>
</div>
</div>
<form class="form-horizontal" th:action="#{/login}" method="POST">
<fieldset>
<div class="control-group">
<label class="control-label">Login</label>
<div class="controls">
<div class="input-prepend">
<span class="add-on">#</span>
<input id="loginField" name="username" class="span3" type="text"/>
</div>
</div>
</div>
<div class="control-group">
<label class="control-label">Password</label>
<div class="controls">
<input id="passwordField" name="password" class="span3" type="password"/>
</div>
</div>
<div class="form-actions">
<button id="loginButton" class="btn btn-primary" type="submit">Login</button>
</div>
</fieldset>
</form>
</div>
</body>
I did everything as in example projects but it still doesn't want to log me in.
I don't see that hasAnyAuthority(...) will work without "ROLE_", try .access("hasAnyRole('ADMIN','USER')") or .access("hasAnyRole('ROLE_ADMIN','ROLE_USER')").
Note that in String[] authorities = user.getUserRole().stream().map(a -> a.getRole()).toArray(String[]::new); you need in a.getRole() return with prefix ROLE_ or the same what you will have in hasAnyAuthority(...)
For example if your a.getRole() will return WHAT_EVER than hasAnyAuthority('WHAT_EVER) should work, but hasAnyRole('WHAT_EVER') will expect that a.getRole() returns ROLE_WHAT_EVER
Maybe it will help someone so i will unswer my question.
I couldn't login becouse when i launch my program, i add some new users with not encrypted password. But spring security decrypts it anyways so that is why i couldn't login and got 403 repsonse. All i needed is to encrypt password before adding it into database.

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.

Resources