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>