@RequiredArgsConstructor
@Service
public class OAuth2DetailsService extends DefaultOAuth2UserService {
private final UserRepository userRepository;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
System.out.println("OAuth2 서비스 ");
OAuth2User oauth2User = super.loadUser(userRequest);
System.out.println(oauth2User.getAttributes());
User user = User.builder()
.username()
.password()
.email()
.name()
.build();
User userEntity = userRepository.save();
return null;
}
}
이전 시간에 이어서 이제 받아온 정보를 내 DB에 넣고 저장을 해야할텐데,
본인의 경우 Model에 @Builder를 선언하였고 DTO 에서 빌드해서 가져가는 방식을 사용중이다.
Model 인 User
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(unique = true, length = 100)
private String username;
@Column(nullable = false)
private String password;
private String name;
private String website; //웹사이트
private String bio; // 자기소개
@Column(nullable = false)
private String email;
private String phone;
private String gender;
private String profileImageUrl;
private String role;
private LocalDateTime createDate;
}
모델은 이렇게 생겼고
DTO
@Data
public class SignupDto {
@Size(min = 1 , max = 20)
@NotBlank
private String username;
@NotBlank
private String password;
@NotBlank
private String email;
@NotBlank
private String name;
public User toEntity(){
return User.builder()
.username(username)
.password(password)
.email(email)
.name(name)
.build();
}
}
DTO에 username , password , email , name 를 NotBlank 로 걸고 build 하도록 구현해놨기 때문에
DTO의 4가지 요소는 필수적으로 받아야 한다.
@RequiredArgsConstructor
@Service
public class OAuth2DetailsService extends DefaultOAuth2UserService {
private final UserRepository userRepository;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
System.out.println("OAuth2 서비스 ");
OAuth2User oauth2User = super.loadUser(userRequest);
System.out.println(oauth2User.getAttributes());
Map<String , Object> userInfo = oauth2User.getAttributes();
String name =(String)userInfo.get("name");
String email = (String)userInfo.get("email");
String username = "facebook_"+(String)userInfo.get("id");
String password = new BCryptPasswordEncoder().encode(UUID.randomUUID().toString());
User user = User.builder()
.username(username)
.password(password)
.email(email)
.name(name)
.build();
User userEntity = userRepository.save(user);
return null;
}
}
그렇기 때문에
1. userRequest로 받아온 정보들을 getAttribute
2. userInfo 에 담는다, 이때 Map을 사용했는데 이유는 oauth2User.getAttributes()가 map값을 리턴하기때문
3. Map을 꺼내기위해 .get 메소드 사용하는데 이때 리턴이 Object 이므로 String으로 다운캐스팅 해서 담아줌
4. 빌더에 넣는다.
이때 username 에는 따로 "facebook_" 을 넣은건 식별하기 위해서 넣은것이고
password는 암호화 해서 랜덤값을 넣어줬다 (시큐리티는 암호화가 필수기 때문에.)
password 를 저렇게 UUID 해서 Random 값으로 넣은 이유는
소셜로그인은 패스워드로 로그인할게 아니기 때문에 넣어줘도 된다.
로직은 얼추 완성되었지만 한가지 문제가 남았다
매번 페이스북 로그인 할때마다 이 정보들을 insert 할 순 없다.
즉 소셜 로그인을 한번이상 했다면 회원으로 남겨야 하기때문에,
@RequiredArgsConstructor
@Service
public class OAuth2DetailsService extends DefaultOAuth2UserService {
private final UserRepository userRepository;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2User oauth2User = super.loadUser(userRequest);
Map<String, Object> userInfo = oauth2User.getAttributes();
String name = (String) userInfo.get("name");
String email = (String) userInfo.get("email");
String username = "facebook_" + (String) userInfo.get("id");
String password = new BCryptPasswordEncoder().encode(UUID.randomUUID().toString());
User userEntity = userRepository.findByUsername(username);
if (userEntity == null) { //최초 로그인
User user = User.builder()
.username(username)
.password(password)
.email(email)
.name(name)
.build();
return userRepository.save(user);
} else { // 이미 가입되어있음
return userEntity;
}
}
}
그래서 if문으로 각각의 분기를 짜준다..
여기서 return 되는 값은 자동으로 세션에 저장해줍니다.
하지만 문제가 하나 있는데
타입 문제가 뜬다. 요구타입이 OAuth2User 타입인데 User타입을 넣었기 때문
이 경우를 해결하기 위해선 단순하게 User -> OAuth2User 타입으로 해주면 되지않느냐?
그러면 과정도 복잡해질뿐더러 추후에 매우 헷갈립니다.
그렇기 때문에 세션값을 원래 로그인과 동일하게 해줘야하는데,
스프링 시큐리티의 userDetails 를 구현한 곳 (본인의 경우 PrincipalDetails , @AuthenticationPrincipal 로 사용하는 클래스) 에 가서 OAuth2User도 implements 해준다.
implements 했으니 당연히 override 해주고, 해준다면 2가지의 메소드를 구현해야 한다.
@Override
public String getName() {
return null;
}
@Override
public Map<String, Object> getAttributes() {
return null;
}
이를 구현하면
@Data
public class PrincipalDetails implements UserDetails , OAuth2User {
private User user;
// 새로 추가
private Map<String, Object> attributes;
public PrincipalDetails(User user){
this.user = user;
}
.
.
.
.
.
.
// 새로 추가
@Override
public String getName() {
return (String)attributes.get("name");
}
// 새로 추가
@Override
public Map<String, Object> getAttributes() {
return attributes;
}
// 새로 추가
public PrincipalDetails(User user,Map<String, Object> attributes){
this.user = user;
}
}
Override에 의한 추가된 메소드를 구현하기위해 상단에
private Map<String, Object> attriubutes; 를 선언하여 2가지 메소드를 구현해주고
PrincipalDetails Overloading 해준다. attributes 매개변수를 추가해서
로그인 로직시 페이스북 로그인의 경우는 오버로딩 된 principalDetails 를 골라서 사용하면 된다..
그러면 이제
@RequiredArgsConstructor
@Service
public class OAuth2DetailsService extends DefaultOAuth2UserService {
private final UserRepository userRepository;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2User oauth2User = super.loadUser(userRequest);
Map<String, Object> userInfo = oauth2User.getAttributes();
String name = (String) userInfo.get("name");
String email = (String) userInfo.get("email");
String username = "facebook_" + (String) userInfo.get("id");
String password = new BCryptPasswordEncoder().encode(UUID.randomUUID().toString());
User userEntity = userRepository.findByUsername(username);
if (userEntity == null) { //최초 로그인
User user = User.builder()
.username(username)
.password(password)
.email(email)
.name(name)
.build();
return new PrincipalDetails(userRepository.save(user), oauth2User.getAttributes());
} else { // 이미 가입되어있음
return new PrincipalDetails(userEntity,oauth2User.getAttributes());
}
}
}
if문의 return 문 부분을 return new를 통해서 구현한 오버로딩된 메소드를 사용하면 된다.
그 후 실행해보면 로그인이 잘 된다.
로그인 후 DB에도 잘 들어온 모습.
'Spring > JPA + Security' 카테고리의 다른 글
[Security + JPA + OAuth2] 카카오 로그인 구현하기 -2 (0) | 2023.01.17 |
---|---|
[Security + JPA + OAuth2] 카카오 로그인 구현하기 -1 세팅하기 (0) | 2023.01.17 |
[Security + JPA + OAuth2] - 페이스북 로그인 -3 (0) | 2023.01.17 |
[Security + JPA + OAuth2] - 페이스북 로그인 - 2 (0) | 2023.01.15 |
[Security + JPA + OAuth2] - 페이스북 로그인 - 1 (0) | 2023.01.15 |
댓글