모르지 않다는 것은 아는것과 다르다.

Spring Security

CustomUserDetailService

채마스 2022. 2. 28. 20:40

개요

  • 스프링 시큐리티에서 기본적으로 UserDetailsService 를 제공해 주지만, 프로젝트에 맞게 커스텀할 일이 생기기 마련이다.
  • 디비에서 사용자 정보를 가져와서 AuthenticationProvider 에게 UserDetails 타입의 유저 정보를 넘겨주는 UserDetailsService 를 직접 구현해 보려고 한다.





UserDetailsService

  • 위와 같이 UserDtailsService 는 UserRepository 로 부터 데이터를 가져와서 AuthenticationProvider 에게 UserDetails 타입의 유저 정보를 만들어서 넘겨준다.

코드

  • Account 엔티티 구현
@Entity
@Data
@EqualsAndHashCode(of = "id")
@NoArgsConstructor
@AllArgsConstructor
public class Account implements Serializable {

    @Id
    @GeneratedValue
    private Long id;

    @Column
    private String username;

    @Column
    private String email;

    @Column
    private int age;

    @Column
    private String password;

    @Column
    private String role;

}
  • UserRepository 인터페이스 구현
public interface UserRepository extends JpaRepository<Account, Long> {

    Account findByUsername(String username);
}
  • AccountContext 클래스 구현
public class AccountContext extends User {

    private final Account account;

    public AccountContext(Account account, Collection<? extends GrantedAuthority> authorities) {
        super(account.getUsername(), account.getPassword(), authorities);
        this.account = account;
    }

    public Account getAccount() {
        return account;
    }
}
  • User 를 상속받는 AccountContext 를 구현한다.
  • 여기서 User 는 스프링 시큐리티에서 제공하는 User 이다.
  • 밑에서 구현한 CustomUserDetailsService의 loadUserByUsername 메소드의 반환값으로 UserDetails 타입의 객체가 반환되어야 하기 때문에, UserDtails 를 구현하고 있는 User 객체를 상속받는 클래스로 구현한 것이다.
  • CustomUserDetailsService 클래스 (이 부분이 중요하다.)
@Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        Account account = userRepository.findByUsername(username);

        if(account == null){
            throw new UsernameNotFoundException("UsernameNotFoundException");
        }

        List<GrantedAuthority> roles = new ArrayList<>();
        roles.add(new SimpleGrantedAuthority(account.getRole()));

        AccountContext accountContext = new AccountContext(account, roles);

        return accountContext;
    }
}
  • 위와 같이 loadUserByUsername 메소드를 오버라이드해서 재정의 해준다.
  • userRepository 로 부터 데이터를 불러온다. -> 존재하지 않는다면 UsernameNotFoundException 예외를 던진다.
  • UserDetails 타입으로 반환해야 되기 때문에 위에서 구현한 AccountContext 으로 감싸서 반환한다.





SecurityConfig 에 CustomUserDetailService 등록


    private final UserDetailsServie userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailService(userDetailsService);
    }
  • 위와 같이 직접 구현한 userDetailsService 를 등록해 줌으로써, 스프링 시큐리티에서 기본적으로 제공하는 UserDetailsService 를 사용하지 않고 직접 구현한 CustomUserDetailService 를 사용할 수 있다.




REFERENCES

  • 정수원님의 스프링 시큐리티