UserPass Authentication Vault - spring-boot

I have been trying to use the authentication method from Hashicorp Vault (from here) in my application to get configurations.
But not able to get any information regarding this authentication type in Spring's docs, examples, etc. Can you please help me out as I need this type of authentication to help me with vault in multiuser environment.

Here is my solution:
Configuration class:
package com.company.myapp.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.vault.VaultException;
import org.springframework.vault.authentication.ClientAuthentication;
import org.springframework.vault.support.VaultToken;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;
#Configuration
public class VaultConfig {
public static final String LOGIN_PATH = "/v1/auth/userpass/login/";
#Bean
public ClientAuthentication clientAuthentication(#Value("${VAULT_USERNAME}") String username,
#Value("${VAULT_PASSWORD}") String password,
#Value("${spring.cloud.vault.uri}") String host) {
return new UserPassAuthentication(host, LOGIN_PATH, username, password);
}
public static class UserPassAuthentication implements ClientAuthentication {
private RestOperations restOperations = new RestTemplate();
private String url;
private String password;
public UserPassAuthentication(String host, String path, String user, String password) {
this.url = new StringBuilder(host).append(path).append(user).toString();
this.password = password;
}
#Override
public VaultToken login() throws VaultException {
return VaultToken.of(
((Map<String, String>) restOperations.postForEntity(url, new Password(password), Map.class)
.getBody().get("auth")).get("client_token"));
}
}
static class Password {
private String password;
public Password(String password) {
this.password = password;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
resources/bootstrap.properties:
spring.profiles.active=dev
spring.application.name=myapp
spring.cloud.vault.kv.enabled=true
spring.cloud.vault.kv.backend=test-backend
spring.cloud.vault.uri=https://localhost:8200
VAULT_USERNAME=usr
VAULT_PASSWORD=pwd
resources/META-INF/spring.factories
org.springframework.cloud.bootstrap.BootstrapConfiguration=com.company.myapp.config.VaultConfig

Using this solution I had 2 problems.
overriding spring preconfigured beans.
solution: add
spring.main.allow-bean-definition-overriding =true
in your bootstrap.properties file. this allows bean definition overriding.
beanInstantiationException: Because authentication steps were not defined properly.
solution: I used the token authentication method as follows:
public ClientAuthentication clientAuthentication() {
return new TokenAuthentication(new UserPassAuthentication("host", path, "user", "password").login());
}
and the rest of the code exactly as shown before.

Related

when I post ,it shows error of 404 not found in spring boot even when I have declared #RequestMapping

I have made a userservice where I want to add new user, find user by id and also by other attributes. I have extended JPA repository since I learned that there is already functions. But I can't even save a new user.
This is my model
package bt.gov.dit.userservice.model;
import javax.persistence.*;
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private Long Id;
#Column
private String name;
#Column
private String email;
#Column
private String role;
public Long getId() {
return Id;
}
public void setId(Long id) {
Id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public User(Long id, String name, String email, String role) {
Id = id;
this.name = name;
this.email = email;
this.role = role;
}
public User() {
}
}
This is my repository
package bt.gov.dit.userservice.dao;
import bt.gov.dit.userservice.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface UserRepository extends JpaRepository<User,Long> {
User findUserByRole(String role);
}
This is my service
package bt.gov.dit.userservice.service;
import bt.gov.dit.userservice.dao.UserRepository;
import bt.gov.dit.userservice.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class UserService {
#Autowired
private UserRepository userRepository;
public User save(User user){
return userRepository.save(user);
}
public User getByRole(String role){
return userRepository.findUserByRole(role);
}
}
And this is my Controller
package bt.gov.dit.userservice.controller;
import bt.gov.dit.userservice.model.User;
//import bt.gov.dit.userservice.service.UserService;
import bt.gov.dit.userservice.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
#RestController
#RequestMapping("/api")
public class UserController {
#Autowired
private UserService userService;
#PostMapping
public ResponseEntity<User> create(#Valid #RequestBody User user) {
User updated = userService.create(user);
return new ResponseEntity<User>(updated, new HttpHeaders(), HttpStatus.OK);
}
#GetMapping("/{role_id}")
public ResponseEntity<User> getUserByRole(#PathVariable("role_id") String role)
{
User entity = userService.getByRole(role);
return new ResponseEntity<User>(entity, new HttpHeaders(), HttpStatus.OK);
}
}
I have just started learning Spring boot and I can't seem to understand what is wrong in code. I want to insert/save new user. But when I call /api/user it says 404 not found. I am using h2 in-memory db.
I think the issue is the controller method argument name - role->role_id
#GetMapping("/{role_id}")
public ResponseEntity<User> getUserByRole(#PathVariable("role_id") String role_id)
{
User entity = userService.getByRole(role_id);
return new ResponseEntity<User>(entity, new HttpHeaders(), HttpStatus.OK);
}
the role_id should be same as you mentioned in the routes.
Route example->localhost:8080/api/2
you need to map the other part of the request like this:
#PostMapping(value="/user", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE )
I also recommend you to use Springfox Swagger UI using the annotation #EnableSwagger2
This will provide you an user interface with all requests at http://localhost:8080/swagger-ui.html
Link:
https://www.baeldung.com/swagger-2-documentation-for-spring-rest-api

spring-boot error org.springframework.security.core.userdetails.User cannot be cast to in.cad.security.model.MyUserPrincipal class

I am completely new to spring boot and trying to write Unit test cases but stuck completely and not able to understand how Authentication works.
Controller class
#PostMapping (path = "/createConcept")
#ApiOperation(value = "Composite object with conceptCO with added roles",
response = ConceptCO.class)
public ConceptCO createConcept(
#RequestBody final ConceptFormVO conceptFormVO,
#ApiIgnore Authentication authentication
) {
ConceptDTO conceptDTO = new ConceptDTO();
BeanUtils.copyProperties(conceptFormVO, conceptDTO,
AppUtils.getNullPropertyNames(conceptFormVO));
LOGGER.info("Input Config : ::{}", conceptFormVO);
List<UserPrincipalAttributes> cadenzPrincipalAttributesList = PrincipalUtil.getRoles(authentication);
String token = PrincipalUtil.getToken(authentication);
return conceptDelegate.createConcept(conceptDTO,cadenzPrincipalAttributesList,token);
}
PrincipalUtil.java
public final class PrincipalUtil {
public static final String BEARER_TOKEN = "Bearer ";
public static List<UserPrincipalAttributes> getRoles(final Authentication authentication) {
UserPrincipal user =
(UserPrincipal) authentication.getPrincipal();
return new ArrayList<>(user.getUserPrincipalAttributes());
}
public static String getToken(final Authentication authentication) {
UserPrincipal user =
(UserPrincipal) authentication.getPrincipal();
String finalToken = BEARER_TOKEN + user.getToken();
return finalToken;
}
}
UserPrincipal.java
public class UserPrincipal implements AuthenticatedPrincipal {
private String name;
private Set<UserPrincipalAttributes> userPrincipalAttributes;
private String token;
// getter & setters
}
UserPrincipalAttributes .java
public class UserPrincipalAttributes {
Set<String> columns;
Set<String> concepts;
String role;
// getter & setters
}
Below is my test function
private Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
private static final String BASE_URL = "/xyz";
#Before
public void setup() throws Exception {
mvc = MockMvcBuilders
.webAppContextSetup(this.wac)
.apply(SecurityMockMvcConfigurers.springSecurity())
.build();
}
#Test
#WithMockUser(username = "test_user1")
public void createConceptTest() throws Exception {
ConceptFormVO conceptFormVO = new ConceptFormVO();
conceptFormVO.setConceptExpression("payment_cat = ABC");
conceptFormVO.setConceptName("all_subsc_test");
RequestBuilder createConceptRequest = post(BASE_URL + "/createConcept",authentication)
.header("Authorization",
String.format("Bearer %s", accessToken))
.content(objectMapper.writeValueAsString(conceptFormVO))
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON);
this.mvc
.perform(createConceptRequest)
.andExpect(status().isOk())
}
Running above test case gives me error
java.lang.ClassCastException: org.springframework.security.core.userdetails.User cannot be cast to in.at.security.model.UserPrincipal
at in.at.security.util.PrincipalUtil.getRoles(PrincipalUtil.java)
Apologies for silly mistakes.
instead of passing Authentication u can directly inject AuthenticatedPrincipal refer below code let me know if it works,
import java.util.Collection;
import java.util.Map;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
public class SampleController {
#PostMapping (path = "/createConcept")
public SampleController createConcept(
#RequestBody final ConceptFormVO conceptFormVO,
#AuthenticationPrincipal OAuth2User principal
) {
Map<String, Object> principalDetails = principal.getAttributes();
Collection<? extends GrantedAuthority> authorities = principal.getAuthorities();
.....
}
}

How do I use Bcrypt in my spring boot application to secure passwords?

My username and password is coming from angular to spring boot which stores it in mysql. I have simple model, repository, services and controller packages. My model is registration which has name username and password and while loggin in, the username and password is fetched from the registration table
My Registration Model Class
package com.example.angular.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="registration")
public class Registration {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
private String name;
private String username;
private String password;
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public Registration(String name, String username, String password) {
super();
this.name = name;
this.username = username;
this.password = password;
}
public Registration() {
super();
// TODO Auto-generated constructor stub
}
#Override
public String toString() {
return "Registration [id=" + id + ", name=" + name + ", username=" + username + ", password=" + password + "]";
}
}
My registration controller
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.angular.model.Registration;
import com.example.angular.service.RegistrationService;
#RestController
#CrossOrigin(origins="*", allowedHeaders = "*")
#RequestMapping("/register")
public class RegistrationController {
#Autowired
private RegistrationService res;
#PostMapping("/registeruser")
public ResponseEntity<Registration> registeruser(#RequestBody Registration reg)
{
Registration resk= res.registeruser(reg);
return new ResponseEntity<Registration>(resk,HttpStatus.OK);
}
#PostMapping("/login")
public ResponseEntity<Registration> loginuser(#RequestBody Registration reg)
{
List<Registration> regList = res.getusername(reg.getUsername(), reg.getPassword());
System.out.println("Logged in! ");
//return new ResponseEntity<Registration>(reg.getUsername(), HttpStatus.OK);
return null;
}
}
do I have to add any configuartion file in a package or do I have to use bcrypt in angular? Youtube videos are confusing please help
I think you want Spring Security. In this case you should use BCryptPasswordEncoder. Simply create Bean for encryption.
private static final String ADMIN = "ADMIN";
private static final String USER = "USER";
#Autowired
private UserDetailService userDetailService;
#Autowired
private DataSource dataSource;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).dataSource(dataSource)
.passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/admin").hasRole(ADMIN)
.antMatchers("/user").hasAnyRole(ADMIN, USER)
.antMatchers("/", "/register-user").permitAll()
.and().formLogin();
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
If you just want to encrypt the password in BCrypt. You can use like this
String password = "password";
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String hashedPassword = passwordEncoder.encode(password);

Roles not working properly in Spring security [duplicate]

This question already has answers here:
Springboot Security hasRole not working
(3 answers)
Closed 3 years ago.
I am working on a spring security based web application in which I want to limit the items on the side bar based upon whether the logged in user has role ADMIN or USER. As far as authentication is concerned, everything is working fine but roles are not working as expected.
Following this post
For example -
<security:authorize access="hasRole('ADMIN')">
<li class=" nav-item"><a href="<c:url value = "/mapview/list"/>"><i
class="fa fa-map-marker"></i><span class="menu-title" data-i18n="">MapView</span></a>
</li>
</security:authorize>
The above element never gets visible even though I log in as - ADMIN.
Can someone please help me here in understanding what is going wrong.
Security Config
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.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;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Bean
public PasswordEncoder passwordEncoder() {
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
#Bean
public AuthenticationProvider authProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(new BCryptPasswordEncoder());
return provider;
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeRequests().antMatchers("/login", "/resource/**").permitAll()
.anyRequest().fullyAuthenticated()
.and()
.formLogin()
.loginPage("/login").usernameParameter("username").passwordParameter("password").permitAll()
.loginProcessingUrl("/doLogin").successForwardUrl("/postLogin").failureUrl("/loginFailed").and()
.logout().logoutUrl("/doLogout").logoutSuccessUrl("/logout").permitAll().and().csrf().disable();
}
}
UserDetailsImpl
public class UserDetailsImpl implements UserDetails {
private static final long serialVersionUID = 1L;
private Collection<SimpleGrantedAuthority> authorities;
private String username;
private String password;
private Boolean enabled = true;
public void setAuthorities(Collection<SimpleGrantedAuthority> authorities) {
this.authorities = authorities;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
#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 enabled;
}
UserDetailsService
#Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserService userService;
private Converter<User, UserDetails> userUserDetailsConverter;
#Autowired
#Qualifier(value = "userToUserDetails")
public void setUserUserDetailsConverter(Converter<User, UserDetails> userUserDetailsConverter) {
this.userUserDetailsConverter = userUserDetailsConverter;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userUserDetailsConverter.convert(userService.findByUserName(username));
}
}
UserToUserDetails
#Component
public class UserToUserDetails implements Converter<User, UserDetails>{
#Override
public UserDetails convert(User user) {
UserDetailsImpl userDetails = new UserDetailsImpl();
if (user != null) {
userDetails.setUsername(user.getUsername());
userDetails.setPassword(user.getEncryptedPassword());
userDetails.setEnabled(user.getEnabled());
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
user.getRoles().forEach(role -> {
authorities.add(new SimpleGrantedAuthority(role.getName()));
});
userDetails.setAuthorities(authorities);
}
return userDetails;
}
}
Controller
#SessionAttributes({ "currentUser" })
#Controller
public class HomeController {
//skipping other mappings
#RequestMapping(value = "/postLogin", method = RequestMethod.POST)
public String postLogin(Model model, HttpSession session) {
UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder
.getContext().getAuthentication();
validatePrinciple(authentication.getPrincipal());
String username = ((UserDetailsImpl) authentication.getPrincipal()).getUsername();
model.addAttribute("currentUser", username);
return "redirect:/dashboard";
}
}
User
#Entity
public class User extends Auditable<String> {
//skipping other details
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "user_role",
joinColumns = { #JoinColumn(name = "user_id") },
inverseJoinColumns = { #JoinColumn(name = "role_id") })
private Set<Role> roles = new HashSet<>();
}
Tables created in db-
user
role
user_role
EDIT 1
Just added this mapping inside my controller and Once I login, if i hit /test on browser, both the booleans show false. I have no idea why the roles are not being set.. :-(
#GetMapping(value = "/test")
public void test(SecurityContextHolderAwareRequestWrapper request) {
boolean b = request.isUserInRole("ADMIN");
System.out.println("ROLE_ADMIN=" + b);
boolean c = request.isUserInRole("USER");
System.out.println("ROLE_USER=" + c);
}
EDIT 2
But, at the same time, below code shows role as ADMIN
public void test(SecurityContextHolderAwareRequestWrapper request) {
for (GrantedAuthority authority : SecurityContextHolder.getContext().getAuthentication().getAuthorities()) {
String userRole = authority.getAuthority();
System.out.println(userRole);
}
}
This looks like information is getting lost somewhere between retrieval from database and returning a response to the browser (obviously). Good thing you have a debugger, so I would start by tracing each step from where you access your database data, until you return that information. Follow every single step until you find where is it being lost, and post back when you have narrowed it down to a single place. If not maybe we can start looking at your html/script/template engine, but only after we are sure info is reaching browser.

Authentication issue in Spring Security (checking only username not password?)

this is my first project with Spring and I have just started to create the login with Spring Security. I want some pages to be accessible only for the admin and not for the players. I've found some examples on the web and this mechanism works pretty well, I have this secured page that is protected by the login and it's forbidden when the user has no ROLE_ADMIN.
#PreAuthorize("hasAuthority('ROLE_ADMIN')")
#GetMapping("/secured/all")
public String securedHello() {
return "Secured Hello";
}
The problem is that testing my code I found out that Spring authenticates the admin (and the user as well) only checking the username. If I put the wrong password it allows me to enter anyway. I don't understand how this is possible, shouldn't Spring Security do all the authentication work by itself? I've seen somebody suggested to implement an authentication manager or something like that, but I don't understand why and how to insert it in my code. I'm stuck on this since two days, please any advice wuold be really appreciated.
These are my classes:
package model;
import java.io.IOException;
import javax.naming.AuthenticationException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.http.HttpStatus;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.fasterxml.jackson.databind.ObjectMapper;
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
#EnableJpaRepositories(basePackageClasses = PlayersRepository.class)
#ComponentScan(basePackageClasses= CustomUserDetailsService.class)
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(getPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
//http.csrf().disable();
http.authorizeRequests()
.antMatchers("**/secured/**").access("hasAuthority('ROLE_ADMIN')")
.anyRequest().permitAll()
.and()
.formLogin().permitAll();
}
private PasswordEncoder getPasswordEncoder() {
return new PasswordEncoder() {
#Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
#Override
public boolean matches(CharSequence charSequence, String s) {
return true;
}
};
}
}
package model;
import java.util.ArrayList;
import java.util.Optional;
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
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private PlayersRepository usersRepository;
#Autowired
private RoleRepository rolesRepository;
public CustomUserDetailsService(PlayersRepository usersRepository, RoleRepository rolesRepository) {
this.usersRepository=usersRepository;
this.rolesRepository=rolesRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<Player> optionalUser = usersRepository.findByUsername(username);
optionalUser
.orElseThrow(() -> new UsernameNotFoundException("Username not found"));
Player user= optionalUser.get();
System.out.println(user);
return toUserDetails(new UserObject(user.getUsername(),user.getPassword(),user.getRole()));
}
private UserDetails toUserDetails(UserObject userObject) {
return User.withUsername(userObject.name)
.password(userObject.password)
.roles(userObject.role).build();
}
private static class UserObject {
private String name;
private String password;
private String role;
public UserObject(String name, String password, String role) {
this.name = name;
this.password = password;
this.role = role;
}
}
}
package model;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class CustomUserDetails extends Player implements UserDetails {
String role;
public CustomUserDetails(final Player user) {
super(user);
}
public CustomUserDetails(Optional<Player> user, String role) {
super(user);
this.role=role;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
list.add(new SimpleGrantedAuthority("ROLE_"+ role));
System.out.println(list);
return list;
}
#Override
public String getPassword() {
return super.getPassword();
}
#Override
public String getUsername() {
return super.getUsername();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
Shouldn't Spring Security do all the authentication work by itself?
Yes, Spring Security does that for you using an AuthenticationManager.
I've seen somebody suggested to implement an authentication manager or something like that, but I don't understand why and how to insert it in my code.
You actually already have an AuthenticationManager, since you built one within the configure() method:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(getPasswordEncoder());
}
So, what is exactly the reason this isn't working you may ask. Well, the AuthenticationManager you provided contains two parts:
A part that fetches the user information (CustomUserDetailsService)
Another part that checks the password (the getPasswordEncoder()).
What happens behind the screens is that Spring calls your CustomUserDetailsService to fetch your user information, including your (hashed) password. After fetching that information, it calls your PasswordEncoder.matches() function to verify if the raw entered password matches your hashed password provided by the CustomUserDetailsService.
In your case, your PasswordEncoder.matches() function looks like this:
#Override
public boolean matches(CharSequence charSequence, String s) {
return true;
}
This means that regardless of what password you provide, it will return true. This is exactly what you're experiencing since any password will work.
So, how do you solve this? Well, your PasswordEncoder should actually hash your raw password and compare it to the hashed password that is being passed, for example:
#Override
public boolean matches(CharSequence rawPassword, String hashedPassword) {
String hashedPassword2 = null; // hash your rawPassword here
return hashedPassword2.equals(hashedPassword);
}
The implementation of this method depends on how you store your password in your database. Spring Security already comes with a few implementation including BcryptPasswordEncoder, StandardPasswordEncoder, MessageDigestPasswordEncoder, ... . Some of these implementations are deprecated, mostly to indicate that the hashing mechanisms used by those encoders are considered unsafe. There are no plans at the moment of writing to remove those encoders, as mentioned by the Javadoc:
Digest based password encoding is not considered secure. Instead use an adaptive one way function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or SCryptPasswordEncoder. Even better use DelegatingPasswordEncoder which supports password upgrades. There are no plans to remove this support. It is deprecated to indicate that this is a legacy implementation and using it is considered insecure.
(Emphasis is my own)
If you are free to choose which implementation you pick, then Spring recommends using BCryptPasswordEncoder as mentioned by the Javadoc:
Service interface for encoding passwords. The preferred implementation is BCryptPasswordEncoder.
I just had a Quick scan I found this
private PasswordEncoder getPasswordEncoder() {
return new PasswordEncoder() {
#Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
#Override
public boolean matches(CharSequence charSequence, String s) {
return true;
}
};
}
In your matches you are returning always true.
I guess here you should put logic for checking password for equality something like this
#Override
public boolean matches(CharSequence charSequence, String s) {
return charSequence.toString.equals(s);
}
I would suggest you use something like this
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

Resources