SEB_BE 62일차 - Spring Security 기본2

subimm_·2022년 11월 22일
0

코드스테이츠

목록 보기
62/83

💡 오늘의 학습 목표

  • 데이터베이스 연동 Spring Seurity
  • Spring Security의 웹 요청 처리 흐름

📖 데이터베이스 연동을 통한 로그인 인증

  • Member 엔티티 클래스를 이용하여 회원의 인증 정보를 포함한 회원 정보를 데이터베이스 테이블에서 관리

✔ Custom UserDetailsService를 사용하는 방법

일반적으로 Spring Security에서는 인증을 시도하는 주체를 User 라고 부른다.
Principal은 User의 더 구체적인 정보를 의미. (Spring Security에서의 Username의미)

  • 샘플 애플리케이션에서 Member 엔티티 클래스가 로그인 인증 정보를 포함하는데 이 Member엔티티가 Spring Security의 User 정보를 포함함.

1. SecurityConfiguration의 설정 변경 및 추가
InMemory User를 위한 설정 제거

  • (1) frameOptions() 는 HTML 태그 중에서 <frame>이나 <iframe>, <object> 태그에서 페이지를 렌더링 할지의 여부를 결정하는 기능
    Spring Security에서는 Clickjacking 공격을 막기위해 기본적으로 기능이 활성화 되어 있으며 디폴트 값은 DENY 즉 위의 태그를 이용한 페이지 렌더링을 허용하지 않겠다는 의미
  • (1) 처럼 .frameOptions().sameOrigin() 을 호출하면 동일 출처로부터 들어오는 request만 페이지 렌더링 허용
    개발환경에서는 H2 웹 콘솔 정상 사용을 위해 (1)과 같이 설정

2. JavaConfiguration의 Bean 등록 변경

  • (1)과 같이 데이터베이스에 User의 정보를 저장하기 위해 MemberService 인터페이스의 구현 클래스를 DBMemberService로 변경
  • (1-1) MemberRepository 와 PasswordEncoder 객체를 DI 해준다.

3. DBMemberService 구현
User의 인증 정보를 데이터베이스에 저장하는 역할
Member 엔티티 클래스의 필드에 인증 정보를 담는 password 필드가 포함된다고 생각

  • (1) 생성자를 통해 MemberRepository와 PasswordEncoder Bean 객체를 DI 받는다.
  • (2) PasswordEncoder를 이용해 패스워드를 암호화
  • (3) 암호화 된 패스워드를 password 필드에 다시 할당

    패스워드의 암호화

    • 패스워드 같은 민감한 정보는 반드시 암호화 되어 저장되어야 한다.
      패스워드는 복호화 할 이유가 없기 때문에 단방향 암호화 방식으로 암호화 되어야 한다.

4. Custom UserDetailsService 구현
데이터베이스에서 조회한 User의 인증 정보를 기반으로 인증을 처리하는 클래스 구현

UserDetailsService

  • Spring Security에서 제공하는 컴포넌트 중 하나인 UserDetailsService는 User 정보를 로드하는 핵심 인터페이스.
    로드 = 인증에 필요한 User 정보를 어딘가에서 가지고 온다.
    어딘가 = 메모리가 될 수도, DB 등 영구 저장소가 될 수도 있다.
    UserDetailsManagerUserDetailsService를 상속하는 확장 인터페이스

  • 이 클래스와 같은 Custom UserDetailsService를 구현하기 위해서는 (1) 처럼 UserDetailsService 인터페이스를 구현해야 한다.
  • 위 클래스는 데이터베이스에서 User를 조회하고, 조회한 User의 권한 (Role) 정보를 생성하기 위해서 (2) 처럼 MemberRepository 와 HelloAuthorityUtils 클래스를 DI 받는다.
  • UserDetailsService 인터페이스를 implemnts 하는 구현 클래스는 (3) 처럼 loadUserByUsername(String username) 추상 메서드를 구현해야 한다.
  • (4) HelloAuthorityUtils 를 이용해서 데이터베이스에서 조회한 회원 이메일 정보를 이용해 Role 기반의 권한 정보(GrantedAuthority) 컬렉션 생성.
  • 데이터베이스에서 조회한 인증 정보와 (4) 에서 생성한 권한 정보를 Spring Security에 제공해주어야 하며, (5) 에서 UserDetails 인터페이스 구현체인 User 클래스의 객체를 통해서 제공한다.
    • (5)와 같이 데이터베이스에서 조회한 User 클래스의 객체를 리턴하면 Spring Security가 이 정보를 이용해 인증 절차 수행.
      데이터베이스에서 User의 인증 정보만 Spring Security에게 넘겨주고, 인증 처리는 Spring Security가 대신해 준다.
      • UserDetails
        UserDetailsService에 의해 로드되어 인증을 위해 사용되는 핵심 User 정보를 표현하는 인터페이스.
        UserDetails 인터페이스의 구현체는 Spring Security에서 보안 정보 제공을 목적으로 직접 사용x, Authentication 객체로 캡슐화 되어 제공

