Oauth2.0 네이버 로그인 완료
https://developers.naver.com/main/
application.yml
spring:
security:
oauth2:
client:
registration:
naver:
client-id: <id>
client-secret: <secret>
scope:
- name
- email
- profile_image
client-name: Naver
authorization-grant-type: authorization_code
redirect-uri: http://localhost:8080/login/oauth2/code/naver
구글, 페이스북, 트위치와 달리 네이버는
redirect-uri를 지정해 주어야 한다고 한다.
위의 링크로 접속하여 "Application" -> "애플리케이션 등록" 클릭
서비스 url은
http://localhost:8080
Callback URL은
http://localhost:8080/login/oauth2/code/naver
yml에서 redirect-uri로 지정한 주소와 똑같이 해주어야 한다.
"등록하기"를 누르면 "Client ID"와 "Client secret"이 나온다.
네이버는 oauth2에서 가지고 있는 provider가 아니기 때문에 attributes에 저장할 수 없다는 에러가 나올 것이다.
에러
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration': Unsatisfied dependency expressed through field 'httpSecurity': Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration': Unsatisfied dependency expressed through method 'setContentNegotiationStrategy' parameter 0: Error creating bean with name 'org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration': Unsatisfied dependency expressed through method 'setConfigurers' parameter 0: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.OAuth2ClientConfiguration$OAuth2ClientWebMvcSecurityConfiguration': Unsatisfied dependency expressed through method 'setClientRegistrationRepository' parameter 0: Error creating bean with name 'clientRegistrationRepository' defined in class path resource [org/springframework/boot/autoconfigure/security/oauth2/client/servlet/OAuth2ClientRegistrationRepositoryConfiguration.class]: Failed to instantiate [org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository]: Factory method 'clientRegistrationRepository' threw exception with message: Provider ID must be specified for client registration 'naver'
따라서 네이버는 provider를 만들어 주어야 한다.
application.yml
spring:
security:
oauth2:
client:
registration:
naver:
client-id: 9LPJARMnTpVljmmQooM0
client-secret: MOjdlgLjHa
scope:
- name
- email
- profile_image
client-name: Naver
authorization-grant-type: authorization_code
redirect-uri: http://localhost:8080/login/oauth2/code/naver
provider:
naver:
authorization-uri: https://nid.naver.com/oauth2.0/authorize
token-uri: https://nid.naver.com/oauth2.0/token
user-info-uri: https://openapi.naver.com/v1/nid/me
user-name-attribute: response #회원 정보를 json으로 받는데 response라는 키값으로 네이버가 리턴해줌
위를 참고하여 yml을 수정한다.
provider에 대한 정보는 네이버 개발 문서를 참고하면 알 수 있다.
loginForm.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인 페이지</title>
</head>
<body>
<h1>로그인 페이지</h1>
<hr/>
<form action="/login" method="POST">
<input type = "text" name = "username" placeholder="Username"/> <br/>
<input type = "password" name = "password" placeholder="Password"/> <br/>
<button>로그인</button>
</form>
<a href = "/oauth2/authorization/google">구글 로그인</a>
<a href = "/oauth2/authorization/facebook">페이스북 로그인</a>
<a href = "/oauth2/authorization/naver">네이버 로그인</a>
<a href = "/joinForm">회원가입을 아직 하지 않음</a>
</body>
</html>
네이버 로그인 링크가 추가되었다.
/oauth2/authorization/naver
위의 주소는 고정이다.
실행을 시키면 잘 나온다.
로그인을 시도하면 오류가 나온다.
etClientRegistration : ClientRegistration{registrationId='naver', clientId='9LPJARMnTpVljmmQooM0', clientSecret='MOjdlgLjHa', clientAuthenticationMethod=org.springframework.security.oauth2.core.ClientAuthenticationMethod@4fcef9d3, authorizationGrantType=org.springframework.security.oauth2.core.AuthorizationGrantType@5da5e9f3, redirectUri='http://localhost:8080/login/oauth2/code/naver', scopes=[name, email, profile_image], providerDetails=org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails@f4ae570, clientName='Naver'}
getAccessToken : AAAANlMbp1il-1FmzjSU8wJd1fuLUVQA3mF-X8WEqXOt-fIlL_YR6962z9_e7tvxzKdpQMZaj6vMd1UGKJrvxm6oj3M
getAttributes : {resultcode=00, message=success, response={id=aaaKXHfcYqC-5RWKCuOhhS-yunU6IEKXVd69nwFgfrM, email=xxx@naver.com, name=xxx}}
프린트로 찍은 결과는 일단 나온다.
java.lang.NullPointerException: Cannot invoke "com.example.security1.config.oauth.provider.Oauth2UserInfo.getProvider()" because "oAuth2UserInfo" is null
oAuth2UserInfo 가 null이라고 나온다.
네이버는 attriubutes(구글, 페이스북의 response) 안에 resonse값 안에
필요한 정보가 있기 때문에 추가적인 수정이 필요하다.
(response안에 response가 있는 셈이다.)
NaverUserInfo
package com.example.security1.config.oauth.provider;
import java.util.Map;
public class NaverUserInfo implements Oauth2UserInfo{
private Map<String, Object> attributes; //getAttributes()
public NaverUserInfo(Map<String, Object> attributes) {
this.attributes = attributes;
}
@Override
public String getProviderId() {
return (String)attributes.get("id");
}
@Override
public String getProvider() {
return "naver";
}
@Override
public String getEmail() {
return (String) attributes.get("email");
}
@Override
public String getName() {
return (String) attributes.get("name");
}
}
PrincipalOauth2UserService
package com.example.security1.config.oauth;
import com.example.security1.auth.PrincipalDetails;
import com.example.security1.config.oauth.provider.FacebookUserInfo;
import com.example.security1.config.oauth.provider.GoogleUserInfo;
import com.example.security1.config.oauth.provider.NaverUserInfo;
import com.example.security1.config.oauth.provider.Oauth2UserInfo;
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;
import java.util.Map;
@Service
public class PrincipalOauth2UserService extends DefaultOAuth2UserService {
@Autowired
private UserRepository userRepository;
@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());
Oauth2UserInfo oAuth2UserInfo = null;
if(userRequest.getClientRegistration().getRegistrationId().equals("google")){
System.out.println("구글 로그인 요청");
oAuth2UserInfo = new GoogleUserInfo(oAuth2User.getAttributes());
} else if(userRequest.getClientRegistration().getRegistrationId().equals("facebook")) {
System.out.println("페이스북 로그인 요청");
oAuth2UserInfo = new FacebookUserInfo(oAuth2User.getAttributes());
} else if(userRequest.getClientRegistration().getRegistrationId().equals("naver")) {
System.out.println("네이버 로그인 요청");
oAuth2UserInfo = new NaverUserInfo((Map)oAuth2User.getAttributes().get("response"));
} else {
System.out.println("구글과 페이스북, 네이버 로그인만 가능합니다.");
}
String provider = oAuth2UserInfo.getProvider();
String providerId = oAuth2UserInfo.getProviderId();
String username = provider+"_"+providerId;
String email =oAuth2UserInfo.getEmail();
String role = "ROLE_USER";
User userEntity = userRepository.findByUsername(username);
if(userEntity == null) {
System.out.println("Oauth인이 최초입니다.");
userEntity = User.builder()
.username(username)
.email(email)
.role(role)
.provider(provider)
.providerId(providerId)
.build();
userRepository.save(userEntity);
} else {
System.out.println("로그인을 이미 한 적이 있습니다.");
}
return new PrincipalDetails(userEntity, oAuth2User.getAttributes());
}
}
위의 코드에서 아래코드가 추가되었다.
else if(userRequest.getClientRegistration().getRegistrationId().equals("naver")) {
System.out.println("네이버 로그인 요청");
oAuth2UserInfo = new NaverUserInfo((Map)oAuth2User.getAttributes().get("response"));
}
네이버는 (Map)으로 캐스팅을 해주어 getAttributes().get("response")를 해주어야
NaverUserInfo의 attributes에 값이 전달된다.
네이버 로그인을 시도하면 DB에도 잘 저장되어 있다.
'자바 탐구' 카테고리의 다른 글
스프링) 의존성 주입(Dependency Injection) 방식 (0) | 2023.07.22 |
---|---|
JPA) 영속성 컨텍스트 (0) | 2023.07.21 |
스프링) SpringSecurity - 10) Oauth2.0 페이스북 로그인 완료 (1) | 2023.05.26 |
스프링) SpringSecurity - 9) Oauth2.0 구글 로그인 및 자동 회원 가입 진행 완료 (0) | 2023.05.26 |
스프링) SpringSecurity - 8) Oauth2.0 Authentication객체가 가질 수 있는 2가지 타입 (0) | 2023.05.26 |