Oauth2.0 구글 로그인 및 자동 회원 가입
Oauth2User는
user의 정보를 Attributes() 안에 담고 있다.
이는 Map<String, Object>형식이다.
getAttributes :
{
sub=000000000000000000000,
name=xxx,
given_name=xx,
family_name=x,
picture=https://lh3.googleusercontent.com/a/AAcHTtcU7e8jEeWSfsxjs8_sxp_U1XoKdWQ5HJFQNPqt=s96-c,
email=xxx@gmail.com,
email_verified=true,
locale=ko
}
PrincipalDetails
package com.example.security1.auth;
import com.example.security1.model.User;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
@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 Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collect = new ArrayList<>();
collect.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return user.getRole();
}
});
return collect;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public String getName() {
return null;
}
}
OAuth2User를 추가적으로 implements 해주었고
이에 따라
private Map<String, Object> attributes;
//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;
}
위의 부분들이 추가되었다.
이제 PrincipalDetails은 UserDetails 타입과 OAuth 타입을 갖게 되어
PrincipalDetails 하나로 Authentication안에 정보를 저장할 수 있게 되었다.
PrincipalOauth2UserService
package com.example.security1.config.oauth;
import com.example.security1.auth.PrincipalDetails;
import com.example.security1.model.User;
import com.example.security1.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
@Service
public class PrincipalOauth2UserService extends DefaultOAuth2UserService {
@Autowired
private UserRepository userRepository;
//함수 종료시 @AuthenticationPrincipal 어노테이션이 만들어진다.
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
System.out.println("getClientRegistration : " + userRequest.getClientRegistration());
System.out.println("getAccessToken : " + userRequest.getAccessToken().getTokenValue());
OAuth2User oAuth2User = super.loadUser(userRequest);
System.out.println("getAttributes : " + oAuth2User.getAttributes());
String provider = userRequest.getClientRegistration().getRegistrationId();
String providerId = oAuth2User.getAttribute("sub");
String username = provider+"_"+providerId; //google_000000000000000000
String email = oAuth2User.getAttribute("email");
String role = "ROLE_USER";
User userEntity = userRepository.findByUsername(username);
if(userEntity == null) {
userEntity = User.builder()
.username(username)
.email(email)
.role(role)
.provider(provider)
.providerId(providerId)
.build();
userRepository.save(userEntity);
}
return new PrincipalDetails(userEntity, oAuth2User.getAttributes());
}
}
PrincipalDetails 에서 Oauth 회원가입을 위한 생성자를 만들었기 때문에
OAuth로그인 시도가 들어오면 loadUser 메서드가 호출되어
서버로부터 받은 정보를 가공하여 DB에 이미 있는지 없는지 존재 유무에 따라
저장시켜 주고 PricipalDetails를 userEntity, Attributes를 인자로 생성한다.
IndexController
package com.example.security1.controller;
import com.example.security1.auth.PrincipalDetails;
import com.example.security1.model.User;
import com.example.security1.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class IndexController {
@Autowired
private UserRepository userRepository;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@GetMapping("/test/login")
public @ResponseBody String testLogin(
Authentication authentication,
@AuthenticationPrincipal PrincipalDetails userDetails) {
System.out.println("/test/login =====================");
PrincipalDetails principalsDetails = (PrincipalDetails) authentication.getPrincipal();
System.out.println("authentication" + principalsDetails.getUser());
System.out.println("userDetails = " + userDetails.getUser());
return "세션 정보 확인하기";
}
@GetMapping("/test/oauth/login")
public @ResponseBody String testOauthLogin(
Authentication authentication,
@AuthenticationPrincipal OAuth2User oauth) {
System.out.println("/test/oauth/login =====================");
OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal();
System.out.println("authentication" + oAuth2User.getAttributes());
System.out.println("OAuth2User : " + oauth.getAttributes());
return "Oauth 세션 정보 확인하기";
}
@GetMapping({"","/"})
public String index() {
return "index";
}
@GetMapping("/user")
public @ResponseBody String user(@AuthenticationPrincipal PrincipalDetails principalDetails) {
System.out.println("principalDetails : " + principalDetails.getUser());
return "user";
}
@GetMapping("/admin")
public @ResponseBody String admin() {
return "admin";
}
@GetMapping("/manager")
public @ResponseBody String manager() {
return "manager";
}
@GetMapping("/loginForm")
public String login() {
return "loginForm";
}
@GetMapping("joinForm")
public String joinForm() {
return "joinForm";
}
@PostMapping("/join")
public String join(User user) {
user.setRole("ROLE_USER");
System.out.println(user);
String rawPassword = user.getPassword();
String encPassword = bCryptPasswordEncoder.encode(rawPassword);
user.setPassword(encPassword);
userRepository.save(user);
return "redirect:/loginForm";
}
@Secured("ROLE_ADMIN")
@GetMapping("/info")
public @ResponseBody String info() {
return "개인정보";
}
@PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")
@GetMapping("/data")
public @ResponseBody String data() {
return "데이터정보";
}
}
아랫부분이 이전과 변경되었다.
//OAuth 로그인을 해도 PrincipalDetails
//일반 로그인을 해도 PrincipalDetails
@GetMapping("/user")
public @ResponseBody String user(@AuthenticationPrincipal PrincipalDetails principalDetails) {
System.out.println("principalDetails : " + principalDetails.getUser());
return "user";
}
loadUser 또는 loadUserByUsername 메서드가 수행되고 난 뒤
@AuthenticationPrincipal 어노테이션이 만들어진다.
위에서 PrincipalDetails로 UserDetails와 Oauth 타입을 받을 수 있게 되어
@AuthenticationPricipal PrincipalDetails principalDetails로
일반 회원가입과 Oauth회원가입을 통한 인증으로 "/user"를 접근할 수 있을 것이다.
'자바 탐구' 카테고리의 다른 글
스프링) SpringSecurity - 11) Oauth2.0 네이버 로그인 완료 (0) | 2023.05.26 |
---|---|
스프링) SpringSecurity - 10) Oauth2.0 페이스북 로그인 완료 (1) | 2023.05.26 |
스프링) SpringSecurity - 8) Oauth2.0 Authentication객체가 가질 수 있는 2가지 타입 (0) | 2023.05.26 |
스프링) SpringSecurity - 7) Oauth2.0 구글 회원 프로필 정보 받아보기 (0) | 2023.05.26 |
스프링) SpringSecurity - 6) Oauth2.0 구글 로그인 준비 (0) | 2023.05.26 |