본문 바로가기
Backend/Spring Boot

[OAuth2] 04. GitHub 회원 탈퇴 구현하기(Spring boot)

by 김파치 2023. 10. 24.

정말 너무너무너무너무너무 고생했던 github 회원 탈퇴 구현... 따흑...

 

어려워서가 아니고 github API docs가 불친절해서임... 아무튼 그럼....

(반박시 님말이 맞음)

 

포스트맨으로 어째저째 성공하고 그걸 RestTemplate으로 옮기는데도 계속 에러나서 넘 슬펐다ㅜㅜ

 

근데 이거 구글링해도 잘 안나왔다구,,,

 

다들 curl로 하거나 javascript로 했단말이야... 스프링부트는 Oauth2 쓴건 하나 봤는데 RestTemplate은 못봤움,,

 

 

회원 탈퇴 로직은 3단계 정도로 구분하였다.

 

1. github API로 재로그인

 

: 2번의 github app authorization을 삭제할 때 github에서 발행한 access token이 필요하다.

 

하지만 우리 서비스는 자체 jwt token을 사용하기 때문에 github API 재로그인을 해서 access token을 받아와야한다.

 

귀찮지 않은가 했는데 사용자 재검증 용도로도 사용할 수 있을 것 같아서 재로그인을 하기로 했다!

 

 

2. github app authorization을 delete한다.

 

: 로그인을 하면 우리가 생성한 github app의 authorization을 받게 된다.

 

회원 탈퇴를 하게 된다면 authorization을 delete 해줘야한다..! (이게.. 진짜 힘들었다...하....)

 

github에서 app authorization을 확인하는 방법은 다음과 같다.

 

먼저 프로필을 클릭해서 settings에 들어간당

 

 

 

 

그리고 스크롤을 내려서 Integrations - Applications를 클릭한다

 

 

 

두번째에 있는 Authorized GitHub Apps를 클릭하고 깃허브 비밀번호를 다시 입력하면 다음과 같이 내가 authorize한 github app들이 뜬다.

 

Revoke를 누르면 권한을 삭제할 수 있다.

 

이 과정을 api로 구현하는 것이 2번째 단계이다!

 

 

 

 

 

3. db에서 사용자 정보 및 프로필 사진을 삭제한다.

 

: 그리고 마지막으로 사용자 정보 및 프로필 사진을 db에서 삭제시켰다!

 

 

 

 

이번엔 컨트롤러부터 시작해봅시다아

 

