OAuth2를 사용하지 않을 경우

https://mountain-noroo.tistory.com/176

 

[Spring] Naver 로그인 (Oauth2 X)

네이버 애플리케이션 등록네이버 소셜 로그인 기능을 사용하기 위해서는 네이버 디벨로퍼스에서 네이버 애플리케이션 등록이 필요하다.https://developers.naver.com/ NAVER Developers네이버 오픈 API들을

mountain-noroo.tistory.com

 

OAuth2 라이브러리를 사용하지 않을 경우는 위 글을 참고해보자.

여기서는 기본적으로 구글, 네이버의 애플리케이션 등록이 끝났음을 전제로 포스팅을 작성하겠다.

개인적으로는 OAuth2를 사용하였을 때 훨씬 간편했으나 유연성은 직접 만들었을 때를 따라잡을 수 없다고 생각한다.

 

 

OAuth2란

Open Authorization 2.0 혹은 OAuth2.0은 웹 및 애플리케이션 인증 및 권한 부여를 위한 개방형 표준 프로토콜입니다. 이 프로토콜에서는 third-party 애플리케이션이 사용자의 리소스에 접근하기 위한 절차를 정의하고 서비스 제공자의 API를 사용할 수 있는 권한을 부여합니다. 대표적으로 네이버 로그인, 구글 로그인과 같은 소셜 미디어 간편 로그인이 있습니다. OAuth2.0을 사용해 third-party 애플리케이션이 사용자의 소셜미디어 프로필 정보에 접근할 수 있도록 합니다. B2B PRISM Live Studio 역시 OAuth2.0을 사용하여 권한을 관리하기 때문에 OAuth2.0의 기본 개념을 안내하고, 권한 부여 방법을 설명합니다.

네이버 클라우드 문서에서 퍼온 글이다.

https://guide.ncloud-docs.com/docs/b2bpls-oauth2

 

표준 프로토콜을 사용해 여러 소셜 로그인을 하나의 로직으로 처리할 수 있다는 장점이 있다.

 

 

Spring Security OAuth2 라이브러리 사용하기

build.gradle 라이브러리 추가

implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'

 

 

application.yml

    # OAuth 로그인
    security:
        oauth2:
            client:
                registration:
                    google:
                        client-id: ${SOCIAL_GOOGLE_CLIENT_ID}
                        client-secret: ${SOCIAL_GOOGLE_CLIENT_SECRET}
                        scope:
                            - email
                            - profile
                    naver:
                        client-id: ${SOCIAL_NAVER_CLIENT_ID}
                        client-secret: ${SOCIAL_NAVER_CLIENT_SECRET}
                        scope:
                            - name
                            - email
                        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

google, naver를 사용하기 위한 설정 값들을 세팅해준다.

 

 

OAuth2User

우선 기존의 UserDetails를 구현하던 클래스에서 OAuth2User를 구현한다.

@Getter
@RequiredArgsConstructor
public class UserPrincipal implements UserDetails, OAuth2User {
    private final User user;
    private Map<String, Object> attributes;

    @Override
    public Map<String, Object> getAttributes() {
        return attributes;
    }

    // ...
}

getAttributes 구현이 필수인데 모양만 흉내내고 직접 사용하진 않았다.

이렇게 소셜 로그인 유저도 UserPrincipal이 처리할 수 있게 된다.

 

 

DefaultOAuth2UserService

DefaultOAuth2UserService를 상속하여 새로운 서비스 클래스를 만든다.

@Service
public class OAuth2UserServiceImpl extends DefaultOAuth2UserService {
    private final List<OAuth2UserInfo> oAuth2UserInfoList;
    private final UserService userService;

    public OAuth2UserServiceImpl(UserService userService) {
        this.userService = userService;
        this.oAuth2UserInfoList = List.of(new NaverOAuth2UserInfo(), new GoogleOAuth2UserInfo());
    }

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        Assert.notNull(userRequest, "userRequest cannot be null");

        OAuth2User oAuth2User = super.loadUser(userRequest);

        String providerId = userRequest.getClientRegistration().getRegistrationId();
        OAuth2UserInfo oAuth2UserInfo = oAuth2UserInfoList.stream()
                .filter(userInfo -> userInfo.supports(providerId))
                .findFirst()
                .orElseThrow(() -> new OAuth2AuthenticationException("지원하지 않는 provider"));

        User user;
        try {
            user = this.userService.getUserByEmailWithSnsInfo(
                    oAuth2UserInfo.getEmailFromAttributes(oAuth2User.getAttributes()),
                    () -> new OAuth2AuthenticationException("존재하지 않는 유저")
            );

            if (user.getSnsInfo() == null) {
                this.userService.addSnsInfoToUser(user, providerId);
            }
        } catch (Exception e) {
            UserSignupRequestDto userSignupRequestDto = new UserSignupRequestDto();
            userSignupRequestDto.setEmail(oAuth2UserInfo.getEmailFromAttributes(oAuth2User.getAttributes()));
            userSignupRequestDto.setName(oAuth2UserInfo.getNameFromAttributes(oAuth2User.getAttributes()));
            userSignupRequestDto.setPassword(UUID.randomUUID().toString().substring(0, 8));
            userSignupRequestDto.setRole(User.Role.NORMAL);
            userSignupRequestDto.setStatusCode(User.StatusCode.ACTIVE);
            user = this.userService.signup(userSignupRequestDto, providerId);
        }

        return new UserPrincipal(user);
    }
}

loadUser의 구현이 필수인데 이는 OAuth2User를 반환한다.

 

request에서 RegistrationId를 가져와(yml의 spring.security.oauth2.client.registration.naver.client-name) 무슨 플랫폼인지 확인한다. 플랫폼 별 반환 값의 형식, 명칭 등이 조금씩 차이가 있기 때문에 해당하는 어트리뷰트를 가져와야 하기 때문.

 

그 이후는 이전 편에서 봤던 방식과 동일하다.

 

 

SecurityConfig

        http.oauth2Login(httpSecurityOAuth2LoginConfigurer -> httpSecurityOAuth2LoginConfigurer
                .loginPage("/login.html")
                .defaultSuccessUrl("/hellowrodl")
                .userInfoEndpoint(userInfoEndpointConfig -> userInfoEndpointConfig.userService(OAuth2UserServiceImpl))
                .successHandler(oauth2SuccessHandler())
        );

oauth2Login 설정도 SecurityConfig에 추가한다.

참고로 우리 프로젝트에선 successHandler에서 토큰 발급을 처리했다.

+ Recent posts