🗄 이번엔 인메모리 방식이 아닌 DB에서 유저를 가져와 인증해봅시다!
Spring Security에서는 UserDetailService를 이용하여 유저를 조회합니다. 우리는 이를 구현하는 클래스를 만듭니다. 우리의 username은 email이므로 userMapper가 파라미터로 들어온 email로 유저를 조회하여 반환할 수 있도록 만듭니다.
@Service
@RequiredArgsConstructor
public class CustomUserDetailServiceImpl implements UserDetailsService {
private final UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
return userMapper.findByEmail(s);
}
}
UserDetailService가 반환하는 클래스는 UserDetails입니다. 따라서 UserVO가 이를 구현하는 클래스로 수정합니다. UserDetails는 반드시 권한을 가져야합니다. 따라서 일단 ROLE_USER를 넣어 반환하겠습니다.
@Data
public class UserVO implements UserDetails {
private int id;
private String name;
private String password;
private String email;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
}
/*
* Spring Security의 username을 반환
*/
@Override
public String getUsername() {
return email;
}
/*
* 계정이 만료되었는가?
* */
@Override
public boolean isAccountNonExpired() {
return true;
}
/*
* 계정이 잠겼는가?
* */
@Override
public boolean isAccountNonLocked() {
return true;
}
/*
* 계정의 인증(비밀번)이 만료되었는가?
* */
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/*
* 계정이 사용가능한가?
* */
@Override
public boolean isEnabled() {
return true;
}
}
우리는 이제 인메모리 방식이 아닌 커스텀된 UserDetailService를 사용해 유저를 조회합니다. Spring Security는 반드시 PasswordEncoder를 사용해야해 이를 생성하고 반환하는 메소드를 @Bean으로 만들어줍니다. 여기서 PasswordEncoderFactories.createDelegatingPasswordEncoder()는 기본적으로 BCRYPT 인코딩입니다딩
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder(){
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
비밀번호를 확인하는 메소드는 필요가 없으므로 주석처리하고, 유저의 정보를 저장할 때 비밀번호를 인코딩하여 저장할 수 있도록 수정합니다.
public interface UserService {
public void save(UserVO userVO);
public boolean checkEmailDuplication(String email);
// public boolean checkPassword(String email, String password);
public UserVO findByEmail(String email);
}
@RequiredArgsConstructor
@Service
public class UserServiceImpl implements UserService {
private final UserMapper userMapper;
private final PasswordEncoder passwordEncoder;
@Override
public void save(UserVO userVO) {
userVO.setPassword(passwordEncoder.encode(userVO.getPassword()));
userMapper.save(userVO);
}
@Override
public boolean checkEmailDuplication(String email) {
return userMapper.checkEmailDuplication(email)==0;
}
// @Override
// public boolean checkPassword(String email, String password) {
// return password.equals(userMapper.findByEmail(email).getPassword());
// }
@Override
public UserVO findByEmail(String email) {
return userMapper.findByEmail(email);
}
}
인코딩한 비밀번호는 길어서 user 테이블의 password의 길이를 늘려줍니다. 그리고 이미 있는 user는 모두 삭제해야 하므로 저장되있던 데이터를 모두 삭제합니다.
alter table user modify password varchar(100);
delete from file;
delete from post;
delete from user;