스따뚜 - ヾ(•ω•`)o

 

 

1. UserController.java

 

 

회원 탈퇴는 사용자가 로그인이 되어있는 상태여야하니까 Oauth2Controller가 아닌 UserController에 API를 만들었다.

 

코드에 각 단계별로 주석을 달았으니 참고하면 될 것 같다!

 

 

@DeleteMapping
@ApiOperation(value = "사용자 탈퇴", notes = "사용자 탈퇴 API")
@ApiResponses(value = {@ApiResponse(code = 400, message = "파일 저장 에러"),
    @ApiResponse(code = 401, message = "access token 오류"),
    @ApiResponse(code = 403, message = "존재하지 않는 사용자"),
    @ApiResponse(code = 500, message = "내부 오류")})
ResponseEntity<String> deleteUser(@RequestParam String code, @RequestHeader String Authorization) {
    log.info("controller - deleteUser :: 사용자 탈퇴 진입");
    try {
        // 사용자 정보 받아오기
        UserDto userDto = jwtService.getUser(Authorization)
            .orElseThrow(() -> new NoSuchElementException("getUserInfo :: 존재하지 않는 사용자입니다."));

        // github token 받아오기
        Map<String, String> tokens = userService.githubToken(code);
        String githubAccessToken = tokens.get("access_token");

        // 깃허브 authorization 삭제하기
        userService.unlink(githubAccessToken);

        // 사용자 정보 db/서버에서 삭제하기
        userService.deleteUser(userDto);

        return new ResponseEntity<>("사용자 탈퇴 성공!", HttpStatus.OK);
    } catch (NoSuchFieldException e) {
        log.error(e.getMessage());
        return new ResponseEntity<>("access token 오류", HttpStatus.UNAUTHORIZED);
    } catch (NoSuchElementException e) {
        log.error(e.getMessage());
        return new ResponseEntity<>("존재하지 않는 사용자", HttpStatus.FORBIDDEN);
    } catch (IOException e) {
        log.error(e.getMessage());
        return new ResponseEntity<>("파일 저장 에러", HttpStatus.BAD_REQUEST);
    } catch (Exception e) {
        log.error(e.getMessage());
        return new ResponseEntity<>("사용자 탈퇴 실패..", HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

 

 

 

2. UserServiceImpl.java

 

 

UserService interface를 구현한 UserServiceImpl이다. 

 

인터페이스는 생략...! 그냥 똑같음..!

 

 

UserServiceImpl - githubToken() : Oauth2Service의 githubToken을 그대로 사용하였다!

 

 

@Override
public Map<String, String> githubToken(String code) throws Exception {
    return oauth2Service.githubToken(code);
}

 

 

 

UserServiceImpl - unlink() : 마찬가지로 Oauth2Service의 unlink를 그대로 사용하였다.

githubToken으로 받아온 access token을 매개변수로 받아왔다!

 

 

@Override
public Boolean unlink(String oauthAccessToken) throws Exception {
    return oauth2Service.unlink(oauthAccessToken);
}

 

 

Oauth2Service - githubToken(), unlink()  : 전체 코드는 이전 포스팅을 참고하길 바란댜...!

 

 

 

[OAuth2] 03. GitHub 로그인 구현하기(Spring boot) (2) GitHub API로 token, 사용자 정보 불러오기

시작하기 전에 대략적인 폴더 구조는 다음과 같다. 여기에 config 파일까지 하면 로그인 완성이다! 먼저 프론트에서 github api를 사용해서 로그인을 한 후, github에서 redirect-url로 보내준 code를 백엔

bread-diary.tistory.com

 

 

authorization을 delete하는 api 문서는 아래 링크의 "Delete an app authorization"을 참고하면 된다.

 

https://docs.github.com/en/rest/apps/oauth-applications?apiVersion=2022-11-28#delete-an-app-authorization

 

OAuth Authorizations - GitHub Docs

Status: 200 { "id": 1, "url": "https://api.github.com/authorizations/1", "scopes": [ "public_repo", "user" ], "token": "ghu_16C7e42F292c6912E7710c838347Ae178B4a", "token_last_eight": "Ae178B4a", "hashed_token": "25f94a2a5c7fbaf499c665bc73d67c1c87e496da8985

docs.github.com

 

 

delete 요청 api는 https://api.github.com/applications/클라이언트 아이디/grant 이다. 

 

헤더에는 basic auth로 client와 secret key를, 바디에는 깃허브 access token을 넣어줘야한다.

 

 

먼저 HttpHeaders를 선언하고 basic auth로 Client Id와 Secret key를 넣어준다.

 

headers.setBasicAuth()에 client id와 secret key를 추가하면 알아서 세팅된다.

 

그리고 content type을 application_json으로 세팅해줬다.

 

 

JSONObject 객체를 선언해서 access_token으로 깃허브 access token을 넣어줬다.

 

HttpEntity에 JSONObject와 헤더를 넣는다.

 

원래는 MultiValueMap을 썼었는데 깃허브 api에서 JSON을 읽지 못한다는 에러가 계속 나서 찾아보다가

JSONObject로 바꿨더니 바로 성공했다...!

 

RestTemplate으로 delete 요청을 보내면 권한 삭제 완료!

 

성공하면 204를 return하기 때문에 response.getStatusCode().is2xxSuccessful()을 return해줬다.

 

 

private String API_URL = "https://api.github.com";

public Boolean unlink(String oauthAccessToken) throws Exception {
    log.info("service - unlink :: github authorization 연결 끊기 진입");
    RestTemplate restTemplate = new RestTemplate();

    String deleteUrl = API_URL + "/applications/" + CLIENT_ID + "/grant";

    HttpHeaders headers = new HttpHeaders();
    headers.setBasicAuth(CLIENT_ID, CLIENT_SECRET);
    headers.setContentType(MediaType.APPLICATION_JSON);

    JSONObject requestBody = new JSONObject();
    requestBody.put("access_token", oauthAccessToken);

    HttpEntity<String> entity = new HttpEntity<>(requestBody.toString(), headers);

    ResponseEntity<Object> response = restTemplate.exchange(
        deleteUrl,
        HttpMethod.DELETE,
        entity,
        Object.class
    );
    return response.getStatusCode().is2xxSuccessful();
}

 

 

 

UserServiceImpl - deleteUser() : 사용자 정보와 프로필 사진을 db에서 지우는 코드이다.

 

먼저 사용자 db에서 사용자를 삭제한다.

 

그리고 서버와 db에서 프로필 사진을 삭제한다.

 

 

 

@Override
public void deleteUser(UserDto userDto) throws Exception {
    log.info("service - deleteUser :: 사용자 db/서버 삭제 진입");
    // 프로필 사진 받아오기
    ProfileDto profileDto = userDto.getProfileDto();

    // 사용자 db에서 삭제
    userRepository.delete(userDto);

    // 서버/db에서 프로필 사진 삭제
    if(profileDto != null && profileDto.getId() != DEFAULT_IMG_ID) {
        profileService.deleteProfile(profileDto);
    }
}

 

 

ProfileServiceImpl - deleteProfile() : 서버와 db에서 프로필 사진 정보를 지우는 코드이다.

 

이건 설명 패쓰...!

 

사진 관련해서는 따로 포스팅을 할 예정이당 :-)

 

 

@Override
public void deleteProfile(ProfileDto profileDto) throws Exception {
    // 서버에 저장된 이미지 파일 삭제
    String prevFilePath = profileDto.getFilePath();
    File prevFile = new File(prevFilePath);
    if (prevFile.delete()) {
        log.info("EC2 :: 프로필 사진 삭제 성공!");
    } else {
        log.error("EC2 :: 프로필 사진 삭제 실패..");
    }

    // db에 저장된 이미지 삭제
    profileRepository.deleteById(profileDto.getId());
}

 

 

그럼 깃허브 api 로그아웃은 끝이다~!~!~!~!~!

 

워후~!~!~!~!