Spring boot/Spring Security

Spring Security - JPA 연동하기

metamong-data 2024. 7. 15. 17:30
728x90
반응형
  • InMemoryUser 방식은 문제점이 존재하기때문에 DATABASE와 연동하는 방법을 사용한다.
  • 방법이 다양하지만 그중에서도 JPA와 연동하는 방법을 사용한다.

의존성 추가하기

  • JPA 의존성을 추가하고, DATABASE는 h2 를 사용하도록 한다.

!https://blog.kakaocdn.net/dna/c6uds7/btqx8MbfeK7/AAAAAAAAAAAAAAAAAAAAAP8x5C5CLUYtSbhhobf3h_slhgo3kQTzpPFmp4bYf6-M/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1751295599&allow_ip=&allow_referer=&signature=WZw2Sdd%2BzkYIw%2BKx%2FJ4gr32bunk%3D

Account

  • User 정보에 해당하는 Account Entity 를 생성한다.
  • username은 유일한 값이기 때문에 Unique 제약조건을 걸어준다.

!https://blog.kakaocdn.net/dna/NZVmI/btqx9zvwsWl/AAAAAAAAAAAAAAAAAAAAAPtpihmbFzxUg7w0cJp7OndrTBo0B1DQS4ifzkPN2R4_/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1751295599&allow_ip=&allow_referer=&signature=wOmfFJfD3%2FoIdyonvF2lkTMtr%2B8%3D

  • JpaRepository 를 상속받는 AccountRepository 를 생성해준다.
  • 기본적인 CRUD, Paging 관련 구현이 모두 끝난다.
  • 또한 추가적으로 username으로 조회를 해야하기때문에 username으로 조회하는 namedQuery Method를 정의해준다.

!https://blog.kakaocdn.net/dna/XY0Zs/btqx8UNE5Cu/AAAAAAAAAAAAAAAAAAAAAMsx6fgxplfLo0MFbadxZCM_q2YYeXG5Zng5Eg8_z5_0/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1751295599&allow_ip=&allow_referer=&signature=TFwpWp7HKgXb7rzaSk7R5DMaziI%3D

UserDetailsService

  • Spring Security 에서 User 정보를 Database와 연동하여 가져오는 역할을 담당하는 인터페이스 이다.
  • 이에 대한 구현체는 NoSQL, RDBMS 등 어떤 방식으로 구현을 하던 제약사항이 없다.
  • 그저 Username에 해당하는 User를 조회해 UserDetails Type으로 리턴해주는 역할만 한다.
  • 객체지향 원칙중 하나인 단일 책임의 원칙에 의하면 하나의 역할만 하는것이 맞지만 UserDetailsService의 역할도 AccountService의 역할중 하나 이기때문에 UserDetailsService의 구현체로 사용한다.
  • loadUserByUsername(String username) 의 구현
    • username에 해당하는 유저정보를 DB로 부터 읽어온다.
    • 해당하는 유저가없다면 UsernameNotFoundException 예외를 발생시킨다.
    • 존재한다면 UserDetails 를 구현하는 User Type으로 Return 해준다
      • 이전에는 User라는 클래스를 제공해 주지않아 이를 연결해주는 Adapter 클래스를 많이 사용하였다.
      • 하지만 상황에따라 Adapter 클래스를 사용할때도 존재한다.

!https://blog.kakaocdn.net/dna/dsl5hv/btqx8A3d5Ms/AAAAAAAAAAAAAAAAAAAAAFzOmhvWV0uH7tDjflI8nSc5DQyLmvPOZumEGxwCcEHE/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1751295599&allow_ip=&allow_referer=&signature=%2F%2F%2BSZLkmdcjzhkyjYVCnsWGMdSQ%3D

SecurityConfig

  • 기존의 InMemory 설정은 더이상 필요가 없어졌기때문에 해당 설정은 다음과 같이 제거해준다.

!https://blog.kakaocdn.net/dna/xYaZQ/btqx8pngi0i/AAAAAAAAAAAAAAAAAAAAAADrALaMsKPu1XPYTNz4gfGjQubCJbcetgkU8S1PGysK/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1751295599&allow_ip=&allow_referer=&signature=015LMmqNCndesSE9b%2FD3CpfRnyA%3D

문제점

  • 현재는 애플리케이션을 실행하고나면 User를 생성할 방법이 없다.
  • 따라서 편의상 Account를 생성할수 있도록 AccountController클래스를 생성해야한다.

AccountController

  • PathVariable로 role/username/password를 받아 유저를 생성하고, 생성된 유저를 반환해주는 RestController를 구현한다.
  • 하지만 이 방법은 절대 사용해선 안된다.
  • 편의상 구현한 방법이지 절대 사용해선 안되는 방법이다.

!https://blog.kakaocdn.net/dna/bzInkT/btqx8LwF7dn/AAAAAAAAAAAAAAAAAAAAAF7fCuTD5wdKzN1fdHuJ6mYceOj_yQ87Z3ZAd144NQ2v/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1751295599&allow_ip=&allow_referer=&signature=pGpRuRplUr6A7SYvOtMq9pqCBtA%3D

