I'm trying to add bcrypt to a spring app of mine. Authentication works just fine without. But when I try to encode using bcrypt I get "Reason: Bad credentials" when trying to login.
My user model looks as follows.
#Entity
#Table(name="users") // user is a reserved word in postgresql
public class User extends BaseEntity {
private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
...
#Column(nullable=false)
private String password;
...
public String getPassword() {
return password;
}
public void setPassword(String password) {
String hashedPassword = passwordEncoder.encode(password);
this.password = hashedPassword;
}
...
}
My SecurityConfig looks as follows.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
private BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
...
}
Do the above seem right? Do I need to do more than what I've already done?
My bad for not posting enough code. Naturally my user model didn't tell the entire story. I also have a class called SecurityUser which I've posted below. Due to the copy constructor the password gets hashed twice.
public class SecurityUser extends User implements UserDetails {
private static final long serialVersionUID = 867280338717707274L;
public SecurityUser(User user) {
if(user != null)
{
this.setId(user.getId());
this.setName(user.getName());
this.setEmail(user.getEmail());
this.setPassword(user.getPassword());
this.setRoles(user.getRoles());
}
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
Set<Role> userRoles = this.getRoles();
if(userRoles != null)
{
for (Role role : userRoles) {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.getName());
authorities.add(authority);
}
}
return authorities;
}
...
}
I've made my passwordEncoder method public and promoted it to a bean so I can autowire it into my UserService which is shown below. That way I only have to change encoder in one place if I ever decide to do so.
#Service
public class UserService {
#Autowired
private UserRepository userRepository;
#Autowired
private PasswordEncoder passwordEncoder;
public User create(User user) {
String hashedPassword = passwordEncoder.encode(user.getPassword());
user.setPassword(hashedPassword);
return userRepository.save(user);
}
...
}
Here is how I would set it up.
User Table has 4 properties (amongst others)
id (auto increment)
username (or email_address) field
password field.
enabled (value will be either 1 or 0)
Role table (3 properties)
id (auto increment)
user_id (user table foreign key)
authority;
Create Java Entities for the two tables.
Spring Security Configuration Class looks like:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
String usrsByUnameQry = "SELECT u.email_address, u.password, u.enabled FROM user u WHERE u.email_address=?";
3String authByUnameQry = "SELECT u.email_address, r.authority FROM user u, role r WHERE u.id=r.user_id AND u.email_address=?";
auth
.eraseCredentials(false)
.jdbcAuthentication()
.dataSource(dataSource)
.passwordEncoder(passwordEncoder())
.usersByUsernameQuery(usrsByUnameQry)
.authoritiesByUsernameQuery(authByUnameQry);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.usernameParameter("username") //username property defined in html form
.passwordParameter("password") //password property defined in html form
// url that holds login form
.loginPage("/signin")
.loginProcessingUrl("/signin/authenticate")
.failureUrl("/loginfail")
// Grant access to users to the login page
.permitAll()
.and()
.logout()
.logoutUrl("/signout")
.deleteCookies("JSESSIONID")
.logoutSuccessUrl("/signin")
.and()
.authorizeRequests()
.antMatchers("/foo/**").permitAll()//Grant access to all (no auth reuired)
.antMatchers("/").hasAnyAuthority("ROLE_USER","ROLE_ADMIN") //Grant access to only users with role "ROLE_USER" or "ROLE_ADMIN"
}
#Bean(name = "authenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
#Bean
public TextEncryptor textEncryptor(){
return Encryptors.noOpText();
}
Related
So I am making my first spring boot project (I am making a RESTful backend API) and I came across jwt authentication. After trying a few tutorials I was keep getting stuck until one tutorial kinda helped me.
My problem:
When I run my AuthenticationController:
#RestController
#CrossOrigin
public class JwtAuthenticationController {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Autowired
private JwtUserDetailsService userDetailsService;
#RequestMapping(value = "/authenticate", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(#RequestBody JwtRequest authenticationRequest) throws Exception {
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
);} catch (BadCredentialsException e){
throw new Exception("Incorrect username or password", e);
}
final UserDetails userDetails = userDetailsService
.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtResponse(token));
}
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ResponseEntity<?> saveUser(#RequestBody UserDTO user) throws Exception {
return ResponseEntity.ok(userDetailsService.save(user));
}
}
My code does not run past this piece of code:
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
);} catch (BadCredentialsException e){
throw new Exception("Incorrect username or password", e);
}
I know this because of debugging basically. It always goes to the catch and I don't know why.
When I comment out that not working piece of code, my code runs but I can use any username and password with the given jwt Token, and that is not what I want.
So I know the piece of code that makes sure my program does not run as planned is that piece.
I know the not working piece of code uses my JwtRequest so maybe anyone needs that to help me fix my problem:
JwtRequest:
package com.example.demo.Model;
public class JwtRequest{
private String username;
private String password;
public JwtRequest() {
}
public JwtRequest(String username, String password) {
this.username = username;
this.password = password;
}
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;
}
}
Here is my debugger and database to confirm that the credentials are correct:
My debugger:
My DataBase:
My Postman:
What I have tried:
I have tried replacing this code:
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
);} catch (BadCredentialsException e){
throw new Exception("Incorrect username or password", e);
}
With this:
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
And my according authenticate method:
private void authenticate(String username, String password) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
throw new Exception("USER_DISABLED", e);
} catch (BadCredentialsException e) {
throw new Exception("INVALID_CREDENTIALS", e);
}
}
But then my debugger says this:
I am basically stuck and I do not know what to do now. Can anyone help me or provide me with a tutorial on how to implement JWT authentication with an MYSQL database that is not outdated and has the source code included in the tutorial?
Thanks!
If you need some piece of code just ask and I will include it in the post.
As requested the following files:
WebSecurityConfig:
#Configuration
#AllArgsConstructor
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {//provides security for endpoints
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
private final AccountService accountService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()//So we can send post requests without being rejected(if we using form based indication we want to enable this)
.authorizeRequests()
.antMatchers("/authenticate","/register")
.permitAll()//any request that goes trough that end point we want to allow.
.anyRequest()
.authenticated().and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider =
new DaoAuthenticationProvider();
provider.setPasswordEncoder(bCryptPasswordEncoder);
provider.setUserDetailsService(accountService);
return provider;
}
}
Password encoder:
#Configuration
public class PasswordEncoder{
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
}
UserdetailsService:
#Service
public class JwtUserDetailsService implements UserDetailsService {
#Autowired
private UserDao userDao;
#Autowired
private PasswordEncoder bcryptEncoder;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
DAOUser user = userDao.findByUsername(username);
if (user == null){
throw new UsernameNotFoundException("User not found with username: " + username);
}
return new User(user.getUsername(), user.getPassword(), new ArrayList<>());
}
public DAOUser save(UserDTO user) {
DAOUser newUser = new DAOUser();
newUser.setUsername(user.getUsername());
newUser.setPassword(bcryptEncoder.bCryptPasswordEncoder().encode(user.getPassword()));
return userDao.save(newUser);
}
}
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider =
new DaoAuthenticationProvider();
provider.setPasswordEncoder(bCryptPasswordEncoder);
provider.setUserDetailsService(accountService);
return provider;
}
You are setting accountService as userDetailsService instead of `JwtUserDetailsService'. have you tried that?
change in JwtUserDetailsService.java
//not sure why accountService-> don't make it final
//#Autowired //missing
//private AccountService accountService;
#Autowired
private PasswordEncoder bcryptEncoder;
#Configuration
#AllArgsConstructor
//#EnableWebSecurity not needed
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {//provides security for endpoints
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private JwtUserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
//private AccountService accountService;
#Autowired
private PasswordEncoder bCryptPasswordEncoder;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()//So we can send post requests without being rejected(if we using form based indication we want to enable this)
.authorizeRequests()
.antMatchers("/authenticate","/register")
.permitAll()//any request that goes trough that end point we want to allow.
.anyRequest()
.authenticated().and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
//.and().addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); You are not using filter to authenticate, you are using a controller for that
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider =
new DaoAuthenticationProvider();
provider.setPasswordEncoder(bCryptPasswordEncoder);
//provider.setUserDetailsService(accountService);
return provider;
}
}
I am creating an application using Role based restriction to certain REST endpoints.
I have a Roles table, user_roles table and a users table.
ROLE_USER is working fine, and users with that Role can successfully access their pages. Even if I swap it around so that only ROLE_USER can access /admin/**, it works.
However, when I try use ROLE_ADMIN on the admin page, it never works. It always redirects me to the 403 Forbidden page
Security Config:
#Bean
DaoAuthenticationProvider provider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsServiceImpl);
provider.setPasswordEncoder(passwordEncoder);
return provider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(provider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/banking/**").hasAnyRole("USER", "ADMIN")
.antMatchers(HttpMethod.GET, "/admin/**").hasRole("ADMIN")
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.logout().permitAll()
.and()
.exceptionHandling().accessDeniedPage("/403");
}
}
MyUserDetails:
public class MyUserDetails implements UserDetails {
private static final long serialVersionUID = -2067381432706760538L;
private User user;
public MyUserDetails(User user) {
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<Role> roles = user.getRoles();
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (Role role : roles) {
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));
}
return authorities;
}
UserDetailsServiceImpl:
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserService userService;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.findByUsername(username);
if(user == null) {
throw new UsernameNotFoundException("User not found");
}
return new MyUserDetails(user);
}
}
Would appreciate any help as I've been stuck on this for a while. Thanks!
Used Spring Boot 2 + Spring Security Starter.
Authorizes users, but for some reason gives an error 403.
I tried to configure in different ways, but it does not work.
After successful authorization (the loadUserByUsername method works fine) it shows 403 on all pages with the / admin prefix, and before authorization, switching to any page with this prefix leads to a redirect to / login
#Controller
public class AdminController {
#RequestMapping(value = "/admin", method = {GET, POST})
public String adminMainPage() {
return "redirect:/admin/article";
}
}
#Controller
#RequestMapping("/admin/article")
public class ArticleController {
#RequestMapping(value = "", method = {GET, POST})
public ModelAndView indexAdminPage(...){
...
}
}
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements UserDetailsService {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.userDetailsService(this)
.authorizeRequests()
.antMatchers("/", "/login",
"/login*", "/assets/**", "/lib/**", "/page.scripts/*").permitAll()
.antMatchers("/admin/**").hasAnyRole("ADMIN")
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.usernameParameter("login")
.passwordParameter("password")
.successForwardUrl("/admin")
.permitAll()
.and()
.logout()
.deleteCookies("JSESSIONID")
.permitAll();
}
private Collection<? extends GrantedAuthority> adminGrantedAuthoritySet = new HashSet<>() {{
add(new SimpleGrantedAuthority("ADMIN"));
}};
private final UserRepository userRepository;
public WebSecurityConfig(UserRepository userRepository ) {
this.userRepository = userRepository;
}
#Override
public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {
Optional<UserEntity> optionalUser = userRepository.findByLogin(login);
if (optionalUser.isEmpty()) {
throw new UsernameNotFoundException("User by login '" + login + "' not found");
} else {
UserEntity userEntity = optionalUser.get();
return new User(login, userEntity.getPassword(), adminGrantedAuthoritySet);
}
}
}
In Spring Security there is a distinction between a role and an authority.
A role is an authority that is prefixed with "ROLE_". In this example the authority "ROLE_ADMIN" is the same as the role "ADMIN".
You are setting your admin authorities to be a list of new SimpleGrantedAuthority("ADMIN"), but you are restricting access to .hasAnyRole("ADMIN").
You need to change one of those configurations.
If you use .hasAnyRole("ADMIN"), then you should change the admin authorities list to use new SimpleGrantedAuthority("ROLE_ADMIN").
Otherwise, if you want your list to be new SimpleGrantedAuthority("ADMIN"), then you should use .hasAnyAuthority("ADMIN").
First, I will advice that you separate UserDetailsService from the WebSecurityConfig.
Have a separate class for UserDetailsService like
#Service("customCustomerDetailsService")
public class CustomCustomerDetailsService implements UserDetailsService {
#Autowired
private CustomerRepository customers;
#Override
public UserDetails loadUserByUsername(String email) {
return this.customers.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException("Username: " + email + " not found"));
}
}
Then your UserEntity should implement UserDetails class where you set the authorities.See the answer //userdetails
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.roles.stream().map(SimpleGrantedAuthority::new).collect(toList());
}
#Override
public String getUsername() {
return this.getEmail();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
#Transient
private List<String> roles = Arrays.asList("ROLE_USER");
public List<String> getRoles() {
return roles;
}
Then you need DAOauthentication manager which makes use of the UserDetailsService like this:
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
#Bean
#Override
public UserDetailsService userDetailsService() {
return new CustomCustomerDetailsService();
}
I don't know think putting everything in the WebSecurityConfig is good practice and it will be complicated and prone to errors!
I'm new to spring and I'm trying to implement spring security in a project. I was able to create user with hash password using Bcrypt, but whenever I tried to login using the same password it fails. I've also tried checking other SO answers (like Spring Security BCryptPasswordEncoder Inserted But Not Match) non solved the issues faced.
Below is what have tried so far
WebSecurityConfigurerAdapter Class
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsServiceImpl userDetailsService;
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
http.csrf().disable();
http.authorizeRequests().antMatchers("/login", "/app-assets/**", "/assets/**").permitAll();
http.authorizeRequests().antMatchers("/add-user", "/users-list").hasRole("ADMIN");
http.authorizeRequests().antMatchers("/", "/index", "/add-content", "/mange-content").hasAnyRole("ADMIN", "USER");
http
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/index")
.failureUrl("/login?error=true")
.usernameParameter("username")
.passwordParameter("password")
//.failureUrl("/login-error.html")
.and()
.logout()
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutSuccessUrl("/login?logout")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.permitAll();
}
#Bean
public DaoAuthenticationProvider authProvider() {
System.out.println("GOT CALLED HERE.....");
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// TODO Auto-generated method stub
//auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
auth.authenticationProvider(authProvider());
}
}
UserDetailsService
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserDAO userDAO;
#Autowired
private RoleDAO roleDAO;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDAO.findUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User "+username+"not fount");
}
List<String> roleNames = roleDAO.getRoleNames(user.getAdminId());
System.out.println("USERNAME: "+user.getAdminId() + " "+user.getPassword());
List<GrantedAuthority> grantList = new ArrayList<GrantedAuthority>();
if (roleNames != null) {
for (String role : roleNames) {
GrantedAuthority authority = new SimpleGrantedAuthority(role);
grantList.add(authority);
}
}
UserDetails userDetails = (UserDetails) new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantList);
return userDetails;
}
}
TEST
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTests {
#Autowired
private UserDAO userDAO;
#Autowired
private BCryptPasswordEncoder encoder;
#Test
public void contextLoads() {
String password = "eureka";
String encrytedPassword = encoder.encode(password);
User user = userDAO.findUserByEmail("xxx#gmail.com");
System.out.println(encrytedPassword);
System.out.println("Matched: " + encoder.matches("eureka", encrytedPassword));//This line returns true
assertEquals(encrytedPassword, user.getPassword());
}
}
I also tried overriding matches() but to no avail
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder() {
#Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
// This throws an error
return matches(rawPassword, encodedPassword);
}
};
}
Note: Hashed password and raw password was gotten from matches() method. So there's no issues retrieving hashpassword from database.
I am new in Java Spring and I want to create a system with registration for users, which are stored in my DB (Postgres), where a password is stored encrypted by BCryptPasswordEncoder. The registration process is working fine, but when I want to log in, I always get an "Invalid username or password." message. I already search everywhere and read a lot of articles, but everything that I did had the same result.
Here is my SecurityConfiguration class:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("customUserDetailsService")
private CustomUserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService()).and().authenticationProvider(authProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/signin", "/confirm", "/error","/signup", "/css/**","/js/**","/images/**").permitAll()
.antMatchers("/admin/**").hasAuthority("ROLE_ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/signin")
.usernameParameter("username").passwordParameter("password")
.defaultSuccessUrl("/cockpit.html")
.permitAll()
.and()
.exceptionHandling().accessDeniedPage("/403")
.and()
.csrf()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/signin").and().exceptionHandling()
.accessDeniedPage("/error");
}
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
}
And here is my CustomUserDetailsService class:
#Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService{
private final UserRepository userRepository;
private final RoleRepository userRolesRepository;
#Autowired
private PasswordEncoder bCryptPasswordEncoder;
#Autowired
public CustomUserDetailsService(UserRepository userRepository,RoleRepository userRolesRepository) {
this.userRepository = userRepository;
this.userRolesRepository=userRolesRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws
UsernameNotFoundException {
Logger LOGGER = Logger.getLogger(CustomUserDetailsService.class.getName());
User user = userRepository.findByUsername(username);
if (null == user) {
return null;
} else {
List<GrantedAuthority> authorities =
buildUserAuthority(userRolesRepository.findRoleByUserName(username));
LOGGER.info("Loaded account: " + user.getUsername() + " password: " + user.getPassword() + " password matches: " + bCryptPasswordEncoder.matches("password", user.getPassword()));
org.springframework.security.core.userdetails.User userDetails = new org.springframework.security.core.userdetails.User(user.getUsername(), Deuser.getPassword(),authorities);
return userDetails;
}
}
private List<GrantedAuthority> buildUserAuthority(Set<Role> userRoles) {
Set<GrantedAuthority> setAuths = new HashSet<>();
// add user's authorities
for (Role userRole : userRoles) {
setAuths.add(new SimpleGrantedAuthority(userRole.getRole()));
}
return new ArrayList<>(setAuths);
}
public User findByConfirmationToken(String confirmationToken) {
return userRepository.findByConfirmationToken(confirmationToken);
}
public void saveUser(User user){
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
userRepository.save(user);
}
public void saveRole(User user) {
Role role = new Role();
role.setRole("ROLE_USER");
role.setId(user.getId());
role.setUsername(user.getUsername());
userRolesRepository.save(role);
}
}
I call the method saveUser(user) and saveRole(user) during registration. The LOGGER.info message gives me "false" for bCryptPasswordEncoder.matches("password", user.getPassword()) even I wrote right password.
SOLVED
Okay, I just found out where was the mistake. I called method saveUser twice, during registration, and then during activation, so the password was encrypted twice. I solved that by adding method updateUser without using encryption.
Thank you for your help.
Try like this:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// Create a default account
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
CustomUserDetails:
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User account = userDao.getUserByUsername(username);
System.out.println("User got from DB----------------------" + account.getPassword());
boolean enabled = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
User user = new User(account.getUserName(), account.getPassword(), enabled, accountNonExpired,
credentialsNonExpired, accountNonLocked, getAuthorities(account.getRole()));
System.out.println(user.getPassword());
return user;
}