failure of login page in thymeleaf - spring-boot

I create login page and it fetch username and password from database
but when i enter correct username and password it throw invalid
credential exception .I check every thing properly and define
everything. I want when user successful login he can see products and
search products. but my login not working .it crashed every time
login.html
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:replace="/fragments/head"></head>
<body>
<nav th:replace="/fragments/nav :: nav-front"></nav>
<div class="container-fluid mt-5">
<div class="row">
<div th:replace="/fragments/categories"></div>
<div class="col"></div>
<div class="col-6 text-center">
<h3 class="display-4">Login</h3>
<div class="alert alert-danger" th:if="${param.error}">
Invalid credentials
</div>
<form method="post" th:action="#{/login}">
<div class="form-group">
<label>Username:</label>
<input type="text" class="form-control" name="username" id="username" placeholder="Username">
</div>
<div class="form-group">
<label>Password:</label>
<input type="password" class="form-control" name="password" id="password" placeholder="Password">
</div>
<button class="btn btn-danger mb-5">Login</button>
<p>
Click <a th:href="#{/register}">here</a> to register.
</p>
</form>
</div>
<div class="col"></div>
</div>
</div>
<div th:replace="/fragments/footer"></div>
</body>
</html>
pagesController.kt
package com.nilmani.cmsshopingcart.controller
import com.nilmani.cmsshopingcart.model.Page
import com.nilmani.cmsshopingcart.repository.PageRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
#Controller
#RequestMapping("/")
class PagesController {
#Autowired
private lateinit var pageRepository: PageRepository
#GetMapping
fun home(model: Model?):String{
val page: Page? = pageRepository.findBySlug("home")
if (model != null) {
model.addAttribute("page",page)
}
return "page"
}
#GetMapping("/login")
fun login():String{
return "login"
}
#GetMapping("/{slug}")
fun page(#PathVariable slug: String, model: Model): String? {
val page: Page = pageRepository.findBySlug(slug) ?: return "redirect:/"
model.addAttribute("page", page)
return "page"
}
}
UserRepositoryUserDetailsService.kt
package com.nilmani.cmsshopingcart.security
import com.nilmani.cmsshopingcart.model.Admin
import com.nilmani.cmsshopingcart.model.UserEntity
import com.nilmani.cmsshopingcart.repository.AdminRepository
import com.nilmani.cmsshopingcart.repository.UserRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.core.userdetails.User
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.stereotype.Service
#Service
class UserRepositoryUserDetailsService : UserDetailsService {
#Autowired
private lateinit var userRepo:UserRepository
#Autowired
private lateinit var adminRepo:AdminRepository
#Throws(UsernameNotFoundException::class)
override fun loadUserByUsername(email: String): UserDetails {
val user: UserEntity? = userRepo.findByUsername(email)
val admin: Admin? = adminRepo.findByUsername(email)
if (user != null) {
return User(user.username,user.password,ArrayList())
}
if (admin != null) {
return admin
}
throw UsernameNotFoundException("User: $email not found!")
}
}
UserEntity.kt
package com.nilmani.cmsshopingcart.model
import lombok.Data
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UserDetails
import java.util.*
import javax.persistence.*
import javax.validation.constraints.Email
import javax.validation.constraints.Size
import kotlin.jvm.Transient
#Data
#Entity
#Table(name = "user")
data class UserEntity(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
val id:Int=0,
#Size(min = 5,message = "Name must be attlist 5 character long")
private var username:String="",
#Size(min = 8,message = "Password contain above 8 character long")
private var password:String="",
#Transient
val confirmPassword:String="",
#Email(message = "please enter a valid email")
val email:String="",
#Size(min = 10,message = "phone number must be 10 digit")
val phoneNumber:String="",
):UserDetails{
override fun getAuthorities(): List<SimpleGrantedAuthority> {
return listOf(SimpleGrantedAuthority("ROLE_USER"))
}
override fun getPassword(): String {
return password
}
override fun getUsername(): String {
return username
}
override fun isAccountNonExpired(): Boolean {
return true
}
override fun isAccountNonLocked(): Boolean {
return true
}
override fun isCredentialsNonExpired(): Boolean {
return true
}
override fun isEnabled(): Boolean {
return true
}
}
UserRepository.kt
package com.nilmani.cmsshopingcart.repository
import com.nilmani.cmsshopingcart.model.UserEntity
import org.springframework.data.jpa.repository.JpaRepository
interface UserRepository : JpaRepository<UserEntity,Int> {
fun findByUsername(email:String):UserEntity?
}
SecurityConfig.kt
package com.nilmani.cmsshopingcart.security
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service
#Service
#Configuration
#EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {
#Autowired
private lateinit var userDetailsService: UserDetailsService
#Bean
fun encoder(): PasswordEncoder {
return BCryptPasswordEncoder()
}
#Throws(java.lang.Exception::class)
override fun configure(auth: AuthenticationManagerBuilder?) {
auth
?.userDetailsService(userDetailsService)
?.passwordEncoder(encoder())
}
#Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http
.authorizeRequests()
.antMatchers("/category/**").hasAnyRole("USER", "ADMIN")
.antMatchers("/admin/**").hasAnyRole("USER")
.antMatchers("/").permitAll()
.and()
.formLogin()
.loginPage("/login")
.and()
.logout()
.logoutSuccessUrl("/")
.and()
.exceptionHandling()
.accessDeniedPage("/")
}
}
what is the region of failure of my loginPage

