지금으로서는 유저 정보(UserDetails)를
SQL 스크립트를 활용하여
데이터베이스에 수동으로 입력하고 있습니다
하지만 실제 상황에서는
이런 방법은 절대로 권장되지 않습니다
우리 엔드 유저들이 자신들의 자격 증명을
언제든지 직접 입력하여 등록이 가능하여야 합니다
그러므로 실제와 비슷한 시나리오를 위하여
새로운 비지니스 로직을 작성해
엔드 유저가 자신의 정보를 직접 입력하여
데이터베이스에 등록하고
같은 자격 증명을 이용하여
Spring Boot 웹 애플리케이션이나
그 웹 애플리케이션 내부에 존재하는
REST 서비스에 접근이 가능하도록 하여야 합니다
이렇게 하기 위한 방법에는 두 가지가 있습니다
하나는 인터페이스
즉 UserDetailsManager를 구현하는 것입니다
그리고 UserDetailsManager 인터페이스를
구현할 때에는
createUser, updateUser, deleteUser와 같은 메소드가 있어
비지니스 로직을 작성하기 위해서는 모두 오버라이드해야 합니다
하지만 이 방법은 사용하고 싶지 않습니다
이유는 Spring Security를
엔드 유저를 생성, 관리
삭제할 때마다 연관시킬 필요는 없기 때문입니다
Spring Security는
인증 과정에만 연관되었으면 좋겠습니다
그러므로 UserDetailsService에 존재하는
loadUserByUsername은 그냥 오버라이드해도 됩니다
나머지 연산에 대해서는
Spring Security 관련 인터페이스는
마찬가지로 구현하고 싶지 않습니다
엔드 유저에게 노출하여도 문제 없는
간단한 REST 서비스를 이용하여도
연산이 가능하기 때문입니다
그렇게 설정하면 엔드 유저는 UI 애플리케이션을 통해서든
Postman을 통해서든 직접 등록이 가능합니다
그리고 이는 개발자들에게도 약간의 유연성을 추가로 줍니다
앞으로 누군가가 여러분의 팀에 새로 들어온다면
그 개발자는
Spring Security 프레임워크에 대해 잘 알 필요가 없습니다
개발자는 엔드 유저 등록과
관리를 위하여
여러분이 설정한 REST 서비스만 보면 됩니다
그러기만 하면 코드 디버깅이나
개선이 가능합니다
그러므로 controller 패키지 내부에
새로운 제어기를 생성하려 합니다
클래스 명칭은 LoginController입니다
이 클래스의 위쪽에 우선
@RestController이라는 주석(어노테이션)을 입력합니다
새로운 REST 서비스를 만들어
엔드 유저가 직접 등록이 가능하도록 하기 위해서입니다
등록 과정에
어떤 일이 일어나야 하냐면
엔드 유저가 입력한 자격 증명을 받아
우리가 생성한 데이터베이스 표에 저장하고 싶습니다
같은 자격 증명을 엔드 유저가 사용하여
애플리케이션에 로그인하는 데 사용할 수 있을 것입니다
이를 위하여 저는 첫 번째로
CustomerRepository Bean을 이 제어기 클래스에 주입합니다
그래서 Autowired 주석(어노테이션)을 여기 두는 이유입니다
CustomerRepository 주석(어노테이션)을 자동 주입하였으면
등록 API 경로를 지원하는
새로운 메소드를 생성할 것입니다
새 메소드를 여기 만들겠습니다
이제 이 메소드를 사용하여
/register 경로가 있는 REST 서비스를 만들겠습니다
이제 이 내부에서 무엇을 할 것인지 설명하겠습니다
조금만 기다려 주십시오
필요한 클래스를 모두 내려받습니다
이 부분은 아주 간단합니다
데이터베이스에 새로운 기록을 저장할 것이므로
@PostMapping을 사용하여야 할 겁니다
그리고 지금 사용하고 싶은 API 경로는 /register입니다
메소드 명칭은 registerUser이며
이 메소드는 customer 객체를
RequestBody 내부에 받아들일 것입니다
같은 customer 객체를 이용하여
RequestBody는 이메일이 무엇인지
비밀번호와 역할 값은 무엇인지 판단할 수 있습니다
엔드 유저부터 이 정보를 받고 나서는
여기를 보시면
customerRepository 객체를 이용하여
CrudRepository 내부에 존재하는
메소드를 호출할 수 있습니다
이 메소드는
엔터티의 노출된 표현을 가져올 것입니다
즉 customer 객체를 가져다가
기록을 데이터베이스에 자동으로
INSERT 스크립트를 통해 저장할 것입니다
그게 바로 이 try 블록에서 일어나는 일입니다
고객 정보가 저장되었으면
이제 0보다 큰
기본키 값을 얻었을 것입니다
기본키가 0보다 크다면
기록에 성공하였다는 신호를 보내고
.body 내부에는 주어진 유저 정보가
성공적으로 생성되었다고 메세지를 출력합니다
하지만 이유를 알 수 없는 예외가 발생한다면
status 500으로 응답을 보내고
응답 메세지는
“이러이러한 이유 때문에 예외(exception)가 발생했다”라고 띄웁니다
지금 간단한 Java 메소드가
REST 서비스로 기능하는 중입니다
이해가 되셨길 바랍니다
이제 빌드를 해 줍니다
빌드가 완료되었으면 Postman을 열어
정보를 API 경로로 보내 보도록 합시다
여기가 제 Postman입니다
POST를 선택하도록 합니다
요청 URL은 http://
localhost입니다
포트 번호는? 8080입니다
그리고 경로는 무엇일까요?
경로 명칭은 register입니다
Body에서는
customer 객체가 정확히 표현되었으면 좋겠습니다
그러므로 raw를 선택합니다
이 드롭다운에서는 JSON을
입력 포맷으로 선택합니다
이제 Body에 JSON 요청을 붙여넣습니다
이게 바로 JSON 객체입니다
이 JSON 요청 내부에는 email
pwd 그리고 role 필드가
customer 객체의 명명 규칙에 따라 있습니다
여기의 customer 객체를 확인하면
여기에도 email, pwd, role이 있습니다
그러면 정상적으로 작동할 것입니다
여기에 중단점를 두겠습니다
이걸로 준비는 됐습니다
Send 버튼을 눌러 보겠습니다
지금 보이시듯이 403 Forbidden 에러가 뜹니다
저희는 Spring Security에게
이 경로에 보안을 적용할지 말지 알리지 않았기 때문입니다
이 시나리오에서는 permitAll을 입력합니다
유저가 처음으로 등록하는 상황이기 때문입니다
그러므로 이 경로를 보안하려 해도
아무런 의미가 없습니다
그런 이유로 이 API 경로에는 모든 접근을 허용하고
이제는 ProjectSecurityConfig 클래스로 왔습니다
여기에는 공지와 연락 API 외에도
우리가 개발한 새로운 API가 허용되도록 해야 합니다
신규 API 는/register입니다
그러므로 permitAll 메소드에 대해
이 메소드를 등록합니다
이걸로 변경사항을 저장한 다음
여기에 빌드를 합니다
이번에는 문제 없이 작동했으면 좋겠습니다
Postman으로 왔는데 서버가 재시동 중이라서
요청을 아직 보낼 수 없습니다
몇 초만 기다립시다
아직도 Forbidden 에러가 뜹니다
Spring Security 프레임워크에서
제공하는
CSRF 보안 때문입니다
Spring Security 프레임워크의 기본 설정은
어떤 POST 요청이든 간에
데이터베이스나 백엔드 내부의 데이터를
수정하고자 하는 것이면 막도록 되어 있기 때문입니다
그러므로 이와 같은 요청은
CSRF 공격을 막기 위해 모두 막을 것입니다
지금은 Spring Security 프레임워크의
CSRF 보안을 그냥 해제해 버리겠습니다
나중에 CSRF를 더 자세히 다룰 때는
CSRF 보안을 어떻게
웹 애플리케이션 내부에
제대로 적용할 수 있는지 알려 드리겠습니다
Spring Security의
CSRF 보안을 해제하기 위해서
해야 할 일은
이 authorizedHttpRequests 메소드 바로 앞에
csrf 메소드를 두는 것입니다
csrf 메소드가 호출되면
disable 메소드도 호출해야 합니다
이러면 SS CSRF 보안이 해제될 것입니다
그러면 변경 사항을 저장하고
빌드를 해 주겠습니다
지금은 CSRF가 무엇인지
왜 해제하고 있는 것이지 크게 고민하실 필요 없습니다
CSRF가 무엇인지와
어떻게 다루어야 하는지에 대해서는
이후 섹션에서 자세히 다루겠습니다
CSRF를 해제하였으니
이제 등록 API를
한 번 더 실행해 봅시다
제 Postman으로 왔습니다
그리고 Send 버튼을 누르겠습니다
이번에는 보이시듯이
브레이크포인트로 인해 실행이 정지되었습니다
그리고 customer 객체 내부에는
보면 이메일과 비밀번호를 얻었습니다
그러면 로그인을 다시 할 수 있도록
이 정보를 기억하여야 합니다
이 중단점을 재개하고
Postman의 반응을 봅시다
예외(exception)가 떴습니다
콘솔을 확인해 봐야겠습니다
콘솔을 보니 이 exception은
Spring Data JPA 프레임워크는
데이터베이스 서버와 협력하여
삽입하고자 하는 새로운 기록에 대한
시퀀스 값을 생성하는 데 실패해서 발생한 것 같습니다
이제 문제가 왜 발생하는지 알았습니다
Customer.java 클래스로 가면
제가 이 customer Entity 클래스에서 실수를 했습니다
그래서 예외(exception)가 발생하였습니다
제가 한 실수가 어떤 것인가 하면
id 필드 위에서 저는
Spring Data JPA가 시퀀스 수를
자동으로 기본키에 대하여 생성해야 한다고 명령했지만
어떻게 생성하는지는 설정하지 않았습니다
Spring Data JPA에는
이에 대한 접근 방법이 아주 많기 때문에
어떤 방법을 사용할지 알려 주어야 합니다
이 문제를 해결하기 위해서는
우선 이 주석(어노테이션)을 제거하겠습니다
그리고 새로운 정보를 언급합니다
이 내부에는
처음에는 전과 같은 주석(어노테이션)인
@GeneratedValue이며
Spring Data JPA가 사용할 방식은
AUTO입니다
이는 기본키 시퀀스 값이
Spring Data JPA에 의해 자동으로 생성되도록 한다는 뜻입니다
그런데 여기에 있는 정보는
native 타입의 생성기를 따라야 한다는 뜻입니다
제가 정의한 이 생성기는
주석(어노테이션) @GenericGenerator의
이 name 열에서
제가 준 native라는 값과
생성기 열에 대해서도 사용되고 있습니다
그러면 이 생성기의 strategy는
native로 설정된 것입니다
strategy가 native이라는 것은
새로운 시퀀스 수를 생성하는 일을
데이터베이스에게 맡긴다는 것입니다
Spring Data JPA는
시퀀스 값에 대해 생각할 필요가 없습니다
시퀀스 값은 백엔드 데이터베이스 서버가 맡습니다
데이터베이스 서버는 스스로
새로운 기록을
데이터베이스에 삽입하고 싶을 때
시퀀스 수를 생성할 것입니다
변경을 하였으니 빌드를 해 줍니다
이 두 주석(어노테이션)을 추가하였으니 이제 괜찮을 겁니다
이 두 개의 주석(어노테이션)에 대해 더 알고 싶으시다면
전에도 말씀드렸듯이 제 Spring 강좌를 추천드립니다
이제 무엇을 하고자 하느냐면
Postman으로 가서
요청을 한 번 더 보낼 것입니다
이번에는 성공적이기를 바라겠습니다
Send 버튼을 다시 한 번 클릭합니다
그리고 중단점을 해제합니다
이제 Postman을 확인해 봅시다
이번에는 요청이 성공하였으며
status는 201입니다
이것이 표시되는 메세지입니다
SELECT 쿼리를 데이터베이스 표에 대해
실행하여도 확인이 가능합니다
그리고 새로운 기록이 삽입되어 있습니다
sam@example.com
비밀번호 12345입니다
이제는 보안된 REST API에
이 자격 증명에 접근을 시도하여
단대단 프로세스가 제대로 작동하는지 빠르게 확인합시다
새로운 브라우저를 열었습니다
보안된 경로 중 하나인
myLoans에 접근을 시도합시다
자격 증명을 요청하고 있습니다
이번에는 이메일 sam@example.com과
비밀번호 12345를 입력합니다
로그인을 클릭하면
loadUserByUsername 내부에서 멈춰 있습니다
이 중단점을 해제하겠습니다
이제는 보이시듯이 성공적인 응답이 돌아옵니다
이 콘솔을 봄으로써 애플리케이션 내부에서
실행되고 있는 모든 SQL을 확인할 수도 있습니다
이건 처음에 기록을 삽입하기 위해 사용된
INSERT 쿼리이며 나중에는
SELECT 쿼리로 유저 정보를 반환했었습니다
SQL 선언들을 어떻게 볼 수 있는 것이냐면
application.properties 내부에
우리가 두 가지 프로퍼티를 언급하였기 때문입니다
두 개 모두 SQL에 관한 것입니다
application.properties를 열어 보면
이 두 개의 프로퍼티가
Spring Data JPA 프레임워크에서 생성되고 있는
SQL 선언을 가져올 수 있도록 하고 있습니다
이 강의와 이 섹션을 마무리하기 전에
우리가 구축한 EazyBankUserDetails를 이용한
시퀀스 흐름이자
UserDetailsService의 구현을
보여드리겠습니다
이게 시퀀스 흐름입니다
전에 다루었던 것과 비슷하지만
이번에는
DaoAuthenticationProvider가
우리만의 UserDetailsService 구현인
EazyBankUserDetails에 의존하고 있습니다
전에는 DaoAuthenticationProvider가
InMemory AuthenticationManager
또는 JdbcUserDetailsManager에 의존했습니다
하지만 이제 우리에게는 우리만의 UserDetailsService
구현이 있으므로 DaoAuthenticationProvider는
EazyBankUserDetails를 활용합니다
그 외의 구성 요소 즉
보안 필터들, ProviderManager
DaoAuthenticationProvider, PasswordEncoder
이들은 전에 다루었던 것과
거의 똑같은 역할을 수행합니다
우리가 사용하는 또 다른 시퀀스 흐름에도
마찬가지입니다
5단계와 6단계 사이를 보시면
DaoAuthenticationProvider는
EazyBankUserDetails를 호출합니다
이해가 되셨길 바랍니다
그리고 알려드리고 싶은
또 다른 중요한 사항은
저희가 이번 섹션
섹션 3에서 다루었던 모든 코드는
GitHub 리포지터리의
이 폴더 section3/springsecsection3에서 찾을 수 있습니다
이 폴더를 클릭하면
지금까지 다룬 모든 코드가 들어 있습니다
이걸로 웹 애플리케이션 내부에서
적용한 모든 개선 사항이
잘 이해되셨을 거라 믿습니다
지금 저희는 데이터베이스를 통하여 인증을 하고 있으며
신규 유저 등록도 가능합니다
이번 섹션에서는 진도가
잘 나간 것 같아서 만족스럽습니다
여러분도 강좌가 마음에 들었으면 좋겠습니다
잠깐 쉬고 즐겁게 프로그래밍 하다가 다시 돌아오시기 바랍니다
감사드리며 다음 강의에서 뵙겠습니다
수고하셨습니다