Search

83. 스프링부트 포토그램 OAuth2 페이스북 로그인 구현 완료

이제 회원정보를 가지고 회원가입을 할거다
OAuth2DetailesService
package com.cos.photogramstart.config.oauth; import java.util.Map; import java.util.UUID; 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; import com.cos.photogramstart.domain.user.User; import com.cos.photogramstart.domain.user.UserRepository; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @Service public class OAuth2DetailesService extends DefaultOAuth2UserService { private final UserRepository userRepository; private final BCryptPasswordEncoder bCryptPasswordEncoder; @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 username = "facebook_" + (String) userInfo.get("id"); String password = bCryptPasswordEncoder.encode(UUID.randomUUID().toString()); String name = (String) userInfo.get("name"); String email = (String) userInfo.get("email"); User user = User.builder() .username(username) .password(password) .email(email) .name(name) .build(); User userEntity = userRepository.save(user); return null; //return super.loadUser(userRequest); } }
Java
복사
근데 페이스북 로그인할때마다 회원가입시키면 안된다
한번 회원가입된 유저라면 있는 유저인지 체크해줘야한다.
@RequiredArgsConstructor @Service public class OAuth2DetailesService extends DefaultOAuth2UserService { private final UserRepository userRepository; private final BCryptPasswordEncoder bCryptPasswordEncoder; @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 username = "facebook_" + (String) userInfo.get("id"); String password = bCryptPasswordEncoder.encode(UUID.randomUUID().toString()); String name = (String) userInfo.get("name"); String email = (String) userInfo.get("email"); 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; } } }
Java
복사
근데 오류가 나고 있다.
리턴하는 타입이 OAuth2User이라 그렇다
oauth 폴더 안에 OAuth2User.java 생성
public class OAuth2UserDetails implements OAuth2User{ @Override public Map<String, Object> getAttributes() { // TODO Auto-generated method stub return null; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { // TODO Auto-generated method stub return null; } @Override public String getName() { // TODO Auto-generated method stub return null; } }
Java
복사
틀을 잡아주고
private User user; public OAuth2UserDetails(User user) { this.user = user; }
Java
복사
이부분도 추가해주고
OAuth2DetailesService에서 리턴을 수정해주면
이제 해당 타입오류가 안난다.
근데 이제 일반로그인 유저들은 PrincipalDetails
페이스북 로그인 유저들은 OAuth2DetailesService
그러면 controller 에서 매개변수가 PrincipalDetails이라
페이스북에서는 이걸로 하면 오류난다. 타입이 안맞아서 그렇다고 OAuth2UserDetails로 변경하면 일반유저로그인이할때 오류가 난다.
OAuth2UserDetails.java는 삭제하고
PrincipalDetails.java에 타입을 추가해준다
그리고 unimplemented 누르면
2개가 생성됨.
그러면 OAuth2DetailesService에서 리턴타입을 OAuth2UserDetails로 준 부분을 PrincipalDetails로 변경해줘도 된다
그러면 일반 로그인 유저도 페이스북 로그인 유저도 다 PrincipalDetails로 통일
대신 구분을 할 수 있다
PrincipalDetails
@Data public class PrincipalDetails implements UserDetails, OAuth2User{ private static final long serialVersionUID = 1L; private User user; private Map<String, Object> attributes; public PrincipalDetails(User user) { this.user = user; } public PrincipalDetails(User user, Map<String, Object> attributes) { this.user = user; } //권한: 한개가 아닐 수 있음. (3개 이상의 권한) @Override public Collection<? extends GrantedAuthority> getAuthorities() { Collection<GrantedAuthority> collector = new ArrayList<>(); collector.add(() -> { return user.getRole(); }); return collector; } @Override public String getPassword() { return user.getPassword(); } @Override public String getUsername() { return user.getUsername(); } @Override public boolean isAccountNonExpired() { //계정이 만료가 되었는 return true; //false가 맞는거다(로그인을 위해서 true로 변경) } @Override public boolean isAccountNonLocked() {//계정이 잠겼는가 return true; //false가 맞는거다(로그인을 위해서 true로 변경) } @Override public boolean isCredentialsNonExpired() { //비밀번호가 오랫동안 안바뀐거 아닌가 return true; //false가 맞는거다(로그인을 위해서 true로 변경) } @Override public boolean isEnabled() { //계정이 활성화 되어있는가 return true; //false가 맞는거다(로그인을 위해서 true로 변경) } @Override public Map<String, Object> getAttributes() { return attributes; //{iid : 912312391293 , name : blarblar , email : test@gmail.com } } @Override public String getName() { return (String)attributes.get("name"); } }
Java
복사
OAuth2로 로그인하는 유저들은 attributes 데이터가 존재할거다. 일반로그인 유저들에게는 attributes 데이터가 없을거다.
OAuth2DetailesService에도 리턴값에 추가해주자
OAuth2User에 빠진 권한을 넣어주자
이제 테스트 고고
*************************** APPLICATION FAILED TO START *************************** Description: The dependencies of some of the beans in the application context form a cycle: ┌─────┐ | securityConfig defined in file [/Users/heoji/git/photogram_sns_springboot_/photogram/target/classes/com/cos/photogramstart/config/SecurityConfig.class] ↑ ↓ | OAuth2DetailesService defined in file [/Users/heoji/git/photogram_sns_springboot_/photogram/target/classes/com/cos/photogramstart/config/oauth/OAuth2DetailesService.class] └─────┘
Java
복사
는 오류가 난다.
The dependencies of some of the beans in the application context form a cycle:
라는 문장을 보면 뭔가 bean 순서가 꼬인거 같다.
해당 부분을 지우고
이렇게 수정하니까 오류가 안난다..
DI될때 오류가 난건데 OAuth2DetailesService 보다 SecurityConfig가 늦게 떴다는 거다
SecurityConfig에서 IoC등록하기도 전에 OAuth2DetailesService가 먼저 실행되면서 IoC 등록할려는데 SecurityConfig가 들고있는 bean 어노테이션을 못찾아서 해당 오류가 난거다
이제 진짜 테스트
페이스북 로그인 클릭하니까
하핳
Caused by: java.sql.SQLException: Data too long for column 'username' at row 1 at org.mariadb.jdbc.internal.protocol.AbstractQueryProtocol.readErrorPacket(AbstractQueryProtocol.java:1681) ~[mariadb-java-client-2.7.2.jar:na] at org.mariadb.jdbc.internal.protocol.AbstractQueryProtocol.readPacket(AbstractQueryProtocol.java:1543) ~[mariadb-java-client-2.7.2.jar:na] at org.mariadb.jdbc.internal.protocol.AbstractQueryProtocol.getResult(AbstractQueryProtocol.java:1506) ~[mariadb-java-client-2.7.2.jar:na] at org.mariadb.jdbc.internal.protocol.AbstractQueryProtocol.executeQuery(AbstractQueryProtocol.java:316) ~[mariadb-java-client-2.7.2.jar:na] ... 117 common frames omitted
Java
복사
username 길이가 짧아서 그렇다
User.java에서
100으로 수정
테이블 관련으로 변경하는거라서 application.yml에 가서 create 저장 후 update로 변경 후 다시 저장
페이스북 로그인 후
타임라인 접근 성공! ( 데이터 다 날라가서 흰화면 뜨는게 맞다)
User 테이블에 데이터도 잘 들어왔다!

*참고