if you don't want to encrypt password spring automatically encrypted
password. so in that case use NoOpPasswordEncoder.getInstance()
instead BCryptPasswordEncoder()
so change the code
#Bean
fun encoder(): PasswordEncoder {
return BCryptPasswordEncoder()
}
To
#Bean
fun encoder(): PasswordEncoder {
return NoOpPasswordEncoder.getInstance()
}
and pass the instance of passwordEncoder in
AuthenticationManagerBuilder
this code works for me fine

Related

I have create basic login page but not work that page. Why display whitelable error page?

I have created a basic login page but it does not work that page display only the Whitelabel error and does not create a table in the database. (without encoding)
Not indicate the error.
I am a beginner at coding.
Who can support finding that error? and explain
LoginController
package Controller;
import domain.login;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;
import repository.LoginRepository;
import service.LoginService;
import java.util.Objects;
#Controller
public class LoginController {
#Autowired
private LoginService loginService;
#GetMapping("/login")
public ModelAndView login() {
ModelAndView mav = new ModelAndView("login");
mav.addObject("user", new login());
return mav;
}
#PostMapping("/login")
public String login(#ModelAttribute("user") login user){
login oauthUser = loginService.login(user.getUsername(), user.getPassword());
System.out.print(oauthUser);
if(Objects.nonNull(oauthUser)) {
return "redirect:/";
} else {
return "redirect:/login";
}
}
}
domain (login)
package domain;
import javax.persistence.*;
#Entity
#Table(name="login")
public class login {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
public login(){
}
public login(Long id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
LoginRepository
package repository;
import domain.login;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface LoginRepository extends JpaRepository<login, Long>{
login findByUsernameAndPassword(String username, String password);
}
LoginService
package service;
import domain.login;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import repository.LoginRepository;
#Service
public class LoginService {
#Autowired
private LoginRepository repo;
public login login(String username, String password) {
login user = repo.findByUsernameAndPassword(username, password);
return user;
}
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Welcome to Home Page</title>
</head>
<body>
<h1>Welcome To Home Page</h1>
</body>
</html>
login.html
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Login Page</title>
</head>
<body>
<h1>login page</h1>
<form th:action="#{/login}" th:object="${user}" method="post">
<div class="form-group">
<label>User Name</label>
<input type="text" th:field="*{username}">
</div>
<div class="form-group">
<lable>Password</lable>
<input type="text" th:field="*{password}">
</div>
<button type="submit">Login</button>
</form>
</body>
</html>
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/login?useSSL=false&serverTimezone=UTC&useLegacyDatetimecode=false
spring.datasource.username=root
spring.datasource.password=123123
#Hibernate
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
#Hibernate auto ddl
spring.jpa.hibernate.ddl-auto=update
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type=TRACE

How to combine spring boot oauth2 with custom user accounts?

This here is my first post to stack overflow (after plenty of reading), so I'm going to do my best to get it right. I've been looking up this problem for weeks and haven't found an answer to it, but I happily admit that there's a possibility I'm not looking up the right terms. I'm self-taught, so I sometimes miss things.
I have a Spring Boot application with custom user accounts stored in a postgres database. I would like to add OAuth2 support to that application. I have been able to successfully add OAuth2 login, but I would like to intercept that OAuth2 user information and use it to access one of my custom user accounts.
User signs into Github OAuth -> Server accesses OAuth details and retrieves matching user account info from my user database.
My ideal workflow would be to intercept the OAuth2 details and retrieve the custom info from my DB before the user is redirected to the protected page or back to the index.
Hopefully I managed to include all of the relevant code snippets below. In case I missed anything, the full project can be seen on this here github repository: https://github.com/Ahimsaka/shorturl2.
Web Security Config
package com.github.ahimsaka.shorturl.security;
import com.github.ahimsaka.shorturl.service.impl.UserDetailsServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
auth.userDetailsService(userDetailsService());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().ignoringAntMatchers("/url", "/user/registration", "/user/resetPassword", "/user/savePassword", "login/oauth2/code/github")
.and()
.authorizeRequests()
.antMatchers("/user").hasAnyAuthority("USER", "ADMIN")
.antMatchers("/user/registration", "/u/*", "/login*", "/forgotPassword",
"/user/resetPassword", "user/savePassword").permitAll()
.antMatchers(HttpMethod.POST, "/url").permitAll()
.antMatchers("/delete/**").hasAnyAuthority("ADMIN", "USER")
.antMatchers("/admin").hasAuthority("ADMIN")
.antMatchers("/u/*").permitAll()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/user", true)
.permitAll()
.and()
.logout().permitAll()
.and()
.exceptionHandling().accessDeniedPage("/403")
.and()
.oauth2Login();
}
}
Index With Github Login Link
<!DOCTYPE html>
<html xmlns:th="http:/www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Index</title>
</head>
<body>
Index
<div sec:authorize="isAuthenticated()">
Welcome <b><span sec:authentication="name">Username</span></b>
<i><span sec:authentication="principal.authorities">Roles</span></i>
<form th:action="#{/logout}" method="post">
    <input type="submit" value="Logout" />
</div>
<div sec:authorize="isAuthenticated() == false">
<input type="button" value="Login" />
With GitHub: click here
</div>
<div align="center">
<h1>Shorten My URL</h1>
<br />
<form action="#" th:action="#{/url}"
method="post">
<table border="0" cellpadding="10">
<tr>
<td>URL:</td>
<td><input type="text" name="url"/></td>
</tr>
<tr>
<td colspan="2"><button type="submit">Save</button></td>
</tr>
</table>
</form>
<div sec:authorize="hasAuthority('ADMIN')">Admin</div>
<div sec:authorize="hasAuthority('USER')">User</div>
</div>
</body>
</html>
Registration Controller - Where I would LIKE the Magic to Happen
package com.github.ahimsaka.shorturl.controller;
import com.github.ahimsaka.shorturl.dto.PasswordDto;
import com.github.ahimsaka.shorturl.dto.UserDto;
import com.github.ahimsaka.shorturl.entity.User;
import com.github.ahimsaka.shorturl.entity.VerificationToken;
import com.github.ahimsaka.shorturl.exception.UserAlreadyExistException;
import com.github.ahimsaka.shorturl.exception.UserNotFoundException;
import com.github.ahimsaka.shorturl.exception.util.GenericResponse;
import com.github.ahimsaka.shorturl.registration.OnRegistrationCompleteEvent;
import com.github.ahimsaka.shorturl.service.UserSecurityService;
import com.github.ahimsaka.shorturl.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.MessageSource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.reactive.result.view.RedirectView;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.net.http.HttpRequest;
import java.util.Calendar;
import java.util.Locale;
import java.util.Optional;
import java.util.UUID;
#RestController
public class RegistrationController {
private final Logger log = LoggerFactory.getLogger(RegistrationController.class);
private UserService userService;
private ApplicationEventPublisher applicationEventPublisher;
private MessageSource messages;
private JavaMailSender mailSender;
private UserSecurityService userSecurityService;
RegistrationController(UserService userService, ApplicationEventPublisher applicationEventPublisher,
MessageSource messages, JavaMailSender mailSender, UserSecurityService userSecurityService) {
this.userService = userService;
this.messages = messages;
this.applicationEventPublisher = applicationEventPublisher;
this.mailSender = mailSender;
this.userSecurityService = userSecurityService;
}
#GetMapping("/user/registration")
public ModelAndView showRegistrationForm() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("registration");
UserDto userDto = new UserDto();
modelAndView.getModel().put("user", userDto);
return modelAndView;
}
#PostMapping("/user/registration")
public ModelAndView registerUserAccount(#ModelAttribute("user") #Valid UserDto userDto,
HttpServletRequest request, Errors errors) {
try {
User registered = userService.registerNewUserAccount(userDto);
String appUrl = request.getContextPath();
applicationEventPublisher.publishEvent(new OnRegistrationCompleteEvent(registered, request.getLocale(), appUrl));
} catch (UserAlreadyExistException uaeEx) {
ModelAndView mav = new ModelAndView("registration", "user", userDto);
mav.addObject("message", "An account for that username/email already exists.");
return mav;
} catch (RuntimeException ex) {
return new ModelAndView("emailError", "user", userDto);
}
return new ModelAndView("successRegister", "user", userDto);
}
#GetMapping("/registrationConfirm")
public ModelAndView confirmRegistration(WebRequest request,
#RequestParam("token") String token) {
ModelAndView mav = new ModelAndView();
Locale locale = request.getLocale();
VerificationToken verificationToken = userService.getVerificationToken(token);
if (verificationToken == null) {
String message = messages.getMessage("auth.message.invalidToken", null, locale);
mav.setViewName("badUser");
mav.getModel().put("message", message);
return mav;
}
User user = verificationToken.getUser();
Calendar cal = Calendar.getInstance();
if ((verificationToken.getExpiryDate().getTime() - cal.getTime().getTime()) <= 0) {
String messageValue = messages.getMessage("auth.message.expired", null, locale);
mav.setViewName("badUser");
mav.getModel().put("message", messageValue);
mav.getModel().put("token", token);
mav.getModel().put("expired", true);
return mav;
}
user.setEnabled(true);
userService.saveRegisteredUser(user);;
mav.setViewName("login");
mav.getModel().put("message", messages.getMessage("message.accountVerified", null, locale));
return mav;
}
#GetMapping("/user/resendRegistrationToken")
public GenericResponse resendRegistrationToken(HttpServletRequest request,
#RequestParam("token") String existingToken) {
VerificationToken newToken = userService.generateNewVerificationToken(existingToken);
User user = userService.getUser(newToken.getToken());
SimpleMailMessage email = constructResendVerificationTokenEmail(getAppUrl(request), request.getLocale(), newToken, user);
mailSender.send(email);
return new GenericResponse(messages.getMessage("message.resendToken", null, request.getLocale()));
}
#PostMapping("/user/resetPassword")
public ModelAndView resetPassword(HttpServletRequest request, #RequestParam("email") String userEmail) {
User user = userService.findByUsername(userEmail);
if (user == null) {
throw new UserNotFoundException();
}
String token = UUID.randomUUID().toString();
userService.createPasswordResetTokenForUser(user, token);
mailSender.send(constructResetTokenEmail(getAppUrl(request), request.getLocale(), token, user));
ModelAndView mav = new ModelAndView("login");
mav.getModel().put("message", messages.getMessage("message.resetPasswordEmail", null, request.getLocale()));
return mav;
}
#GetMapping("/user/changePassword")
public ModelAndView showChangePasswordPage(Locale locale, #RequestParam("token") String token) {
ModelAndView mav = new ModelAndView();
String result = userSecurityService.validatePasswordResetToken(token);
if (result != null) {
String message = messages.getMessage("auth.message." + result, null, locale);
mav.getModel().put("message", message);
mav.setViewName("login");
} else {
mav.getModel().put("password", new PasswordDto());
mav.getModel().put("token", token);
mav.setViewName("updatePassword");
}
return mav;
}
#PostMapping("/user/savePassword")
public ModelAndView savePassword(HttpServletRequest request,
#ModelAttribute("password") #Valid PasswordDto passwordDto) {
ModelAndView mav = new ModelAndView();
String result = userSecurityService.validatePasswordResetToken(passwordDto.getToken());
if (result != null) {
mav.setViewName("updatePassword");
mav.getModel().put("message", messages.getMessage("auth.message." + result, null, request.getLocale()));
return mav;
}
Optional user = userService.getUserByPasswordResetToken(passwordDto.getToken());
if (user.isPresent()) {
userService.changeUserPassword((User) user.get(), passwordDto.getNewPassword(), passwordDto.getToken());
mav.setViewName("login");
mav.getModel().put("message", messages.getMessage("message.resetPasswordSuc", null, request.getLocale()));
} else {
mav.setViewName("updatePassword");
mav.getModel().put("message", messages.getMessage("auth.message.invalid", null, request.getLocale()));
}
return mav;
}
#GetMapping("/forgotPassword")
private ModelAndView forgotPasswordPage() {
return new ModelAndView("forgotPassword");
}
#GetMapping("login/oauth2/code/github")
// This does nothing but I haven't removed it yet.
private ModelAndView oauthUserSignIn(HttpServletRequest request, Authentication authentication) {
return new ModelAndView("user");
}
// NON API
private SimpleMailMessage constructResendVerificationTokenEmail (String contextPath, Locale locale, VerificationToken newToken, User user){
String url = contextPath + "/registrationConfirm?token=" + newToken.getToken();
String message = messages.getMessage("message.resendToken", null, locale);
return constructEmail("Resend Registration Token", message + " \r\n" + url, user);
}
private SimpleMailMessage constructResetTokenEmail(String contextPath, Locale locale, String token, User user) {
String url = contextPath + "/user/changePassword?token=" + token;
String message = messages.getMessage("message.resetPassword", null, locale);
return constructEmail("Reset Password", message + " \r\n" + url, user);
}
private SimpleMailMessage constructEmail(String subject, String body, User user) {
SimpleMailMessage email = new SimpleMailMessage();
email.setSubject(subject);
email.setText(body);
email.setTo(user.getUsername());
return email;
}
private String getAppUrl(HttpServletRequest request) {
return "http://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
}
}

