Springboot authentication issue with customer login - spring-boot

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.

Related

spring boot + thymeleaf: ... fails to display errors, automatic transition to Login page

I have a registration page. The field data is validated. When errors are captured in the controller's method for user registration and the registration page is called again (where errors should be displayed), an automatic redirect to the /login URL occurs.
org.apache.juli.logging.DirectJDKLog [ERROR] 19:34:25 - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/account/register.html]")] with root cause
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'userData' available as request attribute
at org.springframework.web.servlet.support.BindStatus.(BindStatus.java:153)
at org.springframework.web.servlet.support.RequestContext.getBindStatus(RequestContext.java:926)
spring:
thymeleaf:
enabled: true
check-template-location: true
prefix: classpath:/templates/
mode: HTML
cache: false
encoding: UTF-8
suffix: .html
logging:
pattern:
console: '%C{1.yaml.} [%-5level] %d{HH:mm:ss} - %msg%n'
level:
org:
hibernate:
sql: debug
type:
descriptor:
sql:
BasicBinder: trace
springframework:
web:
client: trace
security: trace
Registrationcontroller
#Controller
#RequiredArgsConstructor
public class RegistrationController {
private final UserService userService;
#GetMapping("/register")
public String showRegistrationForm(final Model model){
UserDataDto user = new UserDataDto();
model.addAttribute("userData", user);
return "account/register";
}
#PostMapping("/register")
public String userRegistration(final #Valid UserDataDto userData,
final BindingResult bindingResult,
final Model model){
if(bindingResult.hasErrors()){
model.addAttribute("errors", userData);
return "account/register";
}
try {
userService.register(userData);
}catch (UserAlreadyExistException e){
bindingResult.rejectValue(
"email",
"userData.email",
"An account already exists for this email."
);
model.addAttribute("formreg", userData);
return "account/register";
}
return "redirect:/login";
}
}
LoginPageController
#Controller
public class LoginPageController {
#GetMapping("/login-error")
public String loginError(Model model) {
model.addAttribute("loginError", true);
return "login";
}
#GetMapping("login")
public String getLogin(){
return "account/login";
}
}
dto
public class UserDataDto {
#NotEmpty(message = "First name can not be empty")
private String firstName;
#NotEmpty(message = "Last name can not be empty")
private String lastName;
#NotEmpty(message = "Email can not be empty")
#Email(message = "Please provide a valid email id")
private String email;
#NotEmpty(message = "Password can not be empty")
private String password;
...
}
security config
#Configuration
public class SecurityConfiguration {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests()
.requestMatchers("/login", "/register").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").permitAll()
.defaultSuccessUrl("/home")
.failureUrl("/login?error=true")
.and()
.build();
}
#Bean
public PasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
account/register.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head th:fragment="head">
<meta charset="utf-8">
<!-- Test it -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- Tell the browser to be responsive to screen width -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#4.0.0/dist/css/bootstrap.min.css"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head>
<body class="hold-transition register-page">
<div class="register-logo">
<div class="card">
<div class="card-body register-card-body">
<p class="login-box-msg"></p>
<form action="#" th:action="#{/register}" th:object="${userData}" method="post">
<div th:if="${#fields.hasAnyErrors()}">
<ul>
<li th:each="err : ${#fields.allErrors()}" th:text="${err}" />
</ul>
</div>
<!-- <div class="alert alert-danger" th:if="${#fields.hasErrors('*')}">
<p th:each="err : ${#fields.errors('*')}" th:text="${err}"></p>
</div>-->
<div class="input-group mb-3 w-25">
<input type="text" class="form-control" th:field="*{firstName}" placeholder="First Name"
th:errorclass="is-invalid">
<div class="input-group-append">
<div class="input-group-text">
<span class="fas fa-user"></span>
</div>
</div>
</div>
<div class="input-group mb-3 w-25">
<input type="text" class="form-control" th:field="*{lastName}" placeholder="Last Name"
th:errorclass="is-invalid">
<div class="input-group-append">
<div class="input-group-text">
<span class="fas fa-user"></span>
</div>
</div>
</div>
<div class="input-group mb-3 w-25">
<input type="email" class="form-control" th:field="*{email}" placeholder="Email"
th:errorclass="is-invalid">
<div class="input-group-append">
<div class="input-group-text">
<span class="fas fa-envelope"></span>
</div>
</div>
</div>
<div class="input-group mb-3 w-25">
<input type="password" class="form-control" th:field="*{password}" placeholder="Password"
th:errorclass="is-invalid">
<div class="input-group-append">
<div class="input-group-text">
<span class="fas fa-lock"></span>
</div>
</div>
</div>
<div class="input-group mb-3 w-25">
<input type="password" class="form-control" placeholder="Retype password">
<div class="input-group-append">
<div class="input-group-text">
<span class="fas fa-lock"></span>
</div>
</div>
</div>
<div style="width:150px" ;>
<button type="submit" class="btn btn-outline-primary btn-sm btn-block">Register</button>
</div>
<div></div>
<p>
<div class="row">
<div class="col-8">
<div class="icheck-primary">
<input type="checkbox" id="agreeTerms" name="terms" value="agree">
<label for="agreeTerms">
I agree to the terms
</label>
</div>
</div>
<!-- /.col -->
</div>
</p>
<!-- /.col -->
</form>
</div>
I already have an Account
</div>
</div>
<!-- /.form-box -->
</div><!-- /.card -->
</div>
</script>
</body>
</html>
account/login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head th:fragment="head">
<meta charset="utf-8">
<!-- Test it -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- Tell the browser to be responsive to screen width -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#4.0.0/dist/css/bootstrap.min.css"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
</head>
<body class="hold-transition login-page">
<div class="login-box">
<div class="login-logo">
<div class="card">
<div class="card-body login-card-body">
<p class="login-box-msg">Sign in to start your session</p>
<p th:if="${loginError}" class="error">Wrong user or password</p>
<form th:action="#{/login}" method="post">
<div th:if="${param.error}">
<div class="alert alert-danger">
Invalid username or password.
</div>
</div>
<div class="input-group mb-3">
<input type="email" class="form-control" name="username" placeholder="Email">
<div class="input-group-append">
<div class="input-group-text">
<span class="fas fa-envelope"></span>
</div>
</div>
</div>
<div class="input-group mb-3">
<input type="password" name="password" class="form-control" placeholder="Password">
<div class="input-group-append">
<div class="input-group-text">
<span class="fas fa-lock"></span>
</div>
</div>
</div>
<div class="row">
<div class="col-4">
<button type="submit" class="btn btn-primary btn-block">Sign In</button>
</div>
<!-- /.col -->
</div>
</form>
</div>
</div>
</div>
</div>
<!-- Ionicons
<link rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">-->
<!-- icheck bootstrap -->
</body>
</html>
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
....
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
I tried that:
controller
#GetMapping("/handleLoginErrors")
ModelAndView handleErrors(ModelAndView modelAndView){
modelAndView.setViewName("account/login");
return modelAndView;
}
configuration
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests()
.requestMatchers("/login", "/register").permitAll()
.requestMatchers("/account/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").permitAll()
.defaultSuccessUrl("/home")
.failureUrl("/handleLoginErrors").permitAll()
//.failureUrl("/login?error=true")/**/
.and()
.build();
}
However, the point is to return the errors to the registration page so that the user can see where he made a mistake during registration. That is, to return the user to the registration form and indicate errors in front of the fields where these errors were found.
Who has any ideas how to display errors anyway? And why is there an automatic redirection to /login, and not to the page you specified?
Spring will, by default, redirect to login?error (GET) whenever an error occurs. For login errors, you can, however, change that behaviour by specifying an endpoint (url) that'll handle the login errors. This is done by calling failureUrl.
return http
.authorizeHttpRequests()
.requestMatchers("/login", "/register").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").permitAll()
.defaultSuccessUrl("/home")
.failureUrl("/handleLoginErrors")
.and()
.build();
You should also add a controller method matching handleLoginErrors.
#GetMapping("/handleLoginErrors")
ModelAndView handleErrors(ModelAndView){
mv.addObject("loginStatus", "failed");
mv.setViewName("loginError"); the view name i.e loginError.html
return mv;
}
Spring also provides several mechanisms for generic error handling. More information can be found here https://www.baeldung.com/exception-handling-for-rest-with-spring.

