JPA + MySql + 实现密码加密

huangapple go评论60阅读模式
英文:

JPA + MySql + Implement encrypted password

问题

As I am building a Login for a Website I would like to store an 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.

我正在构建网站的登录功能,我希望以安全的方式将自动生成的密码存储在MySQL中,并以某种方式进行加密。但我不确定如何将自动生成与密码加密相结合。

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 password, what datatype should I use in Java (currently String), and then to what database type is it mapped?

如何自动生成安全的加密密码,我应该在Java中使用什么数据类型(目前是String),然后它映射到什么数据库类型?

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 a transaction to change something in the database, 
//instead of doing this for every method itself it is declared by 
//the annotation @Transactional
public interface UserRepository extends JpaRepository<User, Long> {
	
}

Please note that the code provided here is in English, as requested, with the translation in Chinese provided for context. If you have specific questions or need further assistance, feel free to ask. 请注意,此处提供的代码是英文的,如您要求的那样,提供了中文翻译以提供上下文。如果您有具体问题或需要进一步帮助,请随时提问。

英文:

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 = &quot;user&quot;)
    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&lt;User, Long&gt;{
	
}

答案1

得分: 1

以下是翻译好的部分:

  • 解决了这个问题,对我来说完美运作且看起来很漂亮。

不幸的是,对于这个问题没有真正的简短答案,个人花了很多时间在互联网上,然后自己构建了它,但也许对某人有帮助:

  • 使用了Spring Security结合JWT --> 使用Spring的UserDetails,实现了服务(只有在这个类中实现了loadByUsername,这样在登录时Spring Security会在数据库中查找用户)以及它的DTO,并设置了正确的配置,告诉Spring你要使用JWT,BCRYPT(见下面的配置类)
  • 使用了BCRYPT(使用一个方法进行盐和哈希,自动装配BCRYPT)(见UserDetailsService)
  • 将密码设置为字符串实例,每个Java字符串默认映射到MySQL数据库的VARCHAR(255)

JWT的大部分代码,例如你只需要复制。但我会展示我的UserDetailsService,这更像是一个个性化的东西,用于管理用户,还有一部分配置,告诉Spring如何处理权限和授权,包括JWT和所有这些。

配置

@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;

  /**
   * 配置AuthenticationManager,以便知道从哪里加载用户的匹配凭据。使用BCryptPasswordEncoder。
   *
   * @param auth 创建身份验证管理器
   * @throws Exception 如果身份验证管理器存在问题
   */
  @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();
  }

  /**
   * 配置谁可以访问哪些URL。组织权限和所需的授权。
   */
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    // 不需要CSRF
    http.csrf().disable()
        // 验证特定请求
        .authorizeRequests().antMatchers("/adm/*").hasAuthority("ADMIN")
        .antMatchers("/store/{username}/*").hasAuthority("STORE").and()
        // 确保我们使用无状态会话;会话将不用于存储用户的状态。
        .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    // 在每个请求之前添加一个过滤器来验证令牌
    http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
  }

  /**
   * 配置Spring Web Security可以忽略哪些URL。因此不需要认证。
   */
  @Override
  public void configure(WebSecurity webSecurity) throws Exception {
    // 特定URL不需要认证(登录请求等)
    webSecurity.ignoring().antMatchers(HttpMethod.POST, authenticationPath)
        .antMatchers(HttpMethod.OPTIONS, "/**");
  }
}

UserDetailsService

@Service
public class JwtUserDetailsService implements UserDetailsService {

  @Autowired
  private UserRepository userrepo;

  @Autowired
  private RetailStoreRepository storerepo;

  @Autowired
  private PasswordEncoder bcryptEncoder;

  // 聚合根

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

  // 单项

  /**
   * 添加新用户(ADMIN或STORE)。
   *
   * @param account 新用户的信息
   * @return 新用户
   */
  public AccountDto addUser(AccountDto account) {
    String username = account.getUser().getUsername();
    // 编码密码(哈希)
    String code = bcryptEncoder.encode(account.getUser().getPassword()).toString();
    Role role = account.getUser().getRole();
    // 创建新用户
    User user = new User(username, code, role);
    // 将新用户添加到数据库
    userrepo.save(user);
    // 检查授权并添加商店(如果需要)
    if (account.getUser().getRole().equals(Role.STORE) && account.getStore() != null) {
      storerepo.save(account.getStore());
    }
    return account;
  }

  /**
   * 删除用户。
   *
   * @param userId 被删除的用户的ID
   */
  public void deleteUser(Long userId) {
    // 检查用户是否存在
    if (userrepo.existsById(userId)) {
      userrepo.deleteById(userId);
      // 如果不是管理员,还要删除相应的零售店
      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("找不到用户名为:" + username + "的用户");
    }
    return new JwtUserDetailsDto(user.getUserId(), user.getUsername(), user.getPassword(),
        user.getRole().toString());
  }
}

希望这可以帮助你!如果你需要更多翻译,请提供具体内容。

英文:

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(&quot;${jwt.get.token.uri}&quot;)
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(&quot;/adm/*&quot;).hasAuthority(&quot;ADMIN&quot;)
.antMatchers(&quot;/store/{username}/*&quot;).hasAuthority(&quot;STORE&quot;).and()
// make sure we use stateless session; session won&#39;t be used to
// store user&#39;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, &quot;/**&quot;);
}
}

UserDetailsServcie

@Service
public class JwtUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userrepo;
@Autowired
private RetailStoreRepository storerepo;
@Autowired
private PasswordEncoder bcryptEncoder;
// Aggregated root
public List&lt;User&gt; 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) &amp;&amp; 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(&quot;User not found with username: &quot; + username);
}
return new JwtUserDetailsDto(user.getUserId(), user.getUsername(), user.getPassword(),
user.getRole().toString());
}
}

huangapple
  • 本文由 发表于 2020年8月2日 22:22:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/63217082.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定