본문 바로가기
프로젝트/SpringSecurity

[Spring Security] 13. JWT를 이용해 로그인 테스트하기

by dantriss 2023. 7. 17.

유튜브에서 스프링시큐리티를 심화교육하면서 조금더 레벨업 하고자 한다.

IDE : IntelliJ

언어 : Java 8

스프링부트 버전 : 2.7.13

DB : MySQL

빌드관리 도구 : Maven

OS : iOS

참고유튜브 : 메타코딩


JWT을 이용해서 로그인을 진행해보도록 하자.

auth 패키지에 PrincipalDetails 클래스를 만든다.

 

UserDetails를 오버라이딩 해준 후 User 모델 클래스를 불러와준후 

cmd+n 로 생성자를 만들어주고 ctrl+o로 오버라이딩 메서드들을 추가해준다.

 

cmd+n -> constructor  후 생성자를 만들어준다.

cmd + n

 

ctrl+o

GrantedAuthority에 권한 설정을 위해서 user모델에 있는 RoleList에서 권한을 넣어주도록 설정하고

password와 username은 user 모델에 있는 password와 username을 리턴해주고

계정 만료, 계정 잠금 설정들은 전부 true로 변경해주어서 사용할 수 있게 만들어준다.

public class PrincipalDetails implements UserDetails {
    private User user;

    public PrincipalDetails(User user) {
        this.user = user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        user.getRoleList().forEach(r-> {
                authorities.add(()->r);
        });

        return null;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }




}

 

 

auth 패키지 아래에 PrincipalDetailsService를 생성해준다.

 

UserDetailsService를 오버라이딩 해준다.

 

ctrl+o 로 loadUserByUsername 메서드를 오버라이딩 해준다.

 

@Service 어노테이션추가로 해당 클래스가 서비스라고 지정해주고 @RequiredArgsConstructor 로 생성자들을 주입해준다.

@Service
@RequiredArgsConstructor
public class PrincipalDetailsService implements UserDetailsService {

    private final UserRepository userRepository;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User userEntity = userRepository.findByUsername(username);

        return null;
    }
}

 

그 후 UserRepository로 이동해서 findByUsername 메서드를 작성해준다.

repository 패키지 생성 후 UserRepository 인터페이스를 생성 후 JpaRepository를 오버라이딩 해준 후 

findByUsername을 만들어준다. 파라메터론 username을 받아준다.

public interface UserRepository extends JpaRepository<User,Long> {

    public User findByUsername(String username);

}

 

다시 PrincipalDetailsService로 돌아와서 username으로 찾은 유저 정보를 PrincipalDetails로 넘겨준다.

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User userEntity = userRepository.findByUsername(username);

    return new PrincipalDetails((userEntity));
}

 

원래대로라면 http://localhost:portnumber/login 으로 접근을 하면 해당 서비스가 동작을 하겠지만 SecurityConfig에서

formlogin을 동작하지 않게 만들었기 때문에 실행 한 후 username과 password를 보내도 404에러가 발생한다.

securityConfig에서 formlogin을 사용하지 않겠다고 했다.

 

securityConfig에서 추가했던 필터부분을 다 주석으로 막고

body에 username 과 password를 보내면 404 에러가 뜨는걸 확인 할 수 있다.

 

해당부분을 위해서 필터를 추가해주어야 하는데 jwt패키지에 JwtAuthenticationFilter 클래스 생성

 

UsernamePasswordAuthenticationFilter를 오버라이딩해서 @RequiredArgsConstructor로 생성자를 주입해준다.

해당 클래스에서 attemptAuthentication 메서드를 오버라이딩 해주는데 해당 메서드는 /login으로 접근을 해서 로그인 요청을 할 때

실행 되는 함수이다.  

 

@RequiredArgsConstructor
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    private final AuthenticationManager authenticationManager1;

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        System.out.println("로그인 시도중");
        
        return super.attemptAuthentication(request, response);
    }
}

 

위에서 만든 필터를 SecurityConfig에 추가해주기 위해서 securityConfig로 이동해서 아래와 같이 추가해준다.

    public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {
        @Override
        public void configure(HttpSecurity http) throws Exception {
            AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
            http
                    .addFilter(corsConfig.corsFilter())
                    .addFilter(new JwtAuthenticationFilter(authenticationManager));
        }
    }

 

그리고 securityConfig에 MyCustomDsl 필터 추가해주면 된다.

 

그리고 로그인 시도를 다시 진행해보면 500에러가 발생을 하지만 JwtAuthenticationFilter에서 콘솔에서 찍어본 로그인시도중이라는걸 확인할 수 있다. 즉 /login으로 접근 시도를 했을 때 해당 필터를 통과한다는걸 확인 할 수있다.

 

 

/login으로 접근을 하면 username과 password를 받아서 정상적인 로그인시도인지 PrincipalDetailsService가 실행이 되어 loadUserByUsername 함수가 실행이 된다. 

그렇게 로그인이 되면 PrincipalDetails를 세션에 담아주는데 jwt를 이용해서 로그인 시도를 하는데 왜 또 세션에 유저정보를 저장하느냐 한다면 권한때문에 그렇다.

권한마다 접근할수 있는 곳이 다른데 세션에 저장하지 않으면 해당기능을 사용할 수 없기 때문에 세션에 정보를 저장한 후

jwt 토큰을 만들어서 클라이언트측으로 응답해줘서 jwt를 발급한 서버가 아니더라도 다른 서버에서 유저정보같은 요청을 받아도

jwt 토큰을 이용해서 인증을 해서 해당 사용자의 정보를 제공할 수 있게 된다.

댓글