ERR_TOO_MANY_REDIRECTS Spring Security

When using custom login page I am getting this error, please let me know where I am going wrong.
Controller:
<!-- language: java -->
#GetMapping("/login")
public String login(ModelMap model) {
List<String> authType = ldapAuthConfigService.getEnabledAuthentications();
model.addAttribute("authList", authType);
return "home";
}
Security config:
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/**").authenticated().and().formLogin()
.loginPage("/login").permitAll()
.defaultSuccessUrl("/layout", true).and()
.logout().clearAuthentication(true).invalidateHttpSession(true)
.logoutRequestMatcher(new AntPathRequestMatcher(
"/logout"))
.logoutSuccessUrl(
"/login?logout")
.permitAll().and().authenticationProvider(
customeAuthenticationProvider());
http.headers().disable();
if (protocol != null && protocol.equalsIgnoreCase("http")) {
http.requiresChannel().anyRequest().requiresInsecure();
} else {
http.requiresChannel().anyRequest().requiresSecure();
}
}
}
I added viewController and removed it from GetMapping in controller, it was working fine but i need to add a model map before loading login page
'''
#Component
public class WebConfig implements WebMvcConfigurer {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/" };
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
registry.addResourceHandler("/script-api/**").addResourceLocations("classpath:/static/script-api/");
registry.addResourceHandler("/theme/**").addResourceLocations("classpath:/static/theme/");
if (!registry.hasMappingForPattern("/webjars/**")) {
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
if (!registry.hasMappingForPattern("/**")) {
registry.addResourceHandler("/**").addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS);
}
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("home");
}
}
'''
This is my view page home.html
'''
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<title>EZENGAGE</title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<!-- VENDOR CSS -->
<link rel="stylesheet" type="text/css"
th:href="#{/theme/assets/vendor/bootstrap/css/bootstrap.min.css}" />
<link rel="stylesheet" type="text/css"
th:href="#{/theme/assets/vendor/daterangepicker/daterangepicker.css}" />
<link rel="stylesheet" type="text/css"
th:href="#{/theme/assets/vendor/font-awesome/css/font-awesome.min.css}" />
<link rel="stylesheet" type="text/css"
th:href="#{/theme/assets/vendor/animate-css/vivify.min.css}" />
<link rel="stylesheet" type="text/css"
th:href="#{/theme/assets/vendor/c3/c3.min.css}" />
<link rel="stylesheet" type="text/css"
th:href="#{/theme/assets/vendor/chartist/css/chartist.css}" />
<link rel="stylesheet" type="text/css"
th:href="#{/theme/assets/vendor/chartist-plugin-tooltip/chartist-plugin-tooltip.css}" />
<link rel="stylesheet" type="text/css"
th:href="#{/theme/assets/vendor/toastr/toastr.min.css}" />
<!-- MAIN CSS -->
<link rel="stylesheet" type="text/css"
th:href="#{/theme/dark/assets/css/site.min.css}" />
</head>
<body class="theme-cyan">
<div class="pattern">
<span class="red"></span> <span class="indigo"></span> <span
class="blue"></span> <span class="green"></span> <span class="orange"></span>
</div>
<div class="auth-main particles_js">
<div class="auth_div vivify popIn">
<div class="card text-white text-center mx-auto"
style="width: 350px; background: #343840; box-shadow: 5px 5px 10px black;">
<div class="card-header">
<img class=" card-title img-fluid logo"
th:src="#{/theme/assets/images/ezelink-white-logo.png}"
alt="EZELink Logo" style="width: 150px;" />
</div>
<form class="form-auth-small m-t-20" th:action="#{/login}" method="post"
name="login-form" th:autocomplete="off">
<div th:if="${param.error}">
<div class="alert alert-danger">Username or password is
invalid, please try again.</div>
</div>
<div th:if="${param.logout}">
<div class="alert alert-danger">You have been logged out.</div>
</div>
<div class="card-body form-group">
<div class="mb-3">
<label for="login-username" class="form-label"></label> <input
type="text" name="username" id="login-username"
class="form-control" placeholder="Username"
aria-describedby="helpId">
</div>
<div class="mb-3 form-group">
<label for="login-password" class="form-label"></label> <input
type="password" class="form-control" name="password"
id="login-password" placeholder="Password">
</div>
<div class="mb-3 form-group">
<th:block th:if="${#lists.size(authList) > 0}">
<select class="form-control" th:required="required"
id="authType" name="authType" th:size="1">
<option th:each="list : ${authList}" th:value="${list}"
th:text="${list}" th:checked="${list}"></option>
</select>
</th:block>
</div>
</div>
<div class="card-footer text-muted" style="background: #343840">
<button type="submit" class="btn btn-primary btn-block form-group"
style="width: 300px; right: 20px;">Login</button>
</div>
</form>
</div>
</div>
<div id="particles-js"></div>
<script th:src="#{/theme/dark/assets/bundles/libscripts.bundle.js}"></script>
<script
th:src="#{/theme/dark/assets/bundles/vendorscripts.bundle.js}"></script>
<script
th:src="#{/theme/dark/assets/bundles/mainscripts.bundle.js}"></script>
</div>
</body>
</html>
'''
This is my view page
I was able to reproduce this issue. Whith a login page defined as
.loginPage("/login")
and with the original controller
#GetMapping("login")
public String login() {
return "home";
}
I get
http: error: Too many redirects (--max-redirects=30).
With the following config
http.csrf().disable()
.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/**").authenticated()
.and().formLogin()
.loginPage("/login")
I get an expected response
home
The issue may also be solved with this config:
http.csrf().disable()
.authorizeRequests()
.antMatchers("/**").authenticated()
.and().formLogin()
.loginPage("/login").permitAll()

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

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

Spring Security doesn't post to provided login processing url

For some weird reason, I cannot hit the controller that is registered to handle login posts. I just get redirected to this silly image that is in my resources folder:
https://localhost:8443/images/piggy-bank.jpeg
Here is my controller.
#RequestMapping(value = "/login/process", method = RequestMethod.POST)
public String loginPost(HttpSession session, Authentication authentication) {
String client_id = (String) session.getAttribute("client_id");
if (client_id.equals(Constants.TRUSTED_CLIENT)) {
//TODO:
/*
* 1. Generate an access_token
* 2. Save to database
* 3. Form redirect url with all necessary tokens
* 4. Return redirect url string
*/
return "redirect:" + Constants.REDIRECT_TRUSTED_CLIENT;
}
long userId = AuthenticationUtils.getAuthenticatedUserId(authentication);
return "/user/" + userId;
}
Here is my security configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("customUserDetailsService")
UserDetailsService userDetailsService;
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
authorizeRequests()
.antMatchers("/","/sign_up","/resources/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll()
.loginPage("/login")
.loginProcessingUrl("/login/process")
.defaultSuccessUrl("/")
.failureUrl("/access_denied")
.and()
.csrf()
.and()
.exceptionHandling()
.accessDeniedPage("/access_denied")
.and()
.logout()
.permitAll();
}
}
And here's the view:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">
<title>Spring App</title>
<!--/*/ <th:block th:include="fragments/headerinc :: head"></th:block> /*/-->
</head>
<body>
<div class="container">
<!--/*/ <th:block th:include="fragments/header :: header"></th:block> /*/-->
<div id="mainWrapper">
<div class="login-container">
<div class="login-card">
<div class="login-form">
<form th:action="#{/login/process}" method="post" class="form-horizontal">
<div th:if="${param.error != null}">
<div class="alert alert-danger">
<p>Invalid username and password.</p>
</div>
</div>
<div th:if="${param.logout != null}">
<div class="alert alert-success">
<p>You have been logged out successfully.</p>
</div>
</div>
<div class="input-group input-sm">
<label class="input-group-addon" for="username"><i class="fa fa-user"></i></label>
<input type="text" class="form-control" id="username" name="username" placeholder="Enter Username" />
</div>
<div class="input-group input-sm">
<label class="input-group-addon" for="password"><i class="fa fa-lock"></i></label>
<input type="password" class="form-control" id="password" name="password" placeholder="Enter Password" />
</div>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
<div class="form-actions">
<input type="submit"
class="btn btn-block btn-primary btn-default" value="Log in"/>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
Inspecting my network data, I see that the form post to /login/process was successful and the server responded fine!
Request URL:https://localhost:8443/login/process
Request Method:POST
Status Code:302 Found
Remote Address:[::1]:8443
The log during spring startup also affirms the registration of url "/login/post" to the aforementioned controller. Corresponding log:
2016-04-21 20:44:30.725 INFO 25290 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/login/process],methods=[POST]}" onto public java.lang.String com.springapp.controllers.UserController.loginPost(javax.servlet.http.HttpSession,org.springframework.security.core.Authentication)
The situation may be something more insidious, because I can't seem to be redirected to even the defaultSuccessURL page, i.e. the index ("/"). The same is the case (i.e. loginProcessingURL and defaultSuccessfulURL not redirecting) exists even if I use the default out-of-box login view. Is there something wrong with my jsp view? Am I missing some security configuration?
However, manually entering /user/{id} OR any other url successfully lands me to the target url as long as I'm properly authenticated. What does that mean?
Finally here is the 'header.html' and 'headerinc.html' thymeleaf fragments which are inserted in all my jsp:
header.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<link href="../../static/css/app.css"
th:href="#{css/app.css}" rel="stylesheet" media="screen"/>
<link href="../../static/css/bootstrap.css"
th:href="#{css/bootstrap.css}" rel="stylesheet" media="screen"/>
<link href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.css"
th:href="#{/webjars/font-awesome/4.2.0/font-awesome.css}" rel="stylesheet" media="screen"/>
</head>
<body>
<div class="container">
<div th:fragment="header">
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="#" th:href="#{/}">Home</a>
<ul class="nav navbar-nav">
<!-- if logged in, then display -logout, else display -login, -Sign up. -->
<div th:with="currentUser=${#httpServletRequest.userPrincipal?.name}">
<div th:if="${currentUser != null}">
<form th:action="#{/logout}" method="post">
<input type="submit" value="Log out"/>
</form>
</div>
<div th:if="${currentUser == null}">
<li>Log in</li>
<li>Sign up</li>
</div>
<!-- This is to simply test some authentication logic-->
All Users
</div>
</ul>
</div>
</div>
</nav>
<div class="jumbotron">
<div class="row text-center">
<div class="">
<h2>Spring Framework Example..</h2>
<h3>Spring Boot Web App</h3>
</div>
</div>
<div class="row text-center">
<img src="../../static/images/NewBannerBOOTS_2.png" width="400"
th:src="#{/images/piggy-bank.jpeg}"/>
</div>
</div>
</div>
</div>
</body>
</html>
headerinc.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en" th:fragment="head">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" media="screen" />
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<link href="../static/css/guru.css"
th:href="#{/css/guru.css}" rel="stylesheet" media="screen"/>
</head>
<body>
</body>
</html>
This line:
.loginProcessingUrl("/login/process")
tells Spring Security to process the submitted credentials when sent the specified path and, by default, redirect user back to the page user came from. It will not pass the request to Spring MVC and your controller.
Maybe what you want instead of a request mapping is a custom AuthenticationSuccessHandler.
I had also the same issue very recently.
In my case, I had to add this code
<public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/js/**","/assets/**", "/css/**");
}
Note:
Be careful not to use .anyRequest() here, like in
web.ignoring().antMatchers("/js/**","/assets/**", "/css/**").anyRequest()
Because that also gave me a lot of problems ...

Resources