728x90
목적
로그인이 있는데 로그아웃이 없으면 섭섭함
함정이 있다면, Naver Developer에는 로그아웃을 지원하는 요청 가이드가 없음(자체 구현하라함)
* (필독) 이 글은 Spring Boot JPA환경에서 JWT를 이용한 인증방식의 Spring Security사용자를 위함입니다.
환경
네이버 로그인 구현 : https://radpro.tistory.com/675
레디스서버가 연동되어 있거나, RDBMS에 OAuth 2.0 제공사에서 주는 식별자(ID) 등의 회원정보가 저장이 되어있어야 함
로그인 한 유저의 JWT로 자체 서비스에 등록된 ID값을 획득할 수 있어야 함 (필자는 JwtExtractUtil을 구현해 사용중)
할 것 요약
핸들러 메소드 구현
요점: 파라미터를 구해서 -> HTTP요청으로 Naver 인증센터에 로그아웃 시키고 -> 자체 서비스 로그아웃 시킴
이때, try ~ catch로 로그아웃 성공/실패에 따른 처리를 분화시켜줘야 오류가 없다.
또한, 자체 서비스보다 Naver에 우선적으로 로그아웃을 시켜야 보안상 안전한 로직이 된다.
코드 스니펫
더보기
@GetMapping("/logout")
public ResponseEntity<?> naverLogout(HttpServletRequest request) {
// DI를 이용해 파라미터 및 객체를 구하는 구간
Long memberId = jwtExtractUtil.extractMemberIdFromJwt(request);
Member loginMember = memberService.findMember(memberId);
String accessToken = jwtExtractUtil.extractAccessTokenFromJwt(request);
Long expiration = jwtExtractUtil.getExpiration(accessToken);
// REST API 요청을 전송하기 위한 준비 구간
RestTemplate restTemplate = new RestTemplate(); // Http 요청을 보내기 위한 템플릿 클래스
HttpHeaders userHttpHeaders = new HttpHeaders(); // Http 요청을 위한 Headers
MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); // Http 요청을 위한 parameters를 설정해주기 위한 클래스
HttpEntity<MultiValueMap<String, String>> naverLogoutRequest = new HttpEntity<>(params, userHttpHeaders); // http 요청을 위한 엔티티 클래스 (Header와 Parans를 담아줌)
userHttpHeaders.add("Authorization", "Bearer " + loginMember.getOauthAccessToken());
userHttpHeaders.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
params.add("target_id_type", "user_id");
params.add("target_id", loginMember.getOauthId()); // 여기서 nullpointer발생
// Naver인증센터에 REST API 요청
try {
ResponseEntity<String> LogoutResponse = restTemplate.exchange(
"http://nid.naver.com/nidlogin.logout", // 네이버 developer에 따로 네이버 로그아웃이 없으므로 본 경로를 사용. 단, 이 경로를 사용할 경우 HTML 로그가 나옵니다.
HttpMethod.POST,
naverLogoutRequest,
String.class
);
System.out.println(LogoutResponse);
memberService.verifyMemberFromRedis(memberId, accessToken, expiration); // 자체 서비스 로그아웃 로직
} catch (Exception e) { throw new BusinessLogicException(ExceptionCode.NOT_FOUND); }
return new ResponseEntity<>("Success Logout: User", HttpStatus.OK);
}
비즈니스 로직 메소드 구현
Naver 인증센터에 요청한 로그아웃이 정상적으로 수행되었을 경우, 자체 서비스에서도 로그아웃 시키기 위한 로직.
Google, Kakao 로그아웃에서 구현한 코드를 재사용 한다.
원리 : 레디스 서버에 저장되어있던 RefreshToken을 제거하고, Timeout을 부여한 AccessToken을 저장하여 같은 AccessToken으로 요청이 들어올 때 Access를 거부하는 방식이다.
코드 스니펫
더보기
/**
* Redis에 저장된 RefreshToken을 제거 : RefreshToken의 key값이 RTKey + memberId로 되어 있기 때문에 존재하면 지우는 방식
* Redis에 들어온 Authorization으로부터 parsing한 AccessToken을 저장
* AccessToken을 key, Logout을 value, Expiration은 기존 만료시간 - 현재시간으로 저장하여 시간이 지나면 Redis에서 자동 delete 되게 구현
* @param memberId
* @param accessToken
* @param expiration
*/
public void verifyMemberFromRedis(Long memberId, String accessToken, Long expiration) {
// refreshToken 잘 있나 확인 (곧 지워질 refreshToken)
System.out.println(redisTemplate.opsForValue().get("RTKey"+memberId).toString());
if(redisTemplate.opsForValue().get("RTKey"+memberId)!=null) redisTemplate.delete("RTKey" + memberId);
redisTemplate.opsForValue().set(accessToken, "Logout", expiration, TimeUnit.MILLISECONDS);
// 로그아웃 후 AccessToken이 Redis에 잘 저장됬는지 확인
System.out.println(redisTemplate.opsForValue().get(accessToken).toString());
}
728x90
'Java & Spring > Spring' 카테고리의 다른 글
[OAuth2.0] 네이버 로그인 API 개정 (for SpringBoot JPA + MVC) - 진행중 (0) | 2023.06.14 |
---|---|
[OAuth2.0] Naver OAuth.20 API 요청 시 발생하는 권한 페이지 문제(진행중) (0) | 2023.05.17 |
[OAuth2.0] 네이버(Naver) 로그인 - SpringBoot JPA JWT 방식용 (0) | 2023.05.07 |
[Redis] (5) EC2에 Redis 적용하기 - Ubuntu 사용 (0) | 2023.02.15 |
[Logging] p6spy 커스터 마이징 (0) | 2023.02.15 |