SEB_BE 62일차 - Spring Security 기본2

subimm_·2022년 11월 22일

코드스테이츠

목록 보기
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 등 영구 저장소가 될 수도 있다.
    UserDetailsManager 는 UserDetailsService를 상속하는 확장 인터페이스

  • 이 클래스와 같은 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) HelloAuthorityUtils 의 createAuthorities() 메서드로 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개의 댓글