HelloAuthorityUtils
HelloUserDetailsService 에서 Role 기반의 User 권한을 생성하기 위해 사용

  • (1) application.yml에 추가한 프로퍼티를 가져오는 표현식
    @Value("${프로퍼티 경로}")
    미리 정의한 관리자 권한을 가질 수 있는 이메일 주소를 불러오고 있음.
    • application.yml 파일에 정의한 관리자용 이메일 주소는 회원 등록 시, 특정 이메일 주소에 관리자 권한을 부여할 수 있는지 여부를 결정하기 위해 사용

  • (2) Spring Security에서 지원하는 AuthorityUtils 클래스를 이용해서 관리자용 권한 목록을 List<GrantedAuthority> 객체로 미리 생성 ( 일반 사용자의 권한까지 추가 포함 )
  • (3) 일반 사용 권한 목록을 객체로 미리 생성
  • (4) 파라미터로 전달 받은 이메일 주소가 yml 파일에서 가져온 관리자용 이메일 주소와 동일하다면 관리자용 권한인 List<GrantedAuthority> ADMIN_ROLES 를 리턴

    실무에서는 회원 가입 시 관리자 등록을 위한 추가적인 인증 절차가 있다.

5. H2 웹 콘솔에서 등록한 회원 정보 확인 및 로그인 인증 테스트

  • 애플리케이션 실행 후 회원가입으로 회원 등록 -> H2 콘솔 확인
    • 비밀번호가 암호화되어 저장된 것을 볼 수 있다.

📜 코드 개선

6. Custom UserDetails 구현
✔ 개선된 HelloUserDetailsService(V2)

  • 기존 loadUserByUsername() 의 리턴값을 (1)과 같이 Custom UserDetails 클래스의 생성자로 findMember를 전달하도록 개선

  • 기존 loadUserByUsername() 메서드 내부에서 User의 권한 정보를 생성하는 Collection<? extends GrantedAuthority> authorities = authorityUtils.createAuthorities(findMember); 코드는 (2) 의 클래스 내부로 포함됨.

  • (2) HelloUserDetails 클래스는 UserDetails 인터페이스 구현하며 Member 엔티티 클래스 상속

    🔍 데이터베이스에서 조회한 회원 정보를 Spring Security의 User 정보로 변환하는 과정과 User의 권한 정보를 생성하는 과정을 캡슐화 할 수있다.
    또한, HelloUserDetails 클래스는 Member 엔티티클래스를 상속하고 있기 때문에 HelloUserDetails를 리턴받아 사용하는 측에서는 두 개 클래스 객체를 모두 캐스팅해서 사용 가능하다는 장점이 있다.

  • (2-3) HelloAuthorityUtilscreateAuthorities() 메서드로 User의 권한 정보 생성 (기존과 달리 HelloUserDetails 클래스 내부에서 사용되도록 캡슐화됨)

  • (2-4) Spring Security에서 인식 가능한 username을 Member 클래스의 email 주소로 채운다.getUsername()의 리턴 값은 null일 수 없다.

7. User의 Role을 DB에서 관리하기

  • 현재 User의 권한 정보는 데이터베이스에서 조회한 User 정보를 기준으로 코드상에서 조건에 맞게 생성하고 있다 -> 권한 정보를 데이터베이스에서 관리하도록 코드 수정

    • User의 권한 정보를 저장하기 위한 테이블 생성
    • 회원 가입 시, User의 권한 정보(Role)를 데이터베이스에 저장하는 작업
    • 로그인 인증 시, User의 권한 정보를 데이터베이스에서 조회하는 작업

✔ User의 권한 정보 테이블 생성

JPA를 이용하여 User와 User의 권한 정보 간의 연관 관계 맺기

  • Member 엔티티 클래스에 추가
  • (1) 과 같이 List, Set 같은 컬렉션 타입의 필드는 @ElementCollection 애너테이션 추가하면 User 권한 정보와 관련된 별도의 엔티티 클래스를 생성하지 않아도 간단하게 매핑 처리 가능

✔ 회원 가입 시, User의 권한 정보(Role)를 데이터베이스에 저장 MEMBER_ROLES 테이블

  • DBMemberService 클래스
  • (1) 에서 회원의 권한 정보(List<String>roles) 생성한 뒤 member 객체에 넘겨주고 있다.

  • HelloAuthorityUtils
  • (1) 파라미터로 전달 된 이메일 주소가 application.yml 파일의 mail.address.admin 프로퍼티에 정의된 주소와 동일하면 관리자 Role 목록을 리턴하고 그 외는 일반 사용자 목록을 리턴

✔ 로그인 인증 시, User의 권한 정보를 데이터베이스에서 조회하는 작업

  • 개선된 HelloUserDetailsService(V3)
  • (1) HelloUserDetails가 상속하고 있는 Member(extend Member) 에 데이터베이스에서 조회한 List<String> roles 를 전달
  • (2) 다시 Member에 전달한 Role 정보를 authorityUtils.createAuthorities() 메서드의 파라미터로 전달해서 권한 목록 (List<GrantedAuthority>) 생성

  • HelloAuthorityUtils
  • 단순히 데이터베이스에서 가지고 온 Role 목록(List<String>roles)을 그대로 이용해서 권한 목록(authorities) 를 만든다.
    • (2)와 같이 SimpleGrantedAuthority 객체를 생성할 때 생성자 파라미터로 넘겨 주는 값이 ROLE_USER 와 같은 형태로 넘겨주어야 한다.
profile
코린이의 공부 일지

0개의 댓글