3

as I am building a Login for a Website I would like to store a auto generated password in MySQL in a safe way, thus encrypt it in some way. But I am not sure how to combine the auto generation with encrypting the password.

I looked at a lot of questions but none really helped me or answered my question considering that it should be autogenerated. Also I would like to solve it in an elegant way without hardcoding and stuff if possible. I would be really happy if someone could help me as I am a beginner, thanks in advance!

Question:

How do I auto generate a secure encrypted passwords, what datatype should I use in Java (currently String) and then to what database type is it mapped?

How do I make sure I can decrypt the password of the database to check if it matches when someone logs in?

User Entity:

@Entity
public class User {

    @Id
    @GeneratedValue
    private Integer user_id;
    
    @Column(unique = true)
    private String username;
    
    private String password;
    
    @Enumerated(EnumType.STRING)
    private Role role;
    
    //non-Owning (address) side of the OneToOne relationship
    @OneToOne(mappedBy = "user")
    private RetailStore retailStore;

    /**
     * Constructor
     */
    protected User() {
        
    }
    
    /**
     * Constructor
     * @param username
     * @param role
     */
    public User(String username, String password, Role role) {
        super();
        this.username = username;
        this.password = password;
        this.role = role;
    }

User Repository:

@Repository
@Transactional
//necessary to be in an transaction to change something in database, 
//instead of doing this for every method itself it is declared by 
//the annotation @Transactional
public interface UserRepository extends JpaRepository<User, Long>{
    
}
7
  • 3
    Why are you auto generating passwords in the first place instead of letting users choose them? How do you let the users know about their passwords? Via email? And what makes you think encrypting them instead of hashing them is a good idea? security.stackexchange.com/questions/211/… Commented Aug 2, 2020 at 14:31
  • Do you need the stored pass for anything else OR just to check that the (later) by the user typed in password is correct ? Commented Aug 2, 2020 at 15:03
  • @luk2302 Hey, to answer your questions: Only the admin can register a new user since it is an infromation system and no social network or something and yes the plan is to notify them via email. Thought encryption includes hashing or let's say is a more general headline to include more ideas :) Hashing is probably is enough though but not sure how to combine this with jpa and auto generating. Commented Aug 3, 2020 at 15:44
  • @MichaelFehr Just needed for log in and the possibility for the user to set a new password :) Commented Aug 3, 2020 at 15:49
  • 1
    I would use PBKDF2 or BCRYPT as possible solutions (I would prefer BCRYPT in this case) Commented Aug 3, 2020 at 15:59

1 Answer 1

1

Solved it and for me it works perfect and looks beautiful.

Unfortunetly there is no real short answer for it, personally I spent a lot of time on the internet and then kinda just build it up by myself but maybe it helps someone:

  • used Spring Security combined with JWT --> use UserDetails by Spring, implement service(only loadByUsername is really necessary to implement in this class so that at log in spring security looks for the user in the database) and dto of it and set the right configuration telling Spring you want to use JWT, BCRYPT (see in Configuration Class below)
  • used BCRYPT (salt and hash with only calling a method and Autowire BCRYPT) (see UserDetailsService)
  • set password as string instance, every java string gets by default mapped to VARCHAR(255) in database mySQL

A lot of code for JWT for example you just have to copy. But I will show my UserDetailsService which is more a individual thing, for managing users, and also a part of the configuration which tells spring how to handle authorities and authorization including JWT and all that.

Configuration

@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AuthConfig extends WebSecurityConfigurerAdapter {

  @Autowired
  private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

  @Autowired
  private UserDetailsService jwtUserDetailsService;

  @Autowired
  private JwtRequestFilter jwtRequestFilter;

  @Value("${jwt.get.token.uri}")
  private String authenticationPath;

  /**
   * Configure AuthenticationManager so that it knows from where to load user for
   * matching credentials. Use BCryptPasswordEncoder.
   *
   * @param auth which creates authentication manager
   * @throws Exception if authentication manager has problems
   */
  @Autowired
  public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
  }

  @Bean
  public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
    return new PropertySourcesPlaceholderConfigurer();
  }

  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }

  @Bean
  @Override
  public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
  }

  /**
   * Configure who has access to which URLs. Organize authorities and their needed
   * Authorization.
   */
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    // no need of CSRF
    http.csrf().disable()
        // authenticate particular requests
        .authorizeRequests().antMatchers("/adm/*").hasAuthority("ADMIN")
        .antMatchers("/store/{username}/*").hasAuthority("STORE").and()
        // make sure we use stateless session; session won't be used to
        // store user's state.
        .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    // Add a filter to validate the tokens with every request
    http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
  }

  /**
   * Configure which URLs can be ignored by spring web security. Thus there is no
   * authentication.
   */
  @Override
  public void configure(WebSecurity webSecurity) throws Exception {
    // no authentification needed for specific URLs (login request,...)
    webSecurity.ignoring().antMatchers(HttpMethod.POST, authenticationPath)
        .antMatchers(HttpMethod.OPTIONS, "/**");
  }
}

UserDetailsServcie

@Service
public class JwtUserDetailsService implements UserDetailsService {

  @Autowired
  private UserRepository userrepo;

  @Autowired
  private RetailStoreRepository storerepo;

  @Autowired
  private PasswordEncoder bcryptEncoder;

  // Aggregated root

  public List<User> findAllUser() {
    return userrepo.findAll();
  }

  // Single Item

  /**
   * Add new user (ADMIN or STORE).
   *
   * @param account information about new user
   * @return new user
   */
  public AccountDto addUser(AccountDto account) {
    String username = account.getUser().getUsername();
    // encode password (hashing)
    String code = bcryptEncoder.encode(account.getUser().getPassword()).toString();
    Role role = account.getUser().getRole();
    // create new user
    User user = new User(username, code, role);
    // add new user to database
    userrepo.save(user);
    // check authorization and add store if needed
    if (account.getUser().getRole().equals(Role.STORE) && account.getStore() != null) {
      storerepo.save(account.getStore());
    }
    return account;
  }

  /**
   * Delete user.
   *
   * @param userId id of user who is deleted
   */
  public void deleteUser(Long userId) {
    // check if user exists
    if (userrepo.existsById(userId)) {
      userrepo.deleteById(userId);
      // also delete corresponding retail store if it is no admin
      User user = userrepo.findById(userId).get();
      if (user.getRole().equals(Role.STORE)) {
        RetailStore trashStore = storerepo.findByUser_userId(userId);
        storerepo.delete(trashStore);
      }

    } else {
      throw new UserNotFoundException(userId);
    }

  }

  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User user = userrepo.findByUsername(username);
    if (user == null) {
      throw new UsernameNotFoundException("User not found with username: " + username);
    }
    return new JwtUserDetailsDto(user.getUserId(), user.getUsername(), user.getPassword(),
        user.getRole().toString());
  }

}
Sign up to request clarification or add additional context in comments.

1 Comment

For newer users: please be mindful some elements of this reply are obsolete anymore.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.