반응형

SpringOauth2 를 이용해서 간편로그인 기능을 추가해주었다. 

간편로그인을 추가하고나니 간편로그인한 계정의 이메일과 현재 user테이블에있는 이메일이 중복되는 문제가 발생했다.

유저는 한명으로 유일한 값이어야될것같다는 생각이들어서 간편로그인 테이블을 하나 새로만들어주려고한다.

 

로직순서

  1. 간편로그인으로 로그인시도
  2. 간편로그인한 이메일이 oauth_user 테이블에 있는지 조회
  3. oauthUser 정보가 있다면
    • oauth_user테이블에서 연결된 user 데이터를 조회해서 로그인
  4. oauthUser  정보가 없다면
    • 간편로그인 이메일로 user데이터를 조회 
    • 조회된 user 가있다면 oauth_user entity를 만들고 해당 userEntity를 연결
    • 조회된 user 가 없다면 간편로그인정보로 userEntity를 만들고 해당 entity와 oauth_user를 연결

로직 flowchart

 

user 에 연결시킨 oauth_user 테이블


이제 생각해놓은 이 로직을 코드로 구현하면 된다.

 

Oauth2UserEntity

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "oauth_user")
public class Oauth2UserEntity{
	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;

    @OneToOne(cascade = CascadeType.PERSIST)
    @JoinColumn(name = "user_id")
    private UserEntity user;

    @Column(length = 200, nullable = false)
    private String email;
    
    @Column(length = 100, nullable = false)
    private String domain;
}

 

 

Oauth2UserRepository

public interface Oauth2UserRepository extends JpaRepository<Oauth2UserEntity,Long> {
    Optional<Oauth2UserEntity> findByEmail(String email);
    Optional<Oauth2UserEntity> findByEmailAndDomain(String email,String domain);
}

 

이렇게 새로생성한 테이블의 엔티티와 리포지토리를 생성해주었다.

@Component
@RequiredArgsConstructor
public class CustomOauth2UserService extends DefaultOAuth2UserService {

    private final UserRepository userRepository;
    private final Oauth2UserRepository oauth2UserRepository;

    @Override
    @Transactional
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {

        OAuth2User oAuth2User = super.loadUser(userRequest);

        String registrationId = userRequest.getClientRegistration().getRegistrationId();

        Oauth2Response oauth2Response;

        //간편로그인별 리스폰스 객체 만들기
        if (registrationId.equals("naver")) {
            oauth2Response = new NaverResponse(oAuth2User.getAttributes());
        } else if (registrationId.equals("google")) {
            oauth2Response = new GoogleResponse(oAuth2User.getAttributes());
        } else {
            throw new OAuth2AuthenticationException(new OAuth2Error("unsupported_registration_id"),
                    "Unsupported registration ID");
        }

        String username = oauth2Response.getName();
        String userEmail = oauth2Response.getEmail();

        /*

        * 1. oauth_user 테이블에서 이메일로 조회
        * 데이터가 있는경우 oauth_user 에서 연결된 유저테이블을 조회
        * oauth_uesr 테이블에서 조회된데이터가없는경우
            * 이메일로 user테이블을 조회
             -> 유저가 있는경우
                * 현재 response로 oauth_user 를저장하고 ,
                * 해당 user와 간편로그인 user를연결
            * 없는경우 userEntity를 등록
            * oauth_user 의 user 필드에 해당 userEntity를 넣어서 저장.
        * oauth2_user 테이블에서 조회된데이터가 있는경우
            * 조회된데이터의 user필드를 찾아서 리턴
            * 해당 user 를 userDto로 컨버팅해서 Oauth2Response로 보내준다.


        예외케이스 : oauth_user에도 같은 이메일이 들어갈수있다. ->  이메일과 , 로그인하는 도메인을 같이 저장
        */

        Optional<Oauth2UserEntity> byEmailOauthUser = oauth2UserRepository.findByEmailAndDomain(userEmail,registrationId);

        if (byEmailOauthUser.isEmpty()) { // 처음으로 간편로그인을하는경우 , oauth_user에 데이터가 없는경우
            // oauth_user 이메일을 가진 uesr가있는지 조회
            Optional<UserEntity> byEmail = userRepository.findByEmailAndRoleAndLoginMethod(userEmail, UserRole.ROLE_USER,
                    LoginMethod.valueOf(registrationId));

            // 있다면 해당 유저데이터를 oauth_user에 등록 없다면 userEntity를 생성해서 등록
            UserEntity userEntity = byEmail.orElse(UserEntity.builder()
                    .email(userEmail)
                    .nickName(username)
                    .role(UserRole.ROLE_USER)
                    .loginMethod(LoginMethod.valueOf(registrationId))
                    .build());

            // oauth_user에 로그인한 유저를 저장
            Oauth2UserEntity oauthUserEntity = Oauth2UserEntity.builder()
                    .user(userEntity)
                    .email(userEmail)
                    .domain(registrationId)
                    .build();

            oauth2UserRepository.save(oauthUserEntity);

            UserDto roleUser = UserDto.builder()
                    .username(username)
                    .name(oauth2Response.getName())
                    .email(userEmail)
                    .role("USER")
                    .build();

            return new CustomOauth2User(roleUser);
        } else {
            //간편로그인 계정이 등록되어있는경우 , 연결된 유저를 찾아와서 roleUser를 생성해서 리턴.
            Oauth2UserEntity oauth2UserEntity = byEmailOauthUser.get();
            UserEntity userEntity = oauth2UserEntity.getUser();

            UserDto roleUser = UserDto.builder()
                    .username(userEntity.getNickName())
                    .name(oauth2Response.getName())
                    .email(userEmail)
                    .role(String.valueOf(userEntity.getRole()))
                    .build();

            return new CustomOauth2User(roleUser);
        }
    }
}

 

이 코드가 oauth2간편로그인에 접근했을때 user의정보를 불러와서 처리하는부분의 코드이다. 

로그인한 데이터를 받아오고 로그인에 이용한 도메인에 따라서 response를 만들어준다. 

(도메인별로 보내주는 데이터와 필드의 이름이다르기때문에 각각 다르게 데이터를 처리해주어야한다.)

 

받은 데이터를 이용해서 간편로그인에서 데이터를 조회하고 간편로그인에 연결된 유저데이터를 가져오게된다. 

 

추가

이 로직에서는 이메일을 키값처럼 이용해서 각 테이블에서 조회를 하고있다. 

현재 로그인로직에서는 이메일을 입력받기는하지만 따로 메일을보내서 인증을 거치거나 하지는 않는다. 그래서 본인이 소유하지않은 이메일도 등록해서 회원가입을할수있고 만약 해당 이메일을 실제로보유하고있는사람이 간편로그인으로 연결한다면 연결이 될 문제가 있다. 가능하다면 다음에는 이메일 인증을 구현해보겠다. 

반응형

+ Recent posts