Tomcat error in Spring Security (Spring + MySQL) - spring

Why, when the admin wants to add a new admin, I get an error in the tomcat. I kind of wrote the code correctly, you can see probably made a mistake. I want the admin to be able to add a new user and give him a username, password, role (admin or user). And also that he stored information in a database MySQL. Problem on page "AddUser.JSP"
Tomcat Error
AdminController
#Controller
#RequestMapping("/admin")
public class AdminController {
#Autowired
private StudentService studentService;
private UserService userService;
#GetMapping("/allStudentsAdmin")
public ModelAndView allStudentsForUser() {
ModelAndView mv = new ModelAndView();
List<Student> studentList = studentService.getAllStudents();
mv.addObject("studentList", studentList);
mv.setViewName("allStudentsAdmin");
return mv;
}
#GetMapping(value = "/deleteStudent/{id}")
public ModelAndView deleteUserById(#PathVariable Long id) {
studentService.deleteStudentById(id);
ModelAndView mv = new ModelAndView("redirect:/admin/allStudentsAdmin");
return mv;
}
#GetMapping(value = "/editStudent/{id}")
public ModelAndView displayEditUserForm(#PathVariable Long id) {
ModelAndView mv = new ModelAndView("adminEditStudent");
Student student = studentService.getStudentById(id);
mv.addObject("headerMessage", "Редактирование студента");
mv.addObject("student", student);
return mv;
}
#PostMapping(value = "/editStudent")
public String saveEditedUser(
#RequestParam("id") Long id,
#RequestParam("name") String name,
#RequestParam("surname") String surname,
#RequestParam("avatar") MultipartFile file) {
try {
studentService.updateStudent(name, surname, file, studentService.getStudentById(id));
} catch (FileSystemException ex) {
ex.printStackTrace();
} catch (IOException e) {
return "redirect:/errors";
}
return "redirect:/admin/allStudentsAdmin";
}
#GetMapping(value = "/addStudentAdmin")
public ModelAndView displayNewUserForm() {
ModelAndView mv = new ModelAndView("addStudentAdmin");
mv.addObject("headerMessage", "Add Student Details");
mv.addObject("student", new Student());
return mv;
}
#PostMapping(value = "/addStudentAdmin")
public String saveNewStudent(#RequestParam("name") #NonNull String name,
#RequestParam("surname") #NonNull String surname,
#RequestParam("avatar") MultipartFile file)
throws IOException {
Student student = new Student();
student.setSurname(surname);
student.setName(name);
if (file != null && !file.isEmpty()) {
student.setAvatar(studentService.saveAvatarImage(file).getName());
}
studentService.saveStudent(student);
return "redirect:/admin/allStudentsAdmin";
}
#GetMapping(value = "/addUser")
public ModelAndView displayAddUserForm() {
ModelAndView mv = new ModelAndView("addUser");
mv.addObject("user", new User());
return mv;
}
#PostMapping(value = "/addUser")
public String saveNewUser(#RequestParam("login") #NonNull String login,
#RequestParam("password") #NonNull String password,
#RequestParam("role") #NonNull String role)
throws IOException {
User user = new User();
user.setPassword(password);
user.setLogin(login);
userService.saveUser(user);
return "redirect:/admin/allStudentsAdmin";
}
}
AddUser.JSP
<body>
<div class="add">
<br>
<br>
<br>
<br>
<center>
<form:form method="POST" action="${pageContext.request.contextPath}/admin/addUser" enctype="multipart/form-data">
<table>
<tr>
<td><label path="Login">Login</label></td>
<td><input type="text" name="login"/></td>
</tr>
<tr>
<td><label path="Password">Password</label></td>
<td><input type="text" name="password"/></td>
</tr>
<tr>
<td><select path="role" name="nubexSelect" size="3" multiple form="nubexForm">
<option>Admin</option>
<option>User</option>
</select></td>
<td><input class="btn btn-primary" type="submit" value="Submit"></td>
</tr>
</table>
</form:form>
</center>
</div>
</body>
User.JAVA
#Entity
#Table(name = "user")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String login;
private String password;
private String role;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
#Override
public String toString() {
return "Student{" +
"id=" + id +
", login='" + login + '\'' +
", password='" + password + '\'' +
", role='" + role + '\'' +
'}';
}
}
UserRepository.JAVA
#Repository
public interface UserRepository extends CrudRepository<User, Long>{
}
UserService.JAVA
public interface UserService {
boolean saveUser(User user);
User updateUser(String login, String password, String role, User targetUser) throws IOException;
}
UserServiceImpl.java
#Service
#Transactional
public class UserServiceImpl implements UserService {
#Value("${storage.location}")
private String storageLocation;
private UserRepository repository;
public UserServiceImpl() {
}
#Autowired
public UserServiceImpl(UserRepository repository) {
super();
this.repository = repository;
}
#Override
public boolean saveUser(User user) {
try {
repository.save(user);
return true;
} catch (Exception ex) {
return false;
}
}
#Override
public User updateUser(String login, String password, String role, User targetUser)
throws IOException {
if (login != null && !login.equals(targetUser.getLogin())) {
targetUser.setLogin(login);
}
if (password != null && !password.equals(targetUser.getPassword())) {
targetUser.setPassword(password);
}
if (role != null && !role.equals(targetUser.getRole())) {
targetUser.setRole(role);
}
return targetUser;
}
SecurityConfig
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password(passwordEncoder().encode("1234")).roles("ADMIN")
.and()
.withUser("user").password(passwordEncoder().encode("user1234")).roles("USER")
.and();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/**").permitAll()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/allStudents")
.and()
.logout()
.and()
.csrf().disable();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

Your saveNewUser method is not having correct configuration
From JSP you are passing Form as multipart/form-data but in Controller class you have #RequestParam
Change method to User Object
#PostMapping(value = "/addUser", consumes = "multipart/form-data")
public String saveNewUser(#ModelAttribute User user)
throws IOException {
userService.saveUser(user);
return "redirect:/admin/allStudentsAdmin";
}
Also change the Role combo name parameter
<select path="role" name="role" size="3"

Related

AuthenticationManager returning Bad Credentials

I have this error and I don't know what it could be. I'm following a tutorial to do the User authentication, But when I try to do the authentication it throws a "Bad credentials" but the credentials are right. I'm using MongoDB.
From the tutorial I'm following with these steps it should work (other users confirmed that they did) but the only thing I did differently from the tutorial is in the "ClientEntity" class that already existed and I just implemented what I needed.
User return:
ClientEntity(id=63166ddbe3ea6c4fffd70818, clientName=Test, clientCpf=000.000.000-00, clientEmail=teste2#example.com, clientPassword=2b598e4c0e79baf9dc9211ad303e7626, clientIsBlocked=false, clientBirthDate=1989-05-20, creditCards=[CreditCardEntity()], clientCategory=[ACAO, COMEDIA])
My request to signin, I'm logging in by email and password:
AccountCredentials: AccountCredentials(email=teste2#example.com, password=2b598e4c0e79baf9dc9211ad303e7626)
I know the problem is in that class in "authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(email, password));" but I don't know how to solve it, because the credentials are correct
My AuthService class:
#Service
public class AuthService {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private JwtTokenProvider tokenProvider;
#Autowired
private ClientRepository repository;
public ResponseEntity signin(AccountCredentials data) {
try {
var email = data.getEmail();
var password = data.getPassword();
log.info("METODO SIGNIN, AccountCredentials: " + data);
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(email, password));
log.info("FOI AUTENTICADO!");
var user = repository.findByClientEmail(email);
log.info("O valor de User é: " + user);
var tokenResponse = new Token();
if (user != null) {
tokenResponse = tokenProvider.createAccessToken(email, user.getRoles());
} else {
throw new UsernameNotFoundException("Email " + email + " not found!");
}
return ResponseEntity.ok(tokenResponse);
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println(e.getCause());
System.out.println(e.getLocalizedMessage());
throw new BadCredentialsException("Invalid email/password supplied!");
}
}
public ResponseEntity refreshToken(String email, String refreshToken) {
var user = repository.findByClientEmail(email);
var tokenResponse = new Token();
if (user != null) {
tokenResponse = tokenProvider.refreshToken(refreshToken);
} else {
throw new UsernameNotFoundException("Email " + email + " not found!");
}
return ResponseEntity.ok(tokenResponse);
}
}
My ClientEntity:
#Data
#Document(collection = "Client")
public class ClientEntity implements UserDetails, Serializable {
private static final long serialVersionUID = 1L;
#Id
private String id;
private String clientName;
private String clientCpf;
private String clientEmail;
private String clientPassword;
private boolean clientIsBlocked = false;
private LocalDate clientBirthDate;
private List<CreditCardEntity> creditCards;
private List<ClientCategoryEnum> clientCategory;
private List<Permission> permissions;
public List<String> getRoles() {
List<String> roles = new ArrayList<>();
for (Permission permission : permissions) {
roles.add("USER");
roles.add(permission.getDescription());
}
return roles;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.permissions;
}
#Override
public String getPassword() {
return this.clientPassword;
}
#Override
public String getUsername() {
return this.clientEmail;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
My Class Permission:
#Data
#Document(collection = "Roles")
public class Permission implements GrantedAuthority, Serializable {
private static final long serialVersionUID = 1L;
#Id
private Long id;
private String description;
#Override
public String getAuthority() {
return this.description;
}
}
My SecurityConfig:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtTokenProvider tokenProvider;
/*
#Bean
public UserDetailsService userDetailsService() {
return super.userDetailsService();
}
*/
#Bean
public PasswordEncoder passwordEncoder() {
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
DelegatingPasswordEncoder passwordEncoder = new DelegatingPasswordEncoder("pbkdf2", encoders);
passwordEncoder.setDefaultPasswordEncoderForMatches(new Pbkdf2PasswordEncoder());
return passwordEncoder;
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(
"/auth/signin",
"/auth/refresh"
).permitAll()
.antMatchers("/api/movie_search/**").authenticated()
.and()
.cors()
.and()
.apply(new JwtConfigurer(tokenProvider));
}
}
My CLass TokenProvider:
#Service
public class JwtTokenProvider{
#Value("${security.jwt.token.secret-key:secret}")
private String secretKey = "secret";
#Value("${security.jwt.token.expire-length:3600000}")
private long validityInMilliseconds = 3600000; // 1h
#Autowired
private UserDetailsService userDetailsService;
Algorithm algorithm = null;
#PostConstruct
protected void init() {
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
algorithm = Algorithm.HMAC256(secretKey.getBytes());
}
public Token createAccessToken(String email, List<String> roles) {
Date now = new Date();
Date validity = new Date(now.getTime() + validityInMilliseconds);
var accessToken = getAccessToken(email, roles, now, validity);
var refreshToken = getRefreshToken(email, roles, now);
return new Token(email, true, now, validity, accessToken, refreshToken);
}
public Token refreshToken(String refreshToken) {
if (refreshToken.contains("Bearer ")) refreshToken =
refreshToken.substring("Bearer ".length());
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT decodedJWT = verifier.verify(refreshToken);
String email = decodedJWT.getSubject();
List<String> roles = decodedJWT.getClaim("roles").asList(String.class);
return createAccessToken(email, roles);
}
private String getAccessToken(String email, List<String> roles, Date now, Date validity) {
String issuerUrl = ServletUriComponentsBuilder
.fromCurrentContextPath().build().toUriString();
return JWT.create()
.withClaim("roles", roles)
.withIssuedAt(now)
.withExpiresAt(validity)
.withSubject(email)
.withIssuer(issuerUrl)
.sign(algorithm)
.strip();
}
private String getRefreshToken(String email, List<String> roles, Date now) {
Date validityRefreshToken = new Date(now.getTime() + (validityInMilliseconds * 3));
return JWT.create()
.withClaim("roles", roles)
.withIssuedAt(now)
.withExpiresAt(validityRefreshToken)
.withSubject(email)
.sign(algorithm)
.strip();
}
public Authentication getAuthentication(String token) {
DecodedJWT decodedJWT = decodedToken(token);
UserDetails userDetails = this.userDetailsService
.loadUserByUsername(decodedJWT.getSubject());
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}
private DecodedJWT decodedToken(String token) {
Algorithm alg = Algorithm.HMAC256(secretKey.getBytes());
JWTVerifier verifier = JWT.require(alg).build();
DecodedJWT decodedJWT = verifier.verify(token);
return decodedJWT;
}
public String resolveToken(HttpServletRequest req) {
String bearerToken = req.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring("Bearer ".length());
}
return null;
}
public boolean validateToken(String token) {
DecodedJWT decodedJWT = decodedToken(token);
try {
if (decodedJWT.getExpiresAt().before(new Date())) {
return false;
}
return true;
} catch (Exception e) {
throw new InvalidJwtAuthenticationException("Expired or invalid JWT token!");
}
}
}
My ClientService:
#Service
public class ClientService implements UserDetailsService {
private final ClientRepository clientRepository;
private final ModelMapper modelMapper;
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
var user = clientRepository.findByClientEmail(email);
if (user != null) {
return user;
} else {
throw new UsernameNotFoundException("Email " + email + " not found!");
}
}
}
In debug I came across this:
UsernamePasswordAuthenticationToken
Credentials=[PROTECTED], Authenticated=false, Details=null, Granted Authorities=[]]
Short answer: Passwords stored in your DB must be encrypted by PasswordEncoder.
Long answer:
You created a bean of type PasswordEncoder, so it is used by AuthenticationProvider when you call .authenticate() method of AuthenticationManager class.
As stated in java docs, DelegatingPasswordEncoder expects your passwords to be stored in DB in format like this:
{encoderId}encryptedPassword
You use pbkdf2 password encoder as a default encoder, so passwords in DB should look like this:
{pbkdf2}encryptedPassword
By the look of the returned ClientEntity, you haven't used the PasswordEncoder when you saved this entity to a DB, so the raw password is stored instead of an encrypted one.
The authentication flow should look like this:
your app registers a user, stores his password in DB in encrypted form using PasswordEncoder bean;
user passes his username (email or whatever is neccessary) and raw password to your app through login form of API endpoint;
AuthenticationProvider retrieves encrypted password from the DB and compares it with the raw password the user has provided