Simple Spring Security example not logging in

Morning!
Just started learning Spring Security with the help of the Baeldung tutorial at https://www.baeldung.com/spring-security-authentication-with-a-database.
However, it's not quite working. What I'm trying to do is connecting my simple H2 database, containing a User table with id, username and password (in plaintext for simplicity), with my secured web application.
I created WebSecurityConfig (extending WebSecurityConfigurerAdapter, see below), MyUserDetailsService (implementing UserDetailsService, see below) and LoggedInUser (implementing UserDetails, see below) classes.
WebSecurityConfig: This should secure all pages except home, login and register pages, which is working. Also, globalSecurityConfiguration should enable the login function by linking to the userDetailsService.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
#EnableWebSecurity
#ComponentScan(basePackageClasses = MyUserDetailsService.class)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private MyUserDetailsService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home", "/register").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Autowired
public void globalSecurityConfiguration(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
}
MyUserDetailsService: This gets the Repository injection to access my database. I check the database for the username, and if it's present, I return a new LoggedInUser.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.List;
#Service
public class MyUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) {
List<User> users = userRepository.findByUsername(username);
if (users.size() == 0) {
throw new UsernameNotFoundException(username);
}
return new LoggedInUser(users.get(0));
}
}
And finally, the LoggedInUser class:
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
public class LoggedInUser implements UserDetails {
private User user;
public LoggedInUser(User user) {
this.user= user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return AuthorityUtils.createAuthorityList("ROLE_USER");
}
#Override
public String getPassword() {
return user.getPassword();
}
#Override
public String getUsername() {
return user.getNickname();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
When I try to log in with some nonexistent username, the error message pops as it should. However, when I'm trying to log in with an existing username (with any password, right or wrong), it's not giving any error message but isn't logging me in either (at least I still can't access other secured pages of the app).
I'm omitting User and UserRepository classes since they're just pretty straightforward and well tested. My login page looks like that:
<html xmlns:th="http://www.thymeleaf.org" xmlns:tiles="http://www.thymeleaf.org">
<head>
<title>Spring Security Example</title>
</head>
<body>
<div class="container">
<form name="f" th:action="#{/login}" method="post">
<fieldset>
<legend>Please Login</legend>
<div th:if="${param.error}" class="alert alert-error">
Invalid username and password.
</div>
<div th:if="${param.logout}" class="alert alert-success">
You have been logged out.
</div>
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" name="username"/>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password"/>
</div>
<button type="submit" class="btn btn-primary">Login</button>
</fieldset>
</form>
</div>
</body>
</html>
I know that the loadUserByUsername method isn't touching the password, but from what I've read, checking if the right password was entered happens automatically within the Security framework.
I also tried to implement my own AuthenticationProvider to use instead of the UserDetailsService to check if both username and password inputs match the database entries within the authenticate method, but then I encountered another problem - wrong credentials now get flagged right, but right credentials produced an error Cannot invoke "Object.toString()" because the return value of "org.springframework.security.core.Authentication.getCredentials()" is null. However the line the error mentioned was the one that reads the password from the user input - and since this only happens for passwords matching the correct one, this shouldn't be null. I'm not posting code here since probably this is a different issue though.
Thanks for any help! Remember, this is like the first time I touched any security framework, so better ELI5 :)