또 다른 문제점

  • 유저를 생성할수 있도록 AccountController를 생성해주었는데 무엇이 문제일까 ?
  • 바로 Account를 생성하는 url인 /account/** 에 접근하지 못한다.
  • Security 설정에 의해 현재 상태에서는 인증을 할수 없기때문...

다음과 같이 SecurityConfig에서 /account/** 를 허용해준다.

!https://blog.kakaocdn.net/dna/CxBck/btqx9yXF1Kw/AAAAAAAAAAAAAAAAAAAAAJmZqMGNNjb-yXf3tvA0l_v21CzWwxrqCorQfwsKRrSW/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1751295599&allow_ip=&allow_referer=&signature=kL3O4OoAl5bL28lCf47qJbV725k%3D

유저 생성해보기

!https://blog.kakaocdn.net/dna/biK9Yy/btqx8S99SNU/AAAAAAAAAAAAAAAAAAAAALuzDgLTmJ1g2NSBeBTD4N8Lu8awgVRIadyLbcZY7jm9/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1751295599&allow_ip=&allow_referer=&signature=uFl2fthJxkseDJtNW44MYwd%2BpTw%3D

생성된 유저로 접근하기

위와 같이 생성된 유저정보를 활용하여 접근을 해보도록 하자

  • /dashboard 로 접근하면 시큐리티 기본 로그인폼이 나타나고, user/1234로 로그인을 시도하면 로그인이 되지않는다.
  • 다음과 같은 예외가 발생한다.
    • 이전에서 설명한 {noop}1234 형식의 패스워드가 아니기때문에 발생한 문제이다.

!https://blog.kakaocdn.net/dna/b30vbk/btqx8ohB0tE/AAAAAAAAAAAAAAAAAAAAAFkm42a6MxFJIHFRo7JnVwSyhc674rkZkU1fYGcZDS-q/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1751295599&allow_ip=&allow_referer=&signature=4u3DrFdaLG1lNdBDW8PMNONRoGA%3D

해결방법

  • 먼저 AccountController의 코드를 수정한다.
  • 패스워드를 인코딩하는등의 로직은 Service 계층에 있는것이 맞기때문에 AccountService로 의존성을 교체해준다.
  • 또한 AccountService의 createAccount Method로 유저를 생성하도록 변경한다.
  • AccountController

!https://blog.kakaocdn.net/dna/dSIYtp/btqx7JM5vGj/AAAAAAAAAAAAAAAAAAAAAGROewV1dK-sHcSZ8O2OdaHN_Wa1JRIHNW646ms17TjK/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1751295599&allow_ip=&allow_referer=&signature=zV8SsVF%2F0QQnSCd7r%2FzjbvFC8Zk%3D

  • AccountService의 createAccount Method이다.
  • 생성할 Account 를 받아 password Encoding을 진행한다.
    • {noop}1234 의 형식
  • AccountService

!https://blog.kakaocdn.net/dna/CXckx/btqx8K5Cfjn/AAAAAAAAAAAAAAAAAAAAAFmai8AmKADfrReKmY3d_2XP4jgwsr1ERcEFYxygPFS7/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1751295599&allow_ip=&allow_referer=&signature=8TEPcOOdT0PZT%2FZf3vgxRJLMnA4%3D

  • 정상적인 passwordEncoder 를 사용한 방법은 아니지만 패스워드를 평문으로 저장하기때문에 굳이 passwordEncoder를 사용하지않고,
  • Security에서 인식하도록 형식만 맞춰주도록 구현한다.
  • passwordEncoding 로직은 Service 계층에 존재할수도 있지만. Account Domain과 관련된 로직이기 때문에 Account Entity 클래스에 정의해 주었다.
  • Account

!https://blog.kakaocdn.net/dna/buzpej/btqx8BgHVRF/AAAAAAAAAAAAAAAAAAAAADTzcNgXPolucjcaSxZJvpJwHRJIrBXC-SGuH-utNigH/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1751295599&allow_ip=&allow_referer=&signature=6NVHznqtKWF5ci6TieSihPtwGwo%3D

  • 애플리케이션 을 재기동 하고, 유저를 생성한뒤 /dashboard로 접근 해보자.다음과 같이 유저가 생성되며, 정상적으로 /dashboard로 접근이 가능해진다.

!https://blog.kakaocdn.net/dna/dUsYB5/btqx8LpWjH6/AAAAAAAAAAAAAAAAAAAAAITYCkXDQpBCxrSen3z_DFZOvFCEIUDqSYNuWM91_6In/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1751295599&allow_ip=&allow_referer=&signature=GvSe8UkR4sBwbQ%2BZXhGUdrYX2pA%3D

SecurityConfig 살펴보기

  • 우리는 UserDetailsService의 구현체인 AccountService를 구현했지만 이를 Spring Security에 설정하지 않았는데도 AccountService를 사용하고있다.
  • 다음과 같이 명시적으로 UserDetailsService를 사용하라고 명시해 주어야 한다.
  • 하지만 UserDetailsService 타입의 Bean이 등록되어 있다면 이런 과정을 생략할 수 있다.
  • 자동적으로 userDetailsService로 설정한다.
728x90

'Spring boot > Spring Security' 카테고리의 다른 글

Authorize HttpServletRequests  (1) 2024.07.17
Password Storage  (0) 2024.07.17
Spring boot 3.x 이후 설정 코드  (0) 2024.07.16
BCryptPasswordEncoder란?  (0) 2024.07.15