[Spring]Spring Security를 이용한 로그인처리-(4)

윤재열·2022년 3월 30일
0

Spring

목록 보기
46/72
post-custom-banner

프로젝를 위한 JPA처리

  • 프로젝트에 스프링 시큐리티를 적용하기 위해서는 당연히 이에 맞는 데이터베이스 관련처리가 필요합니다.

  • 예제는 아이디 대신에 최근에 소셜 로그인에서 많이 사용하는 이메일을 아이디로 구성해서 회원을 처리합니다.

  • 회원정보는 다음과 같이 구성합니다.
    -이메일(ID)
    -패스워드
    -이름(닉네임)
    -소셜 가입 여부(소셜 로그인으로 회원 가입된 경우)
    -기타(등록일/수정일)

  • 일반적인 회원 관리와 가장 다른 점은 권한(Role)에 대한 처리입니다. 예제에서는 권한(ClubMemberRole)을 다음과 같이 구분해서 사용합니다.
    -USER : 일반 회원
    -MANAGER : 중간 관리 회원
    -ADMIN : 총괄 관리자

  • 회원의 권한은 한 가지만을 가지는 것이 정상적인 설계지만, 한명의 클럽 회원이 여러 개의 권한을 가진다는 전제로 프로젝트를 구성합니다.

  • ClubMember와 ClubMemberRole의 관계는 1:N 관계지만, 사실상 ClubMemberRole 자체가 핵심적인 역할을 하지는 못하기 때문에 별도의 엔티티 보다는 @ElementCollection이라는 것을 이용해서 별도의 PK없이 구성합니다.

  • 프로젝트 내에 entity 패키지를 구성하고 BaseEntity와 ClubMember라는 클래스를 생성합니다.

  • entity패키지에 ClubMemberRole라는 enum타입을 생성하고 정해진 권한들을 지정합니다.

public enum ClubMemberRole {
    USER, MANAGER, ADMIN
}
 //이 어노테이션은 지정된 속성이 컬렉션을 사용할 것이라는 의미이다.
    //Entity가 아닌 단순한 형태의 객체 집합을 정의하고 관리하는 방법이다.
    //한 테이블에서 연관된 다른 테이블에 대한 정보를 다루는데 One-To-Many관계를 다룬다

    //Fetch Type은 크게 Eager와 Lazy 두 가지의 전략이 있습니다. Eager 전략은 엔티티를 조회할 때,
    // 연관 관계에 있는 엔티티도 함께 가져오고,
    // 반대로 Lazy 전략은 연관 관계 엔티티를 참조할 때 그때서야
    // 가져오게 됩니다.
    @ElementCollection(fetch= FetchType.LAZY)
    @Builder.Default
    private Set<ClubMemberRole> roleSet=new HashSet<>();

    public void addMemberRole(ClubMemberRole clubMemberRole){
        roleSet.add(clubMemberRole);
    }
}
  • ClubMember의 데이터 중에서 패스워드는 반드시 암호화해서 데이터를 추가해야 하기 때문에 테스트 코드를 작성해서 100개의 계정을 생성합니다.
  • repository 패키지를 추가하고 ClubMemberRepository를 생성합니다.

  • 테스트 코드작성
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.zerock.club.entity.ClubMember;
import org.zerock.club.entity.ClubMemberRole;

import java.util.stream.IntStream;

@SpringBootTest
public class ClubMemberRepositoryTests {
    @Autowired
    private ClubMemberRepository clubMemberRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Test
    @DisplayName("100더미생성")
    void insertDummies(){
        //1-80까지는 USER만 지정,
        //81-90까지는 USER,MANAGER,
        ///91-100까지는 USER,MANAGER,ADMIN
        IntStream.rangeClosed(1,100).forEach(i->{
            ClubMember clubMember = ClubMember.builder()
                    .email("user"+i+"@naver.com")
                    .name("사용자"+i)
                    .fromSocial(false)
                    .password(passwordEncoder.encode("1111"))
                    .build();
            //default role
            clubMember.addMemberRole(ClubMemberRole.USER);
            
            if(i>80){
                clubMember.addMemberRole(ClubMemberRole.MANAGER);
            }
            if(i>90){
                clubMember.addMemberRole(ClubMemberRole.ADMIN);
            }
            clubMemberRepository.save(clubMember);

        });
    }
}

회원 데이터 조회 테스트

  • ClubMember 조회 시에는 이메일을 기준으로 조회하고, 일반 로그인 사용자 뒤에 추가되는 소셜 로그인 사용자를 구분하기 위해서 ClubMemberRepository에 별도의 메서드로 처리합니다.
import java.util.Optional;

public interface ClubMemberRepository extends JpaRepository<ClubMember,String> {

    //@EntityGraph는 연관 엔티티의 특정한 속성을 같이 로딩하도록 표시합니다.
    //기본적으로 JPA를 이용하는 경우에는 연관 관계의 FATCH 속성값은 LAZY로 지정합니다.
    //attributePaths : 로딩 설정을 변경하고 싶은 속성의 이름을 배열로 명시합니다.
    //type : @EntityGraph를 어떤 방식으로 적용할 것인지를 설정
    //FATCH 속성값은 attributePaths에 명시한 속성은 EAGER로 처리하고 나머지는 LAZY로 처리합니다.
    //LOAD 속성값은 attributePaths에 명시한 속성은 EAGER로 처리하고 나머지는 엔티티 클래스에 명시되거나 기본 방식으로 처리합니다.
    //EntityGraph를 이용해서 'left outer join'으로 ClubMemberRole이 처리될수 있도록 합니다.
    
    @EntityGraph(attributePaths = {"roleSet"},type = EntityGraph.EntityGraphType.LOAD)
    @Query("select m from ClubMember m where m.fromSocial = :social and m.email =:email")
   Optional<ClubMember> findByEmail(@Param("email") String email, @Param("social") boolean social);
    //Optional은 non-null 값을 가지고 있거나 안 가지고 있을 수 있는 컨테이너 오브젝트이다
    // 값이 존재하면 isPresent()는 true를 반환하고
    //값이 존재하지 않으면 false를 반환한다. 객체를 감싸고 그 안에 값이 있는지 없는지 유무를 판별하기 좋다.
}

```java
@Test
    @DisplayName("회원데이터 조회")
    public void testRead(){

        Optional<ClubMember> result = clubMemberRepository.findByEmail("user28@naver.com", false);

        if(result.isPresent()){
            System.out.println(result.get());
        }
    }
ClubMember(email=user28@naver.com, password=$2a$10$KIxhZJYQ8qA8Dl1ZD7fEs.SZMvwgSjG7Hs7oOoGPs5SCIbZXwYRwO, name=사용자28, fromSocial=false, roleSet=[USER])

profile
블로그 이전합니다! https://jyyoun1022.tistory.com/
post-custom-banner

0개의 댓글