[Spring] UserDetails & UserDetailsService 구현

WOOK JONG KIM·2022년 11월 8일
1
post-thumbnail
post-custom-banner

우선 인증과 인가 코드를 작성하기 위해 의존성을 추가

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

스프링 시큐리티는 기본적으로 UsernamePasswordAuthenticationFilter를 통해 인증을 수행하도록 구성
-> 이 필터는 인증이 실패하면 로그인 폼이 포함된 화면을 전달하게 되는데, 이 프로젝트에는 이러한 화면 존재 안함

따라서 JWT를 사용하는 인증 필터를 구현하고 UsernamePasswordAuthenticationFilter 앞에 인증 필터를 배치해서 인증 주체를 변경하는 작업을 수행하는 방식으로 구성


UserDetails & UserDetailsService 구현

User(사용자 정보) 엔티티 생성

@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Table
public class User implements UserDetails {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @Column(nullable = false, unique = true)
    private String uid;

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Column(nullable = false)
    private String password;

    @Column(nullable = false)
    private String name;

    @ElementCollection(fetch = FetchType.EAGER)
    @Builder.Default
    private List<String> roles = new ArrayList<>();

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return this.roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
    }

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Override
    public String getUsername() {
        return this.uid;
    }

    @JsonProperty(access =  JsonProperty.Access.WRITE_ONLY)
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Override
    public boolean isEnabled() {
        return false;
    }
}

이 엔티티는 앞으로 토큰을 생성할때 토큰의 정보로 사용될 정보와 권한 정보를 갖게 됨

UserDetails 인터페이스

public interface UserDetails extends Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();

    String getPassword();

    String getUsername();

    boolean isAccountNonExpired();

    boolean isAccountNonLocked();

    boolean isCredentialsNonExpired();

    boolean isEnabled();
}
  • getAuthorities() : 계정이 가지고 있는 권한 목록 리턴
  • getPassword() : 계정의 비밀번호 리턴
  • getUsername() : 계정 이름 리턴
  • isAccountNonExpired() : 계정이 만료됐는지 리턴 -> true는 완료되지 않음 의미
  • isAccountNonLocked() : 계정이 잠겨있는지 리턴 -> true는 잠기지 않음
  • isCredentialNonExpired() : 비밀번호가 만료됐는지 리턴 -> true는 만료X 의미
  • isEnabled() : 계정이 활성화돼 있는지 리턴 -> true는 활성화 상태 의미

엔티티를 조회하기 위한 리포지토리와 서비스 구현

public interface UserRepository  extends JpaRepository<User, Long> {
    
    User getByUid(String uid);
}

ID 값은 인덱스 값이기 때문에 id값을 토큰 생성 정보로 사용하기 위해 getByUid() 메서드를 생성

리포지토리를 통해 User엔티티의 id를 가져오는 서비스 생성

UserDetailsServiceImpl 구현

@RequiredArgsConstructor
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    
    private final Logger LOGGER = LoggerFactory.getLogger(UserDetailsServiceImpl.class)
    
    private final UserRepository userRepository;
    
    @Override
    public UserDetails loadUserByUsername(String username){
        LOGGER.info("[loadUserByUsername] loadUserByUsername 수행. username : {}", username);
        return userRepository.getByUid(username);
    }
}
package org.springframework.security.core.userdetails;

public interface UserDetailsService {
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

UserDetails는 스프링 시큐리티에서 제공하는 개념으로, UserDetails의 username은 각 사용자를 구분할수 있는 ID 의미

위 인터페이스를 보면 username을 가지고 UserDetails 객체를 리턴하는데, UserDetails의 구현체로 User 엔티티를 생성하였기에 User 객체를 리턴하게끔 구현한 것

profile
Journey for Backend Developer
post-custom-banner

0개의 댓글