Server/Spring Boot

spring boot // oauth 네이버 로그인 api

Jaybon 2020. 8. 4. 15:07

 

OAuth + 일반로그인

https://ondolroom.tistory.com/646

 

spring boot // 머스태치 mustache html / 주소 일부허용, 컨트롤러에서 허용 / 이클립스 내에서 의존성 ��

-------------- jpa 메서드 참고 https://github.com/codingspecialist/Springboot-Security-OAuth2.0-V2 codingspecialist/Springboot-Security-OAuth2.0-V2 Contribute to codingspecialist/Springboot-Security..

ondolroom.tistory.com

 

 

페이스북 OAuth 로그인

https://ondolroom.tistory.com/651

 

spring boot // 페이스북 로그인

------------------- 테스트결과 잘들어감

ondolroom.tistory.com

 

 

네이버 OAuth 로그인 참고 사이트

https://for1123.tistory.com/24

 

19. 네아로(네이버 아이디로 로그인) API 사용하기

네이버 개발자 센터로 들어가 Application 탭에서 애플리케이션 등록을 누르면 다음과 같은 화면이 나타납니다. 위와 같이 설정하고 등록하면 Client ID와 Secret을 획득할 수 있습니다. 네이버에서는 �

for1123.tistory.com

 

 

참고

구글, 페이스북은 키 - 밸류 형태

네이버는 제이슨 형식이다

 

 

application.yml

기본프로바이더에 네이버가없기 때문에 프로바이더를 추가해준다

 

 

NaverUserInfo.java

기존 OAuth2UserInfo 커스텀과 다르게 attributes안에서 response를 가져와야한다

가져오는 함수를 하나 만들어서 리턴한다

 

 

PrincipalOauth2UserService.java

네이버를 추가해준다

---

 

 

테스트

---

 

 

DB

정상적으로 입력됨

 

----------

 

코드

 

application.yml

더보기
server:
  port: 8080
  servlet:
    context-path: /
    encoding:
      charset: utf-8
      enabled: true
      force: true
      
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/security?serverTimezone=Asia/Seoul
    username: cos
    password: cos1234
    
#  mvc:
#    view:
#      prefix: /templates/
#      suffix: .mustache


  jpa:
    hibernate:
      ddl-auto: update #create update none 3개 중 하나 선택 , 개발이 끝나면 none
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    show-sql: true
      
      
  security:
    oauth2:
      client:
        registration:
          google: # /oauth2/authorization/google가 이 주소를 동작하게 한다
            client-id: 키
            client-secret: 키
            scope:
            - email
            - profile #스코프는 회사마다 다르기 때문에 적음
          
          facebook:
            client-id: 키
            client-secret: 키
            scope:
            - email
            - public_profile
          
          naver:
            client-id: 키
            client-secret: 키
            redirect-uri: http://localhost:8080/login/oauth2/code/naver
            authorization-grant-type: authorization_code
            scope:
            - email
            - profile_image
            
        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

 

 

SecurityConfig.java

더보기
package com.jaybon.securityEx01.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.authentication.UserServiceBeanDefinitionParser;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.user.OAuth2User;

import com.jaybon.securityEx01.config.oauth.PrincipalOauth2UserService;

@Configuration // IoC 빈(bean, 인스턴스)을 등록
@EnableWebSecurity // 필터 체인 관리 시작 어노테이션
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) // 컨트롤러 접근 전에 낚아챔, 특정 주소 접근시 권한 및 인증 미리체크
public class SecurityConfig extends WebSecurityConfigurerAdapter{
	
	@Autowired
	private PrincipalOauth2UserService principalOauth2UserService;
	
	@Bean // IoC에 등록되어 컨피그가 호출될 때 생성, 메서드를 IoC하는 방법
	public BCryptPasswordEncoder enc() { // 마땅히 둘 곳이 없어서 둔 것 Controller를 제외한 곳에 둠
		return new BCryptPasswordEncoder();
	}
	
	@Override
	public void configure(WebSecurity web) throws Exception {
		web.ignoring().antMatchers("/admin/1");
	}
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		
		http.csrf().disable(); // csrf 비활성화
		
		http.authorizeRequests()
			.antMatchers("/user/**").authenticated()// authenticated() 인증
//			.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER')") // access() 권한 .hasAnyRole()
//			.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN') and hasRole('ROLE_USER')")
			.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
			.anyRequest().permitAll()
		.and()
			.formLogin()
			.loginPage("/login") // 인증이 필요한 곳에 /login 으로 리다이렉트
			.loginProcessingUrl("/loginProc") // 필터체인에서 인지하고 있다가 시큐리티가 낚아채서 Authentication Manager
			.defaultSuccessUrl("/") // 성공하면 해당 주소로 이동 / 슬래시만 달면 이전 주소로 이동
		.and()
			.oauth2Login()
			.loginPage("/login")
			.userInfoEndpoint()
			.userService(principalOauth2UserService);
	}
}

 

 