Error creating bean with name 'registrationListener': Unsatisfied dependency expressed through field 'service' (Showing this after building war)

These codes runs fine on my STS but not running after making jar/war.
also tried with deploying on external tomcat. what is making the differace after making the war?? i am getting lost.
This is the registration listener class
#Component
public class RegistrationListener implements ApplicationListener<OnRegistrationCompleteEvent> {
#Autowired
private IUserService service;
#Autowired
private MessageSource messages;
#Autowired
private JavaMailSender mailSender;
#Autowired
private Environment env;
// API
#Override
public void onApplicationEvent(final OnRegistrationCompleteEvent event) {
this.confirmRegistration(event);
}
private void confirmRegistration(final OnRegistrationCompleteEvent event) {
final User user = event.getUser();
final String token = UUID.randomUUID().toString();
service.createVerificationTokenForUser(user, token);
final SimpleMailMessage email = constructEmailMessage(event, user, token);
mailSender.send(email);
}
//
private SimpleMailMessage constructEmailMessage(final OnRegistrationCompleteEvent event, final User user, final String token) {
final String recipientAddress = user.getEmail();
final String subject = "Registration Confirmation";
final String confirmationUrl = event.getAppUrl() + "/registrationConfirm?token=" + token;
String message= "Your SECRET for Google Authenticator: "+user.getSecret()+"\n";
message+= messages.getMessage("message.regSuccLink", null, "You registered successfully. To confirm your registration, please click on the below link.", event.getLocale());
final SimpleMailMessage email = new SimpleMailMessage();
email.setTo(recipientAddress);
email.setSubject(subject);
email.setText(message + " \r\n" + confirmationUrl);
email.setFrom(env.getProperty("support.email"));
return email;
}
}
I have tried with #Service and without #Service tag
#Service
public interface IUserService {
User registerNewUserAccount(UserDto accountDto);
User getUser(String verificationToken);
void saveRegisteredUser(User user);
void deleteUser(User user);
void createVerificationTokenForUser(User user, String token);
VerificationToken getVerificationToken(String VerificationToken);
VerificationToken generateNewVerificationToken(String token);
void createPasswordResetTokenForUser(User user, String token);
User findUserByEmail(String email);
PasswordResetToken getPasswordResetToken(String token);
Optional<User> getUserByPasswordResetToken(String token);
Optional<User> getUserByID(long id);
void changeUserPassword(User user, String password);
boolean checkIfValidOldPassword(User user, String password);
String validateVerificationToken(String token);
String generateQRUrl(User user) throws UnsupportedEncodingException;
User updateUser2FA(boolean use2FA);
List<String> getUsersFromSessionRegistry();
NewLocationToken isNewLoginLocation(String username, String ip);
String isValidNewLocationToken(String token);
void addUserLocation(User user, String ip);
}
is this where the problem is happening?
#Service
#Transactional
public class UserService implements IUserService {
#Autowired
private UserRepository userRepository;
#Autowired
private VerificationTokenRepository tokenRepository;
#Autowired
private PasswordResetTokenRepository passwordTokenRepository;
#Autowired
private PasswordEncoder passwordEncoder;
#Autowired
private RoleRepository roleRepository;
#Autowired
private SessionRegistry sessionRegistry;
#Autowired
#Qualifier("GeoIPCountry")
private DatabaseReader databaseReader;
#Autowired
private UserLocationRepository userLocationRepository;
#Autowired
private NewLocationTokenRepository newLocationTokenRepository;
#Autowired
private Environment env;
public static final String TOKEN_INVALID = "invalidToken";
public static final String TOKEN_EXPIRED = "expired";
public static final String TOKEN_VALID = "valid";
public static String QR_PREFIX = "https://chart.googleapis.com/chart?chs=200x200&chld=M%%7C0&cht=qr&chl=";
public static String APP_NAME = "SpringRegistration";
// API
#Override
public User registerNewUserAccount(final UserDto accountDto) {
if (emailExists(accountDto.getEmail())) {
throw new UserAlreadyExistException("There is an account with that email address: " + accountDto.getEmail());
}
final User user = new User();
user.setFirstName(accountDto.getFirstName());
user.setLastName(accountDto.getLastName());
user.setPassword(passwordEncoder.encode(accountDto.getPassword()));
user.setEmail(accountDto.getEmail());
user.setUsing2FA(accountDto.isUsing2FA());
user.setRoles(Arrays.asList(roleRepository.findByName("ROLE_USER")));
return userRepository.save(user);
}
#Override
public User getUser(final String verificationToken) {
final VerificationToken token = tokenRepository.findByToken(verificationToken);
if (token != null) {
return token.getUser();
}
return null;
}
#Override
public VerificationToken getVerificationToken(final String VerificationToken) {
return tokenRepository.findByToken(VerificationToken);
}
#Override
public void saveRegisteredUser(final User user) {
userRepository.save(user);
}
#Override
public void deleteUser(final User user) {
final VerificationToken verificationToken = tokenRepository.findByUser(user);
if (verificationToken != null) {
tokenRepository.delete(verificationToken);
}
final PasswordResetToken passwordToken = passwordTokenRepository.findByUser(user);
if (passwordToken != null) {
passwordTokenRepository.delete(passwordToken);
}
userRepository.delete(user);
}
#Override
public void createVerificationTokenForUser(final User user, final String token) {
final VerificationToken myToken = new VerificationToken(token, user);
tokenRepository.save(myToken);
}
#Override
public VerificationToken generateNewVerificationToken(final String existingVerificationToken) {
VerificationToken vToken = tokenRepository.findByToken(existingVerificationToken);
vToken.updateToken(UUID.randomUUID()
.toString());
vToken = tokenRepository.save(vToken);
return vToken;
}
#Override
public void createPasswordResetTokenForUser(final User user, final String token) {
final PasswordResetToken myToken = new PasswordResetToken(token, user);
passwordTokenRepository.save(myToken);
}
#Override
public User findUserByEmail(final String email) {
return userRepository.findByEmail(email);
}
#Override
public PasswordResetToken getPasswordResetToken(final String token) {
return passwordTokenRepository.findByToken(token);
}
#Override
public Optional<User> getUserByPasswordResetToken(final String token) {
return Optional.ofNullable(passwordTokenRepository.findByToken(token) .getUser());
}
#Override
public Optional<User> getUserByID(final long id) {
return userRepository.findById(id);
}
#Override
public void changeUserPassword(final User user, final String password) {
user.setPassword(passwordEncoder.encode(password));
userRepository.save(user);
}
#Override
public boolean checkIfValidOldPassword(final User user, final String oldPassword) {
return passwordEncoder.matches(oldPassword, user.getPassword());
}
#Override
public String validateVerificationToken(String token) {
final VerificationToken verificationToken = tokenRepository.findByToken(token);
if (verificationToken == null) {
return TOKEN_INVALID;
}
final User user = verificationToken.getUser();
final Calendar cal = Calendar.getInstance();
if ((verificationToken.getExpiryDate()
.getTime() - cal.getTime()
.getTime()) <= 0) {
tokenRepository.delete(verificationToken);
return TOKEN_EXPIRED;
}
user.setEnabled(true);
// tokenRepository.delete(verificationToken);
userRepository.save(user);
return TOKEN_VALID;
}
#Override
public String generateQRUrl(User user) throws UnsupportedEncodingException {
return QR_PREFIX + URLEncoder.encode(String.format("otpauth://totp/%s:%s?secret=%s&issuer=%s", APP_NAME, user.getEmail(), user.getSecret(), APP_NAME), "UTF-8");
}
#Override
public User updateUser2FA(boolean use2FA) {
final Authentication curAuth = SecurityContextHolder.getContext()
.getAuthentication();
User currentUser = (User) curAuth.getPrincipal();
currentUser.setUsing2FA(use2FA);
currentUser = userRepository.save(currentUser);
final Authentication auth = new UsernamePasswordAuthenticationToken(currentUser, currentUser.getPassword(), curAuth.getAuthorities());
SecurityContextHolder.getContext()
.setAuthentication(auth);
return currentUser;
}
private boolean emailExists(final String email) {
return userRepository.findByEmail(email) != null;
}
#Override
public List<String> getUsersFromSessionRegistry() {
return sessionRegistry.getAllPrincipals()
.stream()
.filter((u) -> !sessionRegistry.getAllSessions(u, false)
.isEmpty())
.map(o -> {
if (o instanceof User) {
return ((User) o).getEmail();
} else {
return o.toString()
;
}
}).collect(Collectors.toList());
}
#Override
public NewLocationToken isNewLoginLocation(String username, String ip) {
if(!isGeoIpLibEnabled()) {
return null;
}
try {
final InetAddress ipAddress = InetAddress.getByName(ip);
final String country = databaseReader.country(ipAddress)
.getCountry()
.getName();
System.out.println(country + "====****");
final User user = userRepository.findByEmail(username);
final UserLocation loc = userLocationRepository.findByCountryAndUser(country, user);
if ((loc == null) || !loc.isEnabled()) {
return createNewLocationToken(country, user);
}
} catch (final Exception e) {
return null;
}
return null;
}
#Override
public String isValidNewLocationToken(String token) {
final NewLocationToken locToken = newLocationTokenRepository.findByToken(token);
if (locToken == null) {
return null;
}
UserLocation userLoc = locToken.getUserLocation();
userLoc.setEnabled(true);
userLoc = userLocationRepository.save(userLoc);
newLocationTokenRepository.delete(locToken);
return userLoc.getCountry();
}
#Override
public void addUserLocation(User user, String ip) {
if(!isGeoIpLibEnabled()) {
return;
}
try {
final InetAddress ipAddress = InetAddress.getByName(ip);
final String country = databaseReader.country(ipAddress)
.getCountry()
.getName();
UserLocation loc = new UserLocation(country, user);
loc.setEnabled(true);
userLocationRepository.save(loc);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
private boolean isGeoIpLibEnabled() {
return Boolean.parseBoolean(env.getProperty("geo.ip.lib.enabled"));
}
private NewLocationToken createNewLocationToken(String country, User user) {
UserLocation loc = new UserLocation(country, user);
loc = userLocationRepository.save(loc);
final NewLocationToken token = new NewLocationToken(UUID.randomUUID()
.toString(), loc);
return newLocationTokenRepository.save(token);
}
}
This is my error from jar,war,extarnal tomcat
o.s.b.f.UnsatisfiedDependencyException: Error creating bean with name 'registrationListener': Unsatisfied dependency expressed through field 'service'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService': Unsatisfied dependency expressed through field 'databaseReader'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'GeoIPCountry' defined in class path resource [com/birol/spring/SecSecurityConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.maxmind.geoip2.DatabaseReader]: Factory method 'databaseReader' threw exception; nested exception is java.io.FileNotFoundException: src\main\resources\maxmind\GeoLite2-Country.mmdb (The system cannot find the path specified)
at o.s.b.f.a.GbHi8bXhDyMP2m1NDUSiYmznAJdJzX36DDor$AutowiredFieldElement.resolveFieldValue(GbHi8bXhDyMP2m1NDUSiYmznAJdJzX36DDor.java:659)
at o.s.b.f.a.GbHi8bXhDyMP2m1NDUSiYmznAJdJzX36DDor$AutowiredFieldElement.inject(GbHi8bXhDyMP2m1NDUSiYmznAJdJzX36DDor.java:639)
at o.s.b.f.a.InjectionMetadata.inject(InjectionMetadata.java:119)
at o.s.b.f.a.GbHi8bXhDyMP2m1NDUSiYmznAJdJzX36DDor.postProcessProperties(GbHi8bXhDyMP2m1NDUSiYmznAJdJzX36DDor.java:399)
at o.s.b.f.s.GbHi8bXhDyMP2m1NDUSiYmznAJdJzX36DD.populateBean(GbHi8bXhDyMP2m1NDUSiYmznAJdJzX36DD.java:1431)
... 46 frames truncated

Spring Security method security not working: PreAuthorize allowing all users instead of limited users

I am trying to implement method level security using spring security. I have annotated 2 separate methods with the #PreAuthorize annotation. In this example, I have 2 users ADMIN and USER. And I have restricted 2 methods both to each of the users. When I try logging in as USER I am able to access both the endpoint restricted to USER (getSomeTextForUser()) as well as to ADMIN(getSomeTextForAdmin()). So this is definitely not right and after viewing multiple tutorials I have not seen the error in my ways.
Expected behavior: person logged in as USER should get an error when trying to access the endpoint /test/admin since it calls getSomeTextForAdmin(). And the similar behavior should happen for the admin when calling /test/user since it calls getSomeTextForUser().
Main class
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
My controller class
#RestController
public class UserController {
#GetMapping("/")
public String home() {
return ("<h1> Welcome </h1>");
}
#GetMapping("/test/admin")
public String test() {
return getSomeTextForAdmin();
}
#GetMapping("/test/user")
public String test2() {
return getSomeTextForUser();
}
#PreAuthorize("hasRole('ROLE_ADMIN')")
public String getSomeTextForAdmin() {
return "For Admin Only!";
}
#PreAuthorize("hasRole('ROLE_USER')")
public String getSomeTextForUser() {
return "For User Only!";
}
}
The security configuration where I've enabled the prePost feature
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/test").hasAnyRole("ADMIN", "USER")
.antMatchers("/").permitAll()
.and().formLogin();
#Bean
public PasswordEncoder getPasswordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
My User details service where I've just placed some default users in memory on startup for testing.
#Repository
public class UserRepositoryImpl implements UserRepository {
Map<String, User> users = new HashMap<>();
public UserRepositoryImpl() {
createDefaultUsers();
}
#Override
public Optional<User> findByUserName(String userName) {
return Optional.of(users.get(userName));
}
private void createDefaultUsers() {
users.put("admin", new User("admin", "pass", "ADMIN"));
users.put("user", new User("user", "pass", "USER"));
}
}
MyUserDetails is here
public class MyUserDetails implements UserDetails {
private final String userName;
private final String password;
private final List<GrantedAuthority> authorities;
public MyUserDetails(User user) {
this.userName = user.getUserName();
this.password = user.getPassword();
this.authorities = Arrays.stream(user.getRoles().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return userName;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
And the user class itself
#Entity
#Table(name = "User")
public class User {
public User(String userName, String password, String roles) {
this.userName = userName;
this.password = password;
this.roles = roles;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
public int getId() {
return id;
}
public void setId(int 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;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
public String getRoles() {
return roles;
}
public void setRoles(String roles) {
this.roles = roles;
}
private String userName;
private String password;
private boolean active;
private String roles;
}
First of all: why do you need this line in your configuration?
.antMatchers("/test").hasAnyRole("ADMIN", "USER")
You don't even have /test endpoint in your controller.
Second thing:
#RestController
public class UserController {
#GetMapping("/")
public String home() {
return ("<h1> Welcome </h1>");
}
#GetMapping("/test/admin")
public String test() {
return getSomeTextForAdmin();
}
#GetMapping("/test/user")
public String test2() {
return getSomeTextForUser();
}
#PreAuthorize("hasRole('ROLE_ADMIN')")
public String getSomeTextForAdmin() {
return "For Admin Only!";
}
#PreAuthorize("hasRole('ROLE_USER')")
public String getSomeTextForUser() {
return "For User Only!";
}
}
It shows you don't understand what Spring Proxy is. Unless you learn it, soon or later you will fall into problems.
I really encourge you to read about it but for now one takeaway to remember:
Annotated methods must be called from different class. In your case you call annotated methods from the same class and Spring doesn't care about any annotation.
You should use somtehing like this:
#Service
public class UserService {
#PreAuthorize("hasRole('ROLE_ADMIN')")
public String getSomeTextForAdmin() {
return "For Admin Only!";
}
#PreAuthorize("hasRole('ROLE_USER')")
public String getSomeTextForUser() {
return "For User Only!";
}
}
#RestController
public class UserController {
#Autowired
private UserService userService;
#GetMapping("/")
public String home() {
return ("<h1> Welcome </h1>");
}
#GetMapping("/test/admin")
public String test() {
return userService.getSomeTextForAdmin();
}
#GetMapping("/test/user")
public String test2() {
return userService.getSomeTextForUser();
}
}

WithMockUser not working, but SecurityContext.getContext().setAuthentification does

I try to mock login with #WithMockUserbut it is not working.
#SpringBootTest
class RoleTest {
#Autowired
private UserService userService;
#Autowired
private RoleService roleService;
private User user;
#BeforeEach
void setUp() {
AuthentificationService.AuthentificationMock.loginAsAdminOnly();
user = new User("maier", "password", "Hansi", "Meier", "hanmai#maier.de", "+4953353535353", roleService.getByName(Role.ROLE_ADMIN).orElse(null));
assertNotNull(user.getRoles());
userService.save(user);
AuthentificationService.AuthentificationMock.logout();
}
#AfterEach
void tearDown() {
AuthentificationService.AuthentificationMock.loginAsAdminOnly();
userService.delete(user);
AuthentificationService.AuthentificationMock.logout();
}
#Test
#WithMockUser(Role.ADMIN)
void testRoleHierarchy() {
boolean succeeded = true;
//WHEN
try {
final Optional<User> byUsername = userService.getByUsernameResolved(user.getUsername());
} catch (Exception e) {
succeeded = false;
}
//THEN
assertTrue(succeeded, "ADMIN has no USER Privileges, check RoleHierarchy");
}
}
If I run test authentication there is an AuthenticationCredentialsNotFoundException and so test fails.
That is the Service which the access is limited:
#Service
#Slf4j
#Transactional
#NoArgsConstructor
public class UserService implements JpaServiceContract<User>, UserDetailsService {
private UserRepository userRepository;
private AuthenticationManager authenticationManager;
private PasswordEncoder passwordEncoder;
#Autowired
public UserService(PasswordEncoder passwordEncoder, AuthenticationManager authenticationManager, UserRepository userRepository) {
this.userRepository = userRepository;
this.authenticationManager = authenticationManager;
this.passwordEncoder = passwordEncoder;
}
#Override
public UserDetails loadUserByUsername(#NotNull String username) {
final String callerName = SharedSecrets.getJavaLangAccess().getStackTraceElement(new Throwable(), 12).getClassName();
if (!callerName.equals("app.config.security.TokenAuthenticationFilter")) {
throw new MethodAccessException();
}
SecurityContextHolder.getContext().setAuthentication(new PreAuthenticatedAuthenticationToken(null, null, Collections.singletonList(new Role(Role.ADMIN))));
Optional<User> user = getByUsernameResolved(username);
SecurityContextHolder.clearContext();
final User user1 = user.orElse(null);
assert user1 != null;
return user1;
}
/**
* This Method is same as {#link #getByUsername(String)} but it resolves all Lazy loaded Properties,
* which could not be resolved outside of the current Transaction anymore
*
* #param username Of the {#link app.model.authentification.User} which should be found
* #return The {#link app.model.authentification.User} found as an {#link java.util.Optional}
*/
#RolesAllowed(value = Role.ROLE_USER)
public Optional<User> getByUsernameResolved(#NotNull String username) {
final Optional<User> userUnresolved = getByUsername(username);
userUnresolved.ifPresent(user -> Hibernate.initialize(user.getRoles()));
return userUnresolved;
}
#RolesAllowed(value = Role.ROLE_USER)
public Optional<User> getByUsername(#NotNull String username) {
if (!existsByUsername(username)) {
throw new RessourceNotFoundException("User not found");
}
return userRepository.findByUsername(username);
}
#Override
#RolesAllowed(Role.ROLE_USER)
public Optional<User> getById(#NotNull Long id) {
return userRepository.findById(id);
}
#Override
#RolesAllowed(Role.ROLE_USER)
public Optional<User> getByIdResolved(#NotNull Long id) {
final Optional<User> byId = getById(id);
if (byId.isPresent()) {
Hibernate.initialize(byId.get().getRoles());
return byId;
} else {
return Optional.empty();
}
}
#RolesAllowed(value = Role.ROLE_ADMIN)
#Override
public Set<User> getAll() {
return (Set<User>) userRepository.findAll();
}
#RolesAllowed(value = Role.ROLE_ADMIN)
#Override
public Set<User> getAllResolved() {
final Set<User> all = getAll();
Hibernate.initialize(all);
return all;
}
public User changePassword(#NotNull String oldPassword, #NotNull String newPassword) {
User user = getLoggedInUser();
user.setPassword(passwordEncoder.encode(newPassword));
return userRepository.save(user);
}
#Override
#RolesAllowed(value = Role.ROLE_USER)
public boolean existsById(#NotNull Long id) {
return userRepository.existsById(id);
}
#RolesAllowed(value = Role.ROLE_USER)
public boolean existsByUsername(#NotNull String username) {
return userRepository.existsByUsername(username);
}
#Override
#RolesAllowed(value = Role.ROLE_ADMIN)
public User save(#NotNull User user) throws RessourceAlreadyExistsException, InvalidParameterException {
if (UserValidator.isFullValid(user))
if (!userRepository.existsByUsername(user.getUsername())) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
return userRepository.save(user);
} else {
throw new RessourceAlreadyExistsException(user.getUsername());
}
throw new InvalidParameterException(new String[]{"User"});
}
#Override
#RolesAllowed(Role.ROLE_USER)
public User update(#NotNull User user) throws InvalidParameterException {
if (UserValidator.isFullValid(user) && userRepository.existsByUsername(user.getUsername())) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
return userRepository.save(user);
}
throw new InvalidParameterException(new String[]{"User"});
}
#Override
#RolesAllowed(value = Role.ROLE_ADMIN)
public void delete(#NotNull User user) throws IllegalArgumentException {
userRepository.delete(user);
}
#RolesAllowed(value = Role.ROLE_ADMIN)
public void delete(#NotNull String username) {
userRepository.deleteByUsername(username);
}
#RolesAllowed(value = Role.ROLE_USER)
private User getLoggedInUser() throws InvalidStateException {
Authentication currentUser = SecurityContextHolder.getContext().getAuthentication();
String username = currentUser.getName();
final Optional<User> userValue = getByUsername(username);
if (userValue.isPresent())
return userValue.get();
throw new InvalidStateException("User " + username + " not found");
}
}
If you like to I could provide more Information of Configuration but at the Moment I don't know if this is necessary. But if you believe it is I will.

Error 403 when I submit the register form in spring boot project

I am learning spring boot and write a register form, but when I run it in idea and submit the form, the browser occurs
There was an unexpected error (type=Forbidden, status=403).
Forbidden
I create the project by using spring initializr in idea, choose web+jpa+h2+thymeleaf.
I defined an Entity called Worker and set error messages in ValidationMessages.properties, here is the Worker entity
#Entity
public class Worker implements UserDetails {
private static final long serialversionUID = 1L;
#Id
#NotNull
#Size(min = 5, max = 16, message = "{username.size}")
private String username;
#NotNull
#Size(min = 2, max = 30, message = "{firstName.size}")
private String firstname;
#NotNull
#Size(min = 2, max = 30, message = "{lastName.size")
private String lastname;
#NotNull
#Size(min = 5, max = 25,message = "{password.size}")
private String password;
#NotNull
#Size(min = 2, max = 30, message = "{profession,size}")
private String profession;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getProfession() {
return profession;
}
public void setProfession(String profession) {
this.profession = profession;
}
//UserDetails methods
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority("WORKER"));
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
and WorkersRepository
public interface WorkersRepository extends JpaRepository<Worker, String> {
Worker findByUsername(String username);
}
I have added spring security, and wrote the config:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private WorkersRepository workersRepository;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/submit").access("hasRole('WORKER')")
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login")
.and()
.logout()
.logoutSuccessUrl("/")
.and()
.rememberMe()
.tokenValiditySeconds(4838400)
.key("workerKey");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(new UserDetailsService() {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return workersRepository.findByUsername(username);
}
});
}
}
If the register input occurs error, the controller returns the registerForm.html to ask user to input again correctly. If the register has no error, the controller redirects to "/", a simple welcome.html. But whether the input is correct or not, I always get the error 403. When I inputhttp://localhost:8080/, I can get the welcome.html,which is a simple page with "welcome!" words. My controller is
private WorkersRepository workersRepository;
#Autowired
public WorkingHoursController(
WorkersRepository workersRepository) {
this.workersRepository = workersRepository;
}
#RequestMapping(method = RequestMethod.GET)
public String welcomePage() {
return "welcome";
}
#RequestMapping(value = "/register", method = RequestMethod.GET)
public String showRegistrationForm(Model model) {
model.addAttribute(new Worker());
return "registerForm";
}
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String registrationProcessing(#Valid Worker worker, Errors errors, RedirectAttributes model) {
if(errors.hasErrors()) {
return "registerForm";
}
workersRepository.save(worker);
model.addAttribute("username", worker.getUsername());
model.addFlashAttribute("worker", worker);
return "redirect:/";
}
...
I wrote the registerForm.html using thymeleaf and add error validations. My registerForm.html is
<form class="form-signin" method="post" th:object="${worker}">
<div class="errors" th:if="${#fields.hasErrors('*')}">
<ul>
<li th:each="err : ${#fields.errors('*')}"
th:text="${err}">Input is in correct.</li>
</ul>
</div>
<img class="mb-4" src="https://getbootstrap.com/assets/brand/bootstrap-solid.svg" alt="" width="72" height="72">
<h1 class="h3 mb-3 font-weight-normal">Please register</h1>
<!-- input username -->
<label for="inputUsername" th:class="${#fields.hasErrors('username')}? 'error'">Username</label>
<input type="text" id="inputUsername" th:field="*{username}" th:class="${#fields.hasErrors('username')}? 'error form-control':'form-control'" placeholder="Username">
...
<!-- input password -->
<label for="inputPassword" th:class="${#fields.hasErrors('password')}? 'error'">Password</label>
<input type="password" id="inputPassword" th:field="*{password}" th:class="${#fields.hasErrors('password')}? 'error form-control':'form-control'" placeholder="Password">
<div class="checkbox mb-3">
<label>
<input type="checkbox" id="remember-me" name="remember-me"> Remember me
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">Register</button>
Before I add validations in thymeleaf and add spring security, everything seems to work properly.
You did not put any action inside form tag. Perhaps that's why you are getting error. Put action inside form tag like this one
<form class="form-signin" action="#" th:action="#{/register}" method="post" th:object="${worker}">
Please check once whether role should be "WORKER" or "ROLE_WORKER" according to your Spring Security JAR version. Also disable the CSRF in your application, and set global CORS config to accept all requests.
add the following to your config class
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
}
This is security stuff, it prevents hackers from using your open account credentials without your permission i.e. you get an email that say click here (that click takes your user info to another site without your consent). only use the above for dev.
For pro; add a csrf token as a hidden input on your form. this token is in your spring server.
<input type="hidden" name="_csrf" th:value="${_csrf.token}"/>

Resources