Spring boot authentication issue?

As you can see on : http://website-live-245110.appspot.com/ (gccloud hosted site) following the code logic, any username with 4 characters should be able to log in. Although this is not the case at the moment and i have trouble understanding why.
You should try logging in multiple times to grasp the issue.
CustomAuthenticationProvider.java
package com.spring.authprovider;
import java.util.ArrayList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider{
#Autowired
private ThirdPartyAuthProviderClient thirdPartyAuthProviderClient;
// one a user logs in, the authentication variable is filled with the details of the authentication
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// when the user logs in to the application, our object will be filled by spring
String name = authentication.getName();
Object password = authentication.getCredentials(); //object that encapsulates password that user types
// not printing or storing password anyone
if(thirdPartyAuthProviderClient.shouldAuthenticate(name,password)) {
// the array list is for roles, because we are not using it now, we are sending it an empty one
return new UsernamePasswordAuthenticationToken(name, password, new ArrayList<>());
} else {
System.out.println("authentication failed for user: " + name);
}
return null;
}
#Override
public boolean supports(Class<?> authentication) {
// there are multiple ways of authentication, use use username and password
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
ThirdPartyAuthProviderClient.java
package com.spring.authprovider;
import org.springframework.stereotype.Component;
#Component
public class ThirdPartyAuthProviderClient {
//emulates request to third party application
public boolean shouldAuthenticate(String username, Object password) {
// 3rd party request to see if user is correct or no or should be logged in
// user with username with 4 digits can be logged in to the application
return username.length() == 4;
}
}
WebSecurityConfig.java
package com.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import com.spring.authprovider.CustomAuthenticationProvider;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider authProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/", "/home", "/time").permitAll() // any request matching /, /home, /time
// can be accessed by anyone
.anyRequest().authenticated() // any other request needs to be authenticated
.and().authorizeRequests().antMatchers("/admin/**") // only admin can access /admin/anything
.hasRole("ADMIN")
.and().formLogin().loginPage("/login") // permit all to form login--- we use loginPage to use custom page
.permitAll()
.and().logout() // permit all to form logout
.permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//specify auth provider
auth.authenticationProvider(authProvider);
}
// configuration of static resources
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/templates/**", "/assets/**");
}
}
MvcConfig.java
package com.spring;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
public class MvcConfig implements WebMvcConfigurer {
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/home").setViewName("home");
registry.addViewController("/").setViewName("home");
registry.addViewController("/hello").setViewName("hello");
registry.addViewController("/login").setViewName("login");
}
}
Templates
hello.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Hello World!</title>
</head>
<body>
<h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
<form th:action="#{/logout}" method="post">
<input type="submit" value="Sign Out"/>
</form>
</body>
</html>
home.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Spring Security Example</title>
</head>
<body>
<h1>Welcome!</h1>
<p>Click <a th:href="#{/hello}">here</a> to see a greeting.</p>
</body>
</html>
login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Spring Security Example </title>
</head>
<body>
<div th:if="${param.error}">
Invalid username and password.
</div>
<div th:if="${param.logout}">
You have been logged out.
</div>
<form th:action="#{/login}" method="post">
<div><label> User Name : <input type="text" name="username"/> </label></div>
<div><label> Password: <input type="password" name="password"/> </label></div>
<div><input type="submit" value="Sign In"/></div>
</form>
</body>
</html>
I expect it to either log me in when a username with 4 characters is entered, Or output Invalid username and password. Error.
Code is here : https://github.com/jeffpascal/Spring-and-springboot/tree/devs/SpringSecurity

Display user name in Spring

I can login and logout, and display a list of users on a page doing this:
<li th:each="user : ${users}">
<span th:text="${user.firstname}+' '+${user.lastname}"></span>
</li>
I would now simply like to display the name of the currently logged in user, but I am not sure how. I would like to add it to a header fragment so every page shows clearly who the user logged in is.
LoginForm.java:
package com.demo.spring.domain;
import org.hibernate.validator.constraints.NotEmpty;
public class LoginForm {
public String getAccountname() {
return accountname;
}
public void setAccountname(String accountname) {
this.accountname = accountname;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#NotEmpty
String accountname;
#NotEmpty
String password;
}
login.html:
<h3>User Login</h3>
<form action="#" th:action="#{/user/login}" th:object="${user}" method="post">
<!--<input type="hidden" th:field="*{id}"/>-->
<p>Account Name:</p> <input type="text" th:field="*{accountname}"/>
<p>Password:</p> <input type="password" th:field="*{password}"/>
<p/><input type="submit" value="Login"/>
</form>
<div th:if="${message!=null}">
<br/>
<span th:text="${message}"/>
</div>
UserController code for logging in:
package com.demo.spring.controller;
import com.demo.spring.domain.LoginForm;
import com.demo.spring.domain.User;
import com.demo.spring.domain.UserSearchForm;
import com.demo.spring.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.util.List;
#Controller
#RequestMapping(value = "/user")
public class UserController {
#Autowired
UserService userService;
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginView(Model model)
{
LoginForm user = new LoginForm();
model.addAttribute("user", user);
return "login";
}
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(Model model, #Valid #ModelAttribute("user") LoginForm user, BindingResult bindingResult, HttpSession session)
{
if(bindingResult.hasErrors())
{
model.addAttribute("user", user);
model.addAttribute("message", "Please provide information in each field");
return "login";
}
if (userService.validateLogin(user)==false)
{
model.addAttribute("user", user);
model.addAttribute("message", "Your accountname and/or password are incorrect");
return "login";
}
session.setAttribute("login", true);
return "redirect:/";
}
UserService
package com.demo.spring.service;
import com.demo.spring.domain.LoginForm;
import com.demo.spring.domain.UserSearchForm;
import com.demo.spring.domain.User;
import com.demo.spring.domain.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
#Service
public class UserService {
public boolean validateLogin(LoginForm user)
{
List<User> users = userRepository.checkUserInput(user.getAccountname(),user.getPassword());
return users !=null && users.size()>0;
}
We put the logged in users' name in the session
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(Model model, #Valid #ModelAttribute("user") LoginForm user, BindingResult bindingResult, HttpSession session)
{
...
session.setAttribute("accountName", user.getAccountName());
session.setAttribute("login", true);
return "redirect:/";
}
Once put in the session, the session variables can simply be accessed as ${session.accountName}. So you can use <span th:text="${session.accountName}"></span> in your header fragment.

Resources