NaverUserInfo.java

더보기
package com.jaybon.securityEx01.config.oauth.provider;

import java.util.Map;


public class NaverUserInfo implements OAuth2UserInfo{
	
    private Map<String, Object> attributes;

	public NaverUserInfo(Map<String, Object> attrubutes) {
		this.attributes = attrubutes;
	}
	
    private Map<String, Object> ofNaver(Map<String, Object> attributes) {
        Map<String, Object> response = (Map<String, Object>) attributes.get("response");

        return response;
    }

	@Override
	public String getProviderId() {
		return (String) ofNaver(attributes).get("id");
	}

	@Override
	public String getProvider() {
		return "naver";
	}

	@Override
	public String getEmail() {
		return (String) ofNaver(attributes).get("email");
	}

	@Override
	public String getName() {
		return (String) ofNaver(attributes).get("name");
	}
}

 

 

PrincipalOauth2UserService.java

더보기
package com.jaybon.securityEx01.config.oauth;

import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
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.jaybon.securityEx01.config.auth.PrincipalDetails;
import com.jaybon.securityEx01.config.oauth.provider.FacebookUserInfo;
import com.jaybon.securityEx01.config.oauth.provider.GoogleUserInfo;
import com.jaybon.securityEx01.config.oauth.provider.NaverUserInfo;
import com.jaybon.securityEx01.config.oauth.provider.OAuth2UserInfo;
import com.jaybon.securityEx01.model.User;
import com.jaybon.securityEx01.repository.UserRepository;

@Service
public class PrincipalOauth2UserService extends DefaultOAuth2UserService{
	
	@Autowired
	private UserRepository userRepository;
	
	@Override
	public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
	
		
		OAuth2User oAuth2User = super.loadUser(userRequest); // 토큰값으로 자원을 받아오는 함수
		
		// 1번: oAuth2User의 정보를 principalDetails에 넣어주면 된다.
		// 2번: PrincipalDetails를 리턴한다.
		System.out.println("userRequest"+userRequest.getAccessToken().getTokenValue()); // 코드 토큰 유저정보
		System.out.println("userRequest"+userRequest.getClientRegistration()); // 코드 토큰 유저정보
		System.out.println(oAuth2User); // 토큰을 통해 응답받은 회원정보
		
		try {
			
		} catch (Exception e) {
			// TODO: handle exception
		}
				
		return processOAuth2User(userRequest, oAuth2User); // 이때 세션에 등록된다 OAuth2User
	}
	
	private OAuth2User processOAuth2User(OAuth2UserRequest userRequest, OAuth2User oAuth2User) {
		
		// 일반적으로는 로그인할 때 유저정보 User
		// 1번 : OAuth2로 로그인할 때 유저정보 oAuth2User.getAttributes() <- 구성해야됨
		// 2번 : DB에 이 사람있나?
		// 있으면? -> 업데이트(구글에서 바뀐정보가 있을 수 있으니 확인 없이 무조건 업데이트)
		// 없으면? -> 인서트(회원가입)
		// 리턴을 PrincipalDetails를 리턴
		
		OAuth2UserInfo oAuth2UserInfo = null;
		if(userRequest.getClientRegistration().getRegistrationId().equals("google")) {
			oAuth2UserInfo = new GoogleUserInfo(oAuth2User.getAttributes());
		} else if(userRequest.getClientRegistration().getRegistrationId().equals("facebook")) {
			oAuth2UserInfo = new FacebookUserInfo(oAuth2User.getAttributes());
		} else if(userRequest.getClientRegistration().getRegistrationId().equals("naver")) {
			oAuth2UserInfo = new NaverUserInfo(oAuth2User.getAttributes());
		} else {
			System.out.println("구글, 페이스북, 네이버만 가능");
		}
		

		Optional<User> userOptional 
			= userRepository.findByProviderAndProviderId(oAuth2UserInfo.getProvider(), oAuth2UserInfo.getProviderId());
		
		User user;
		if(userOptional.isPresent()) {
			user = userOptional.get();
			// 아이디가 있어도 update 해줘야 함
		} else {
			user = User.builder()
					.username(oAuth2UserInfo.getProvider()+"_"+oAuth2UserInfo.getProviderId())
					.email(oAuth2UserInfo.getEmail())
					.role("ROLE_USER")
					.provider(oAuth2UserInfo.getProvider())
					.providerId(oAuth2UserInfo.getProviderId())
					.build();
			userRepository.save(user);
		}
			
		return new PrincipalDetails(user, oAuth2User.getAttributes());
	}
}