이제 우리는
우리만의 커스텀 로직을 작성해
저번 강의에서 생성하였던 표를 이용한
인증이 가능하도록 할 준비가 되었습니다
전에 말씀드렸듯이
데이터베이스로부터 유저 정보를
인증을 위하여 반환하는 것은 흔한 시나리오입니다
이 매우 흔한 시나리오를 해결하기 위하여
Spring Security 프레임워크에 인터페이스가 있으며
그 인터페이스는 UserDetailsService입니다
혹시 기억나십니까?
우리가 전에 다루었던 인터페이스입니다
이제는 이 인터페이스를 오버라이드하여
LoadUserByUsername 메소드
코드를 직접 작성하여야 합니다
그리고 이 메소드 내부에서는 정보를
맞춤 표로부터 가져온 다음
UserDetails 객체로 변환하여 프레임워크로 돌려보내야 합니다
인증이 가능케 하기 위해서 말입니다
이를 위해 저는
이 conflict 패키지 내부에 새로운 클래스를 생성할 것입니다
이 Java 클래스에게 붙일 이름은
EazyBankUserDetails입니다
이 클래스가 implements로
UserDetailsService를 구현하여야 합니다
구현을 시도하자마자
UserDetailsService 내에 이미 존재하는
메소드를 오버라이드해야 한다고 알립니다
오버라이드하겠습니다
지금은 여기 보이시듯이 텅 빈 메소드입니다
이 메소드 안에서만은 로직을 직접 모두 작성해야 합니다
그 전에 데이터베이스로부터
기록을 반환하기 위하여는
Spring Data JPA의 도움을 받아야 합니다
도움을 받기 위하여
CustomerRepository라는 새 리포지터리를 생성해야 합니다
그 다음 이 클래스로 자동주입하려고 합니다
그러므로 @Autowire 주석(어노테이션)을 사용하겠습니다
그러면 이제는 메소드 안에 코드를 붙여 넣을 건데
비지니스 로직과 비슷한 것입니다
그러므로 타자로 전부 칠 수 없습니다
시간이 너무 많이 소요되기 때문입니다
하지만 설명은 모두 자세히 할 것이니
이해해 주십시오
코드 몇 줄을 붙여 넣습니다
메소드에서 필요로 하는
클래스들을 내려받아 봅시다
여기서 Customer는 model 클래스이며 List는
Java 라이브러리의 인터페이스입니다
ArrayList는 Collection에서 온 클래스 입니다
SimpleGrantedAuthority는
Spring Security에서 온 클래스입니다
또한 User는
UserDetailsService의 구현입니다
이제 코드를 보시면
변수 몇 개, 아이디, 비밀번호를 null로 정의하였습니다
authorities라는 리스트를 생성하였으며
리스트의 유형도 null입니다
또한 완전히 비어 있습니다
제가 지금 처음으로 하려는 일은
이 username을 넘김으로써
customer 리포지터리에 있는 메소드를 호출하는 것입니다
메소드의 명칭은 findByEmail입니다
여기서 여러분은 의문이 들 수도 있습니다
비지니스 로직이라고 할 것이 정말 있나?
이 메소드는 어떻게 호출하지?
바로 여기서 SpringData JPA가
활약하는 것입니다
Spring Data JPA가
전달받은 username으로 무엇을 하고자 하느냐면
이 시나리오에서는
아이디가 곧 이메일이므로
이 메소드의 입력은 email이 됩니다
그리고 메소드 명칭을 기반으로 SpringData JPA는
SQL 스크립트를
주어진 데이터베이스에 대하여 실행할 것입니다
그리고 이에 따라
주어진 이메일 주소를 가진 고객을 찾아 올 것입니다
만약 해당되는 고객이 한 명도 없다면?
customer의 크기를 확인하는 부분이 있습니다
그래서 고객이 없으면
"User details not found for the user“라는 에러 메시지가 뜹니다
그러면 인증에 실패하였다는 뜻입니다
else 블록에서는
우선 데이터베이스에 저장하였던
아이디와 비밀번호를
getEmail과 getPwd의 도움으로 반환할 것입니다
이 username과 password 값은
user 클래스 생성자에게 넘길 것입니다
같은 값을
DaoAuthenticationProvider에게 줄 수 있도록 하기 위해서입니다
DaoAuthenticationProvider 내부에서는
데이터베이스에서 보내는 비밀번호와
엔드 유저로부터 받는 비밀번호를
비교할 것입니다
권한에 대해서는
지금으로서는 우리는 권한을
customer 표의 한 열로 저장하고 있습니다
지금 우리의 표 구조로는
엔드 유저 한 명에게만
역할 또는 권한 하나만 할당할 수 있습니다
하지만 앞으로의 강의에서는 이를 개선하여
웹 애플리케이션이 여러 개의 역할을 가지고
싱글 유저가 여러 권한을 가질 수 있도록 하겠습니다
user 클래스에서는 권한 정보를
GrantedAuthority의 형태로 받아들이고 있으니
저는 이에 따라
SimpleGrantedAuthority 클래스를 생성하고 있습니다
SimpleGrantedAuthority 클래스는
GrantedAuthority 인터페이스를 구현합니다
그리고 이 SimpleGrantedAuthority에게
역할의 문자열 값을 넘기고 있습니다
그리고 같은 값을
authorities 리스트에 넘긴 다음
user 클래스 생성자에게 그 리스트를 넘깁니다
그러므로 authorities에 아이디와 비밀번호를 보내면
user 객체는 Spring Security 프레임워크로 돌려보내지고
프레임워크 내부에서
실제 인증이 일어나는 것입니다
저는 여기서 개발자로서
Spring Security 인증 과정에서 작은 역할을 하였습니다
저는 데이터베이스에서 유저 정보를
저의 맞춤 비지니스 로직으로 반환하였습니다
그리고 반환한 사용자 정보를
프레임워크를 보내기만 하면
그곳에서 실제 인증이 일어납니다
이걸로 우리는 성공적으로
UserDetailsService의 구현 클래스를 만들었습니다
하지만 기본적으로는 Spring Security가
우리가 생성한
UserDetailsService의 구현 클래스를
이해할 수 없습니다
Spring 프레임워크인 Spring Security에서
이를 이해할 수 있도록 하려면
이 클래스를 Bean으로 생성하여야 합니다
그러므로 여기에 @Service 주석(어노테이션)을 넣겠습니다
왜냐하면 이 클래스 내부에는
비지니스 로직을 썼으므로 service layer로 기능하기 때문입니다
이제 이 변화들로 Spring Security 프레임워크는
인증을 진행하려 할 때마다
인증 요청을
DaoAuthenticationProvider에게 전달하고
DaoAuthenticationProvider가
개발자에서 사용하도록 요구한
UserDetailsService의 구현 클래스를 찾을 것입니다
지금과 같은 상태에서는
두 개의 구현 클래스를 찾아낼 것입니다
그 중 하나는 EazyBankUserDetails이며
다른 하나는 ProjectSecurityConfig 내부에 있습니다
또한 UserDetailsService 유형의 Bean을 하나 더
JdbcUserDetailsManager class의 도움으로 생성했습니다
여기서는 혼선이 발생할 것이 분명합니다
이제 인증을 시도해 보면
예외가 뜰 것입니다
예외을 한 번 보여드리겠습니다
이를 위하여 우선 프로젝트 코드를 빌드하고
웹 애플리케이션을 재시작합니다
변경 사항이 많으니
그것들이 확실히 반영되도록 하기 위해서입니다
웹 애플리케이션을
Debug 옵션을 이용하여 재시작하겠습니다
이제 서버는 8080번 포트에서 실행되었습니다
브라우저 내부에서
보안 API 중 하나
예를 들면 myLoans를 호출하여
인증을 시도해 보겠습니다
API를 호출합니다
처음에는 아무 자격 증명이나 입력해 보겠습니다
Username에도 아무거나
비밀번호도 아무렇게나 입력했습니다
이제 보시면
No AuthenticationProvider found라고 표시됩니다
이러는 이유는 AuthenticationManager에서
DaoAuthenticationProvider를 포함한
모든 사용 가능한 AuthenticationProvider를 찾아봤지만
DaoAuthenticationProvider에서는
인증을 진행할 수 없는 것입니다
그 원인은 프로젝트 내부에
UserDetailsService 구현이 여러 개 있어
DaoAuthenticationProvider에서
충돌이 발생한 것일 겁니다
해결하기 위해 우리는 무엇을 할 수 있냐면
JdbcUserDetailsManager에 관한
이 코드를 주석으로 변환할 수 있습니다
이러면 더 이상
JdbcUserDetailsManager를
UserDetailsService의 구현 클래스로 사용하지 않는 것입니다
그 대신 우리의 맞춤 로직이 남습니다
여기에 중단점를 둬 보겠습니다
빌드를 합니다
빌드가 완료되었으면
브라우저로 갑니다
여기에 처음에는 자격 증명을 아무렇게나 입력하겠습니다
그 다음 로그인을 클릭합니다
이제 UserDetails 클래스인
EazyBankUserDetails 클래스의
중단점에서 멈췄습니다
처음에 시도하였던 아이디는
제가 아무렇게나 입력한 것입니다
그러므로 이 아이디에 관한
기록은 데이터베이스에 하나도 없습니다
그러므로 Customer 리스트는 비어 있을 것이며
그로 인해 이 if 블록으로 이동할 것입니다
이 중단점에서 재개해 보면
인증은 실패합니다
여기서도 실패한 것을 볼 수 있습니다
이번에는 제대로 된 자격 증명을 입력하여 봅시다
이메일 값은 johndoe@example.com
비밀번호 값은 54321입니다
로그인을 클릭한 다음 중단점에서 재개하면
그러면 이번에는
보안 경로인 myLoans에 접근이 됩니다
이걸로 우리는 웹 애플리케이션을
성공적으로 맞춤화하여
데이터베이스 또는 저장소로부터
사용자 정보로부터 반환하는 로직을 작성해 이를
Spring Security 프레임워크가 인증에 활용할 수 있도록 하였습니다
이걸로 우리가 무엇을 하고 있는지
잘 이해되셨길 바랍니다
저는 여러분이
여러분의 웹 애플리케이션에서
저를 따라하면서 강의를 들으셨으면 좋겠습니다
질문이나 의문이 생기신다면
우선은 여러분의 코드를
GitHub 리포지터리에 있는 코드와 비교해 주십시오
그런데도 문제가 발생하면
Q&A에 질문을 남기셔도 좋습니다
그 전에 Q&A에 같은 질문이 있는지 확인해 주십시오
다른 수강생이 남긴 질문에
제가 이미 답을 달아 놓았을 수도 있습니다
도움이 되는 정보를 못 찾으셨다면
그 때는 질문을 남기신다면
답변을 해 드리겠습니다
Spring Security를 이용하여
웹 애플리케이션을
순조롭게 발전시키고 있습니다
감사드리며 다음 강의에서 뵙겠습니다
수고하셨습니다