Skip to content

Commit

Permalink
[FIX] 네이버 인증 구현 누락 수정 #9 (#32)
Browse files Browse the repository at this point in the history
- OAuth관련 코드가 누락된 부분이 있어 다시 올립니다.
  • Loading branch information
Gyaak authored Aug 2, 2024
1 parent fd7c9e2 commit 559881b
Show file tree
Hide file tree
Showing 10 changed files with 306 additions and 4 deletions.
34 changes: 34 additions & 0 deletions server/src/main/java/com/talkka/server/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,38 @@
package com.talkka.server.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.SecurityFilterChain;

import com.talkka.server.oauth.OAuth2LoginFailureHandler;
import com.talkka.server.oauth.OAuth2LoginSuccessHandler;
import com.talkka.server.oauth.service.CustomOAuth2Service;

import lombok.RequiredArgsConstructor;

@Configuration
@RequiredArgsConstructor
public class SecurityConfig {

private final CustomOAuth2Service customOAuth2Service;
private final OAuth2LoginSuccessHandler oAuth2LoginSuccessHandler;
private final OAuth2LoginFailureHandler oAuth2LoginFailureHandler;

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.cors(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/auth/**").permitAll()
.anyRequest().authenticated())
.oauth2Login(oauth -> oauth
.userInfoEndpoint(userInfo -> userInfo.userService(customOAuth2Service))
.successHandler(oAuth2LoginSuccessHandler)
.failureHandler(oAuth2LoginFailureHandler)
);
return http.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,25 @@
package com.talkka.server.oauth;

public class OAuth2LoginFailureHandler {
import java.io.IOException;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@Component
public class OAuth2LoginFailureHandler extends SimpleUrlAuthenticationFailureHandler {

@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {

request.getSession().invalidate();
String defaultFailureUrl = "/auth/login";
setDefaultFailureUrl(defaultFailureUrl);
super.onAuthenticationFailure(request, response, exception);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,32 @@
package com.talkka.server.oauth;

public class OAuth2LoginSuccessHandler {
import java.io.IOException;

import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import com.talkka.server.oauth.domain.AuthUserDto;
import com.talkka.server.oauth.domain.CustomUserPrincipal;
import com.talkka.server.user.service.UserService;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class OAuth2LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

private final UserService userService;

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
AuthUserDto userDto = ((CustomUserPrincipal)authentication.getPrincipal()).getUser();
if (!userDto.getIsRegistered()) {
getRedirectStrategy().sendRedirect(request, response, "/auth/signUp");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,65 @@
package com.talkka.server.oauth.controller;

import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.talkka.server.oauth.domain.AuthUserDto;
import com.talkka.server.oauth.domain.CustomUserPrincipal;
import com.talkka.server.user.dto.UserCreateDto;
import com.talkka.server.user.dto.UserDto;
import com.talkka.server.user.service.UserService;

import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;

@Controller
@RequiredArgsConstructor
@RequestMapping("/auth")
public class AuthController {

private final UserService userService;

@GetMapping("/login")
public String loginForm() {
return "loginForm";
}

@GetMapping("/signUp")
public String signUpForm(Model model, @AuthenticationPrincipal CustomUserPrincipal principal) {
AuthUserDto userDto = principal.getUser();
model.addAttribute("user", userDto);
return "signUpForm";
}

@SuppressWarnings("checkstyle:WhitespaceAround")
@PostMapping("/signUp")
public String signUp(@RequestParam("nickname") String nickname,
Model model,
@AuthenticationPrincipal CustomUserPrincipal principal,
HttpServletRequest request) {
if (userService.isDuplicatedNickname(nickname)) {
return "signUpForm";
}
AuthUserDto authUserDto = principal.getUser();
UserCreateDto userCreateDto = UserCreateDto.builder()
.name(authUserDto.getName())
.email(authUserDto.getEmail())
.oauthProvider(authUserDto.getProvider())
.nickname(nickname)
.accessToken(authUserDto.getAccessToken())
.build();
UserDto user = userService.createUser(userCreateDto);
request.getSession().invalidate();
return "redirect:/";
}

@GetMapping("/login/naver")
public String loginWithNaver() {
return "redirect:https://nid.naver.com/oauth2.0/authorize";
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
package com.talkka.server.oauth.domain;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class AuthUserDto {
private String name;
private String email;
private String accessToken;
private String provider;
private Boolean isRegistered;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,34 @@
package com.talkka.server.oauth.domain;

public class CustomUserPrincipal {
import java.util.Collection;
import java.util.Collections;
import java.util.Map;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.core.user.OAuth2User;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public class CustomUserPrincipal implements OAuth2User {
private final AuthUserDto user;
private final Map<String, Object> attributes;

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

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"));
}

@Override
public String getName() {
return user.getName();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,46 @@
package com.talkka.server.oauth.service;

public class CustomOAuth2Service {
import java.util.HashMap;
import java.util.Map;

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.talkka.server.oauth.domain.AuthUserDto;
import com.talkka.server.oauth.domain.CustomUserPrincipal;
import com.talkka.server.user.dao.UserRepository;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class CustomOAuth2Service extends DefaultOAuth2UserService {

private final UserRepository userRepository;

@SuppressWarnings("checkstyle:RegexpSingleline")
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(userRequest);
Map<String, Object> tmpMap = oAuth2User.getAttribute("response");
assert tmpMap != null;
String email = (String)tmpMap.get("email");
String name = (String)tmpMap.get("name");
String accessToken = userRequest.getAccessToken().getTokenValue();
String provider = userRequest.getClientRegistration().getClientName();
boolean isRegistered = userRepository.existsByEmail(email);

AuthUserDto userDto = AuthUserDto.builder()
.name(name)
.email(email)
.accessToken(accessToken)
.provider(provider)
.isRegistered(isRegistered)
.build();

return new CustomUserPrincipal(userDto, new HashMap<>());
}
}
10 changes: 10 additions & 0 deletions server/src/main/resources/static/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
이건 홈이에요
</body>
</html>
11 changes: 11 additions & 0 deletions server/src/main/resources/templates/loginForm.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인페이지</title>
</head>
<body>
<h1>로그인 페이지 입니다/</h1>
<a href="/oauth2/authorization/naver" role="button">Naver Login</a>
</body>
</html>
51 changes: 51 additions & 0 deletions server/src/main/resources/templates/signUpForm.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>회원가입 페이지</title>
<script>
function submitForm() {
const nickname = document.getElementById('nickname').value;

const formData = new FormData();
formData.append('name', document.getElementById('name').value);
formData.append('email', document.getElementById('email').value);
formData.append('nickname', nickname);

fetch('/auth/signUp', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
// Handle success (e.g., redirect to another page or show a success message)
})
.catch((error) => {
console.error('Error:', error);
// Handle error (e.g., show an error message)
});
}
</script>
</head>
<body>
<h1>회원가입 페이지 입니다</h1>
<form onsubmit="event.preventDefault(); submitForm();">
<div>
<label for="name">이름:</label>
<input type="text" id="name" name="name" th:value="${user.name}" readonly>
</div>
<div>
<label for="email">이메일:</label>
<input type="email" id="email" name="email" th:value="${user.email}" readonly>
</div>
<div>
<label for="nickname">닉네임:</label>
<input type="text" id="nickname" name="nickname" required>
</div>
<div>
<button type="submit">회원가입</button>
</div>
</form>
</body>
</html>

0 comments on commit 559881b

Please sign in to comment.