brunch

You can make anything
by writing

C.S.Lewis

by 갱그리 May 30. 2016

Spring 소셜로그인(3)
가입/로그인 구현하기

5/30 개발일기

이제 완성된 SocialConfig, SecurityConfig를 기반으로 회원가입/로그인 프로세스를 구현해보려고 한다. 우리가 구현해야 할 기본 프로세스는 아래와 같다.


회원가입 프로세스

1. /auth/{providerId}로 인증 요청

2. 요청 후 생성된 Connection 을 받아 사용자 정보를 끌어옴 (fetchUserProfile)

3. 회원가입 폼으로 이동시켜 사용자 정보 및 개인정보 이용 동의를 받아냄. 

4. 회원가입 폼에서 submit된 데이터로 User 객체를 생성함(DB에도 insert)

5. User 객체로부터 UserDetails 객체를 생성함

6. UserDetails 객체를 SecurityContext에 담아 인증한 사용자로 만듬


1. /auth/{providerId}로 인증 요청

앞서 언급한 것처럼, 기본적으로 brant hwang님의 블로그(http://brantiffy.axisj.com/archives/29)의 예제를 기반으로 개발했는데, 그 블로그에 가보면 댓글에 /auth/{providerId} 라는 것을 어디서 매핑해주냐는 질문이 있다. 나도 첨에는 이게 궁금했다.


컨트롤러 어디에서도 매핑되지 않은 Url인데 어떻게 찾아가는걸까? 찾아보니 oAuth2.0 문서에서 /auth/{providerId} 라는 Url을 인증 Url로 지원한다는 문서를 발견했다. 그 문서 링크는 잃어버렸는데, linkedin 개발자 포럼에서 예제를 발견했다. https://developer.linkedin.com/docs/oauth2


인증요청은 /auth/facebook 으로 post 요청하면 된다. submit할 때 provider 마다 별도로 지정하고 있는 scope를 포함시켜야 한다. 예제는 다음과 같다.


login.jsp

<a class="btn btn-block btn-social btn-facebook"  onclick="document.facebook.submit();">
    <span class="fa fa-facebook""></span>Facebook 계정으로 로그인하기
  </a>
  <a class="btn btn-block btn-social btn-google"  onclick="document.google.submit();">
    <span class="fa fa-google"></span> Google 계정으로 로그인하기
  </a>

<!--  hidden scope-->
<form action="/auth/facebook" name="facebook">
<input type="hidden" name="scope" value="email,user_friends"/></form>

<form action="/auth/google" name="google">
<input type="hidden" name="scope" value="https://www.googleapis.com/auth/plus.login" /></form>


2. 요청 후 생성된 Connection 을 받아 사용자 정보를 끌어옴 (fetchUserProfile)

@RequestMapping(value = "/signup", method = RequestMethod.GET)
public String redirectRequestToRegistrationPage(WebRequest request, ModelMap modelMap)throws Exception {
Connection<?> connection = providerSignInUtils.getConnectionFromSession(request);

//로그인한 유저 정보 끌어오기
UserProfile userProfile = connection.fetchUserProfile();
UserCreateRequestVO userCreateRequestVO = UserCreateRequestVO.fromSocialUserProfile(userProfile);
modelMap.put("user", userProfile);

/auth/{providerId} 로 리턴된 사용자는 provider의 로그인 화면으로 이동하고, 로그인하고 이용 동의를 하고 난 뒤에는 /signup 으로 리턴된다. (이미 동의되어 있는 사용자는 /signin 으로 리턴되는 듯 하다.) 그러면 /signup URL을 캐치하여 위와 같이 Controller 를 작성해주면 된다. 위와 같이 Connection 을 생성하여 fetchUserProfile() 로 로그인한 사용자의 프로필을 끌어올 수 있다. 그 이후 회원가입 폼으로 redirect 하면서 email, name 값들을 적당히 배치해주면 된다. 


3~4는 기초적인 내용이므로 Pass. (여기서 만드는 User 객체는 각자의 User table 에 따르는 custom한 데이터 타입임)


5. User 객체로부터 UserDetails 객체를 생성함

public static void CreateNewAuthenticationbyUser(User user){
        FrontUserDetail frontUserDetail = new FrontUserDetail(user);
        Authentication authentication = new UsernamePasswordAuthenticationToken(frontUserDetail, null, frontUserDetail.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);        
 }

Brant Hwang님의 블로그에 가보면 그분의 예제에는 SocialUserDetails 인터페이스를 기반으로 하는 FrontUserDetail 타입의 UserDetails 객체를 생성한다. SocialUserDetails 인터페이스도 가보면 결국엔 UserDetails 를 상속한다. UserDetails 객체에 대해서는 앞서 SecurityConfig 부분에서 상세히 다루었으므로 패스. 여기서 위와 같이 User를 생성하여 SecurityContext에 인증시킨다. 그러면 회원가입 끗 ^_^/



로그인 프로세스


간단하다! input 필드의 네임을 각각 "username"과 "password"로 작성한 뒤 securityConfig.java에서 작성한 login-process-url 의 url 로 post 전송하면 된다. 그러면 해당 로그인 정보가 맞는 지 확인한 후 맞지 않으면 지정된 failure Handler로 오류를 리턴한다. 


SecurityConfig.java

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .anonymous().and()
                .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/", true)
                .loginProcessingUrl("/login/auth")
                .failureHandler(new CustomLoginFailureHandler())

login.jsp

    
<form action="/login/auth" method="post">
    <input type="text" class="form-control" name="username" />
    <input type="password" name="password" />
    ...
</form>



브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari