⚙️Oauth2

박용민·2024년 3월 12일
0
post-thumbnail

우리가 평소에 로그인할때 보통 2가지의 케이스로 로그인을 시도한다
1. 아이디(이메일) 패스워드를 입력하고 로그인을 시도
2. 카카오톡, 네이버, 구글등의 소셜 로그인으로 로그인을 시도

아이디와 패스워드를 입력하고 로그인을 시도하는 경우는 웹사이트나 애플리케이션에 계정 정보를 직접 제공을 한다.
우리가 회원가입을 했을때 설정한 아이디 및 비밀번호를 통해서 인증을 처리하는 과정을 처리한다. (NAVER)

반면 소셜 로그인 우리가 많이 봤을 아래와 같은 그림과 같이 로그인 폼 밑에 다른 아이콘을 본적이 있을것이다. 이는 다른 소셜 미디어 플랫폼을 통해
제 3자가 인증을 거쳐 자격을 증명하여 데이터에 접근할 수 있도록 권한을 부여할 수 있다. (OP.GG)

✈️Oauth2

OAuth2.0(Open Authorization 2.0, OAuth2)은 인증 및 권한 부여를 관리하기 위한 개방형 표준 프로토콜이며 사용자가 서로 다른 웹 사이트 또는 어플리케이션에서 동일한 자격을 증명을 사용할 수 있도록 허용하는데 소셜 계정을 기반으로 손 쉽게 간편한게 인증하는 시스템이라고 생각하면 된다.

구성요소

이름설명
Resource Owner리소스 사용자는 사용자를 나타낸다.
Client사용자가 엑세스하려는 리소스를 대표하는 어플리케이션, 웹 또는 모바일
Authorization Server(인증 서버)인증 서버는 사용자 인증 및 권한 부여, 토큰 발급
Resource Server클라이언트가 엑세스하려는 실제 리소스를 호스팅, 사용자의 데이터나 서비스
Access Token클라이언트가 리소스 서버에 인증 및 권한 부여를 요청할 때 사용되는 토큰

흐름

대부분의 흐름은 다음과 같으며 구글, 네이버, 카카오등 제공하는 데이터등은 다르지만 요청 방식은 비슷하다.
여기서는 카카오를 이용해서 로그인 요청 시도를 해본다.

  1. 클라이언트 애플리케이션이 인증 서버에 인증을 요청
  2. 사용자는 자격 증명을 제공하여 클라이언트에 대한 엑세스를 승인
  3. 인증 서버는 클라이언트에 대한 엑세스 토큰을 발급
  4. 클라이너트는 엑세스 토큰을 사용하여 리소스 서버에 액세스하고, 사용자의 데이터나 서비스에 접근
  5. 리소스 서버는 엑세스 토큰의 유효성을 확인하고, 클라이언트에게 리소스를 제공

🍫카카오 소셜 로그인

🌻카카오 소셜 로그인 구상도 및 절차

  1. 먼저 사용자가 웹사이트나 애플리케이션에서 카카오 소셜 로그인 버튼을 클릭한다.

  2. 사용자가 카카오 계정으로 로그인하여 권한을 부여 및 동의 항목을 허용한다.

  3. 카카오는 클라이언트에게(여기서는 spring) 애플리케이션 인가 코드를 제공하고 다시 인가 코드를 이용하여 엑세스 토큰을 요청하고 토큰을 제공 받는다.

  4. 엑세스 토큰을 사용하여 카카오 서버에 사용자 데이트를 요청한다.

  5. 사용자 정보를 수신한 클라이언트는 받은 데이터를 이용하여 로그인 완료 및 기타 서비스를 제공한다.

🌻카카오 로그인 준비

먼저 카카오 개발자 페이지로 이동하여 애플리케이션을 등록한다. https://developers.kakao.com/console/app

REST API키를 이용해서 카카오 서버와 통신을 하기위한 키값이니 공개되서는 안된다 그리하여 개인적인 공간에 저장하자

카카오 로그인 탭을 누르고 활성화 설정을 ON으로 변경한다.
이후 RedirectURI에서 Callback으로 받을려는 주소를 저장한다.

보안탭으로 이동하여 Client Secret값을 발급 받고 활성화 시킨다.

마지막으로 동의 항목탭으로 이동하고 얻고자 하는 데이터 동희 항목을 선택한다.

🫛spring 준비

Oauth2를 사용하기위해 oauth2-client 의존성을 추가해주고 jwt로 토큰을 발급하기 위해 다음 의존성을 추가한다.

implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'io.jsonwebtoken:jjwt-api:0.12.3'
implementation 'io.jsonwebtoken:jjwt-impl:0.12.3'
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3'

🌼application.properties 설정

#registration kakao
spring.security.oauth2.client.registration.kakao.client-name=kakao
spring.security.oauth2.client.registration.kakao.client-id={client-id}
spring.security.oauth2.client.registration.kakao.client-secret={client-secret}
spring.security.oauth2.client.registration.kakao.redirect-uri=http://localhost:8080/login/oauth2/code/kakao
spring.security.oauth2.client.registration.kakao.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.kakao.scope=[제공 받고 싶은 데이터]
spring.security.oauth2.client.registration.kakao.client-authentication-method=client_secret_post

#provider kakao
spring.security.oauth2.client.provider.kakao.authorization-uri=https://kauth.kakao.com/oauth/authorize
spring.security.oauth2.client.provider.kakao.token-uri=https://kauth.kakao.com/oauth/token
spring.security.oauth2.client.provider.kakao.user-info-uri=https://kapi.kakao.com/v2/user/me
spring.security.oauth2.client.provider.kakao.user-name-attribute=kakao_account

🌼스프링 SecurityConfig 설정

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    private final CustomOAuth2UserService customOAuth2UserService;
    private final CustomSuccessHandler customSuccessHandler;
    private final JWTUtil jwtUtil;
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    
    //oauth2
        http
            .oauth2Login((oauth2) -> oauth2
                .userInfoEndpoint((userInfoEndpointConfig -> userInfoEndpointConfig
                    .userService(customOAuth2UserService)))
                .successHandler(customSuccessHandler));
                
    //세션 설정 : STATELESS(JWT 방식을 사용하기 위해서 사용)
        http
            .sessionManagement((session) -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS));
                
                ...(기타 설정)
                
   	 return http.build();

oauth2 http 설정

  • oauth2Login : OAuth 2.0 로그인을 구성을 시작하는 부분
  • userInfoEndpoint : OAuth 2.0의 사용자 정보 엔드포인트를 구성하는 부분
  • userService : 사용자 정보 엔드포인트에서 사용될 UserService를 지정
  • successHandler : 로그인 성공 후에 실행될 커스텀 핸들러

🌼CustomOAuth2UserService

카카오 로그인 요청 http://localhost:8080/oauth2/authorization/kakao이 있을때 해당 매소드로 이동한다.
oAuth2User에 로그인한 사용자의 정보들을 JSON형태로 담겨서 가져온다. 이후 적절한 서비스를 제공하면 된다.

@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
    private final UserRepository userRepository;

    public CustomOAuth2UserService(UserRepository userRepository){
        this.userRepository = userRepository;
    }

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2User oAuth2User = super.loadUser(userRequest);
        String registrationId = userRequest.getClientRegistration().getRegistrationId();
        OAuth2Response oAuth2Response = null;
        
        if(registrationId.equals("naver"))
            oAuth2Response = new NaverResponse(oAuth2User.getAttributes());
        else if(registrationId.equals("google"))
            oAuth2Response = new GoogleResponse(oAuth2User.getAttributes());
        else if(registrationId.equals("kakao"))
            oAuth2Response = new KakaoResponse(oAuth2User.getAttributes());
        else
            return null;

        String username = oAuth2Response.getProvider()+" "+oAuth2Response.getProviderId();
        UserEntity existData = userRepository.findByUsername(username);
        ... (기타 로직)
}

DefaultOAuth2UserService

  • Spring Security에서 제공하는 OAuth 2.0 프로바이더
  • 사용자 정보를 가져오는 기본적인 서비스
  • 인증 및 권한 부여에 필요한 추가적인 처리를 수행

🌼OAuth2User

사용자의 정보를 가져와 적절하게 사용하면 된다.

🚀결과

결과를 보면 이메일과 카카오 아이디가 함께 등록이 되었다.

🚨주의점

카카오의 경우 spring.security.oauth2.client.registration.kakao.client-authentication-method를 POST가 아닌 client_secret_post로 값을 설정해야 한다.

spring.security.oauth2.client.registration.kakao.client-authentication-method=POST(x)
spring.security.oauth2.client.registration.kakao.client-authentication-method=client_secret_post(o)

클라이언트와 사용하기위해 CORS를 등록을 해아한다.

// SecurityConfig
http
            .cors(corsCustomizer -> corsCustomizer.configurationSource(
                new CorsConfigurationSource() {
                    @Override
                    public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
                        CorsConfiguration configuration = new CorsConfiguration();

                        configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000"));
                        configuration.setAllowedMethods(Collections.singletonList("*"));
                        configuration.setAllowCredentials(true);
                        configuration.setMaxAge(3600L);

                        configuration.setExposedHeaders(Collections.singletonList("Set-Cookie"));
                        configuration.setExposedHeaders(Collections.singletonList("Authorization"));

                        return configuration;
                    }
                }));
@Configuration
public class CorsMvcConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry corsRegistry) {
        corsRegistry.addMapping("/**")
            .exposedHeaders("Set-Cookie")
            .allowedOrigins("https://localhost:3000");
    }
}

🎉DONE

참고자료 https://www.youtube.com/@xxxjjhhh

0개의 댓글