ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Delivery Together - JWT 인증방식의 로그인(2)
    Projects/Problem & Solution 2021. 12. 24. 15:16

    사용자 정보 생성

    User 클래스 생성
    @Data
    public class User implements UserDetails {
        private String username;   // 사용자 계정
    
        private String birthdate;   // 사용자 생일
    
        private String country;   // 사용자 국적
    
        private String gender;   // 사용자 성별
    
        private String password;   // 사용자 비밀번호
    
        private String role;   // 사용자 권한
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            Collection<GrantedAuthority> collection = new ArrayList<>();
    
            collection.add(new GrantedAuthority() {
                @Override
                public String getAuthority() {
                    return role;
                }
            });
    
            return collection;
        }
    
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }
    
        @Override
        public boolean isAccountNonLocked() {
            return true;
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
    
        @Override
        public boolean isEnabled() {
            return true;
        }
    }

    위 코드는 사용자에 대한 정보를 정의한 클래스이다.

    사용자는 username, birthday, country, password 등과 같은 정보를 가지고 있다.

    여기서 주목해야 할 점은 User 객체가 UserDetails 인터페이스를 구현한다는 점이다.

    그렇다면 왜 UserDetails 인터페이스를 구현하는지 아래에서 알아보겠다.

     

    UserDetails란?

    Spring은 로그인의 진행이 정상적으로 완료되면 Spring Security를 위한 Session을 만들어 로그인 처리를 한다. 이때, Security Session안에 들어갈 수 있는 오브젝트 타입은 Authentication 타입이다.

     

    Authentication 타입 안에는 로그인을 한 User에 대한 정보가 들어가 있어야 하는데, 여기서 중요한 점은 Authentication으로 감쌀 수 있는 User 타입이 단순한 User 오브젝트가 아닌 UserDetail 타입만 가능하다.

     

    Spring Security에서 사용자의 정보를 담은 인터페이스가 바로 UserDetails 인터페이스이다. 개발자가 이 인터페이스를 구현하게 되면 Spring Security에서 해당 인터페이스를 구현한 클래스를 사용자 정보로 인식하고 인증 작업을 수행한다.

     

    따라서, 인증 과정을 올바르게 수행하기 위해서는 사용자 정보를 UserDetails 타입으로 구현해야 하는 것이다.

     

    UserDetails 인터페이스가 가지는 아래의 메서드를 오버라이딩 함으로써, User 클래스를 구현할 수 있다.

     

    메서드 명 설명
    getAuthorities() 계정이 가지고 있는 권한 목록을 리턴
    isAccountNonExpired() 계정이 만료되지 않았는 지 리턴 (true : 만료 안됨)
    isAccountNonLocked() 계정이 잠겨있지 않았는 지 리턴 (true : 잠기지 않음)
    isCredentialNonExpired() 비밀번호가 만료되지 않았는 지 리턴 (true : 만료 안됨)
    isEnabled() 계정이 활성화되었는 지 리턴 (true: 활성화 됨)

    사용자 정보 로드

    1. UserDetailsService
    @Service
    @RequiredArgsConstructor
    public class UserService implements UserDetailsService {
    
        private final UserRepository userRepository;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    
            User user = null;
    
            try {
                user = userRepository.selectUserDetail(username);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            if (user == null)
                throw new UsernameNotFoundException(username + " 사용자를 찾을 수 없습니다.");
    
            return user;
        }
    
    }

    사용자의 정보를 담을 객체를 만들었으니, DB에서 유저 정보를 직접 가져오는 클래스를 생성한다.

    위 코드는 UserDetailsService 인터페이스를 구현하고 있는 형태이며, UserDetailsService 인터페이스에는 DB에서 유저 정보를 불러오는 중요한 메서드가 존재한다. 바로 loadUserByname() 메서드이다.

    loadUserByname() 메서드는 사용자 정보를 불러오는 작업을 수행하며, UserDetails 타입인 User 객체를 반환한다.

     

    Spring Security 인증 절차

     

    1. 사용자가 form을 통해 로그인 정보가 담긴 요청을 보낸다.

    2. AuthenticationFilter가 사용자가 보낸 사용자 이름과 비밀번호를 인터셉트한다. 가로챈 정보를 인증용 객체(UsernamePasswordAuthenticationToken)로 만든 후 인증을 담당하는 AuthenticationManager 인터페이스에게 전달한다.

    3. AuthenticationFilter에게 인증용 객체(UsernamePasswordAuthenticationToken)를 전달받는다.

    4. 실제 인증 작업을 수행하는 AuthenticationProvider에게 Authentication(UsernamePasswordAuthenticationToken) 객체를 전달한다.

    5. 인증 작업이 시작되면 AuthenticationProvider 인터페이스가 실행되며, DB에 있는 사용자의 정보와 화면에 입력한 로그인 정보를 비교하게 된다.

    6. AuthenticationProvider 인터페이스에서는 authenticate() 메서드를 오버라이딩 하게 되는데, 화면에서 입력한 로그인 정보를  authenticate() 메서드의 파라미터인 Authentication으로 가져올 수 있다.

    7. AuthenticationProvider 인터페이스에서 DB에 있는 사용자의 정보를 가져오려면, UserDetailsService 인터페이스를 사용해야 한다.

    8. UserDetailsService 인터페이스는 화면에서 입력한 사용자의 이름을 가지고 loadUserByUsername() 메서드를 호출하여 DB에 있는 사용자의 정보를 UserDetails 타입으로 가져온다. 이렇게, DB에서 가져온 사용자의 정보와 화면에서 입력한 로그인 정보를 비교하게 되고, 일치하면 Authentication 참조를 리턴하고, 그렇지 않으면 예외를 던진다.

    9. 인증이 완료되면 사용자 정보를 가진 Authentication 객체를 SecurityContextHolder에 담은 이후 AuthenticationSuccessHandle을 실행한다.


     

    GitHub - yu-capstone-design/delivery-together: 🍕 배달 주문을 같이할 사용자를 찾도록 매칭 서비스를 제공

    🍕 배달 주문을 같이할 사용자를 찾도록 매칭 서비스를 제공해주는 애플리케이션. Contribute to yu-capstone-design/delivery-together development by creating an account on GitHub.

    github.com

     

    728x90

    댓글

Designed by Tistory.