-
OAuth - 구글 로그인(구글 로그인 및 자동 회원가입)스프링/스프링 시큐리티 2024. 1. 10. 00:11
시큐리티 세션에 들어갈 수 있는 객체는 Authentication 객체이다.
Authentication이 담을 수 있는 타입은 OAuth2User와 UserDetails 타입이다.
저 2개의 타입은 User 객체를 가지고 있지 않기 때문에 PrincipalDetials라는 타입을 만들어서 UserDetails를 implement 시켰는데, 여기에 OAuth2User까지 implement한다는 내용이 이전 포스팅이었다.
PrincipalDetails.class
@Data public class PrincipalDetails implements UserDetails, OAuth2User { private User user; private Map<String, Object> attributes; // 일반 로그인 public PrincipalDetails(User user) { this.user = user; } // OAuth 로그인 public PrincipalDetails(User user, Map<String, Object> attributes) { this.user = user; this.attributes = attributes; } ...(생략) @Override public Map<String, Object> getAttributes() { return attributes; } @Override public String getName() { return null; // 어차피 잘 사용 안하니 null } }
기존에 있던 생성자는 로컬 로그인의 생성자이고, 두 번째 생성자를 만들어줬다. user와 attributes를 파라미터로 받는 생성자이다. 오버라이딩 하는 함수들은 getAttributes()는 attributes를 반환해주며 getName()은 잘 사용 안해서 그냥 null로 해뒀다.
로컬 로그인 유저 : user만 가지고 있음
구글 로그인 유저 : user, attributes를 가지고 있음
PrincipalOauth2UserService.class
@Service public class PrincipalOauth2UserService extends DefaultOAuth2UserService { @Autowired private BCryptPasswordEncoder bCryptPasswordEncoder; @Autowired private UserRepository userRepository; // 구글에서 받은 userRequest 데이터를 후처리하는 함수 @Override public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { System.out.println("getClientRegistration : " + userRequest.getClientRegistration()); // registrationId로 어떤 OAuth로 로그인 했는지 확인 가능 System.out.println("getAccessToken : " + userRequest.getAccessToken()); OAuth2User oAuth2User = super.loadUser(userRequest); System.out.println("getAttributes() : " + oAuth2User.getAttributes()); // 구글 로그인 버튼 클릭 -> 구글 로그인 창 -> 로그인 완료 -> code를 리턴 -> OAuth-client 라이브러리가 코드를 통해 AccessToken 요청 // Token으로 부터 받아온 userRequest 정보로 회원 프로필을 받아야 하는데, 이때 사용되는 함수가 loadUser 함수 // 회원가입 진행 String provider = userRequest.getClientRegistration().getRegistrationId(); // google String providerId = oAuth2User.getAttribute("sub"); String username = provider+"_"+providerId; String password = bCryptPasswordEncoder.encode("password"); // OAuth 로그인을 하면 password가 필요없긴 함 String email = oAuth2User.getAttribute("email"); String role = "ROLE_USER"; User userEntity = userRepository.findByUsername(username); if (userEntity == null) { userEntity = User.builder() .username(username) .password(password) .email(email) .role(role) .provider(provider) .providerId(providerId) .build(); userRepository.save(userEntity); } else { // 이미 있으면 save를 안함 } return new PrincipalDetails(userEntity, oAuth2User.getAttributes()); } }
PrincipalOauth2UserService에서 구글 회원의 정보를 가져온다. 가져온 값들을 가지고 builder를 통해 생성자를 userEntity를 만들어 준 다음, 중복되지 않으면 userRepository에 저장을 해서 회원가입을 시켜주는 로직이다.
@GetMapping("/user") public @ResponseBody String user(@AuthenticationPrincipal PrincipalDetails principalDetails) { System.out.println("principalDetails : " + principalDetails.getUser()); return "user"; }
컨트롤러에서 확인을 해보기위해 PrincipalDetails 타입으로 로그인을 2번 진행했더니 로컬 로그인, 구글 로그인 둘 다 user 객체가 잘 나오는 것을 확인했다.
이렇게 하면 분리할 수 없이 편하게 잘 사용할 수 있다. 또한, 형변환을 따로 할 필요도 없다.
@AuthenticationPrincipal 어노테이션
따로 PrincipalDetailsService와 PrincipalOauth2UserService를 만들었는데, 그 이유는 PrincipalDetails로 반환하기 위해서이다. (OAuth로 들어왔을 때 회원가입을 해주는 이유도 있다.)
이렇게 반환해주면 @AuthenticationPrincipal 객체에 자동으로 return이 되어서 객체가 저장된다.
각각의 Service의 함수가 종료될 때 @AuthenticationPrincipal 어노테이션이 만들어진다.
'스프링 > 스프링 시큐리티' 카테고리의 다른 글
OAuth - 네이버 로그인 (provider 직접 등록) (0) 2024.01.10 OAuth - 페이스북 로그인 (OAuth2UserInfo - 각각의 provider) (0) 2024.01.10 OAuth - 구글 로그인(Authentication 객체가 가지는 2가지 타입) (0) 2024.01.09 OAuth - 구글 로그인(구글 api를 통해 회원 프로필 받아오기) (0) 2024.01.09 시큐리티 권한처리 (0) 2024.01.08