Skip to content

Commit

Permalink
Merge pull request #138 from CLAT-Project/feat-OAuth2
Browse files Browse the repository at this point in the history
feat : OAuth2 기본 구현 완료
  • Loading branch information
AKKDevMachine authored Jan 19, 2025
2 parents 5f4781b + e5244ab commit de45b1d
Show file tree
Hide file tree
Showing 12 changed files with 355 additions and 0 deletions.
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ dependencies {
implementation 'io.jsonwebtoken:jjwt-impl:0.12.3'
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3'

implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' //OAuth2 의존성 추가

implementation 'org.springframework.boot:spring-boot-starter-mail:3.2.2'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'
compileOnly 'org.projectlombok:lombok'
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/team_project/clat/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
Expand All @@ -16,12 +17,15 @@
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import team_project.clat.config.handler.CustomSuccessHandler;
import team_project.clat.jwt.JwtFilter;
import team_project.clat.jwt.JwtUtil;
import team_project.clat.jwt.LoginFilter;
import team_project.clat.jwt.CustomLogoutFilter;
import team_project.clat.oauth2.CustomClientRegistrationRepository;
import team_project.clat.repository.MemberRepository;
import team_project.clat.repository.TokenRepository;
import team_project.clat.service.CustomOAuth2UserService;

import java.util.Arrays;
import java.util.List;
Expand All @@ -37,6 +41,9 @@ public class SecurityConfig {
private final ObjectMapper objectMapper;
private final TokenRepository tokenRepository;
private final MemberRepository memberRepository;
private final CustomOAuth2UserService customOAuth2UserService;
private final CustomSuccessHandler customSuccessHandler;
private final CustomClientRegistrationRepository customClientRegistrationRepository;

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
Expand Down Expand Up @@ -82,6 +89,13 @@ public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
//http basic 인증 방식 disable
http.httpBasic((auth) -> auth.disable());

//oauth2
http.oauth2Login((oauth2) -> oauth2
.clientRegistrationRepository(customClientRegistrationRepository.clientRegistrationRepository())
.userInfoEndpoint((userInfoEndpointConfig) -> userInfoEndpointConfig
.userService(customOAuth2UserService))
.successHandler(customSuccessHandler));

//경로별 인가 작업
http.authorizeHttpRequests((auth) -> auth
.requestMatchers("/ws/**").permitAll()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package team_project.clat.config.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import team_project.clat.dto.CustomOAuth2User;

import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

@Component
public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {

//OAuth2User
CustomOAuth2User customUserDetails = (CustomOAuth2User) authentication.getPrincipal();

String username = customUserDetails.getUsername();
String email = customUserDetails.getEmail();
String name = customUserDetails.getName();

/*Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
Iterator<? extends GrantedAuthority> iterator = authorities.iterator();
GrantedAuthority auth = iterator.next();
String role = auth.getAuthority();*/

// 여기서부터 성공 flag 및 소셜 유저 정보 json 응답 코드 작성
// 응답 객체 설정
response.setStatus(HttpServletResponse.SC_OK); // 200 OK 응답 설정
response.setContentType("application/json"); // 응답 타입을 JSON으로 설정

// JSON 형식으로 성공 flag 및 username 반환
Map<String, Object> responseBody = new HashMap<>();
responseBody.put("success", true); // 성공 플래그
responseBody.put("username", username); // username
responseBody.put("name", name);
responseBody.put("email", email);

// ObjectMapper를 사용하여 JSON으로 변환 후 응답으로 작성
ObjectMapper objectMapper = new ObjectMapper();
String jsonResponse = objectMapper.writeValueAsString(responseBody);

response.getWriter().write(jsonResponse); // 응답 전송
}
}
1 change: 1 addition & 0 deletions src/main/java/team_project/clat/domain/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,5 @@ public void memberUserStatusSet(UserStatus userStatus){
public void memberNameSet(String name) { this.name = name; }

public void memberEmailSet(String email) { this.email = email; }

}
53 changes: 53 additions & 0 deletions src/main/java/team_project/clat/dto/CustomOAuth2User.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package team_project.clat.dto;

import lombok.RequiredArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.core.user.OAuth2User;
import team_project.clat.dto.request.OAuthMemberReqDTO;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

public class CustomOAuth2User implements OAuth2User {

private final OAuthMemberReqDTO oAuthMemberReqDTO;

public CustomOAuth2User(OAuthMemberReqDTO oAuthMemberReqDTO) {
this.oAuthMemberReqDTO = oAuthMemberReqDTO;
}

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

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collection = new ArrayList<>();

collection.add(new GrantedAuthority() {

@Override
public String getAuthority() {

return oAuthMemberReqDTO.getRole();
}
});

return collection;
}

@Override
public String getName() {
return oAuthMemberReqDTO.getName();
}

public String getUsername() {
return oAuthMemberReqDTO.getUsername();
}

public String getEmail() {
return oAuthMemberReqDTO.getEmail();
}
}
14 changes: 14 additions & 0 deletions src/main/java/team_project/clat/dto/request/OAuthMemberReqDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package team_project.clat.dto.request;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class OAuthMemberReqDTO {

private String role;
private String name;
private String username;
private String email;
}
33 changes: 33 additions & 0 deletions src/main/java/team_project/clat/dto/response/GoogleResDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package team_project.clat.dto.response;

import java.util.Map;

public class GoogleResDTO implements OAuth2Response{

private final Map<String, Object> attribute;

public GoogleResDTO(Map<String, Object> attribute) {

this.attribute = attribute;
}

@Override
public String getProvider() {
return "google";
}

@Override
public String getProviderId() {
return attribute.get("sub").toString();
}

@Override
public String getEmail() {
return attribute.get("email").toString();
}

@Override
public String getName() {
return attribute.get("name").toString();
}
}
33 changes: 33 additions & 0 deletions src/main/java/team_project/clat/dto/response/NaverResDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package team_project.clat.dto.response;

import java.util.Map;

public class NaverResDTO implements OAuth2Response{

private final Map<String, Object> attribute;

public NaverResDTO(Map<String, Object> attribute) {

this.attribute = (Map<String, Object>) attribute.get("response");
}

@Override
public String getProvider() {
return "naver";
}

@Override
public String getProviderId() {
return attribute.get("id").toString();
}

@Override
public String getEmail() {
return attribute.get("email").toString();
}

@Override
public String getName() {
return attribute.get("name").toString();
}
}
13 changes: 13 additions & 0 deletions src/main/java/team_project/clat/dto/response/OAuth2Response.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package team_project.clat.dto.response;

public interface OAuth2Response {

String getProvider(); // 제공자(Ex. naver, google, ...)

String getProviderId(); //제공자에서 발급해주는 아이디(번호)

String getEmail();

String getName();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package team_project.clat.oauth2;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;

@Configuration
@RequiredArgsConstructor
public class CustomClientRegistrationRepository {

private final SocialClientRegistration socialClientRegistration;

public ClientRegistrationRepository clientRegistrationRepository(){
return new InMemoryClientRegistrationRepository(
socialClientRegistration.naverClientRegistration(),
socialClientRegistration.googleClientRegistration());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package team_project.clat.oauth2;

import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;
import org.springframework.stereotype.Component;

@Component
public class SocialClientRegistration {

public ClientRegistration naverClientRegistration() {

return ClientRegistration.withRegistrationId("naver")
.clientId("nTYJzjk224aAD0gGHBn9")
.clientSecret("6gqc9cugKC")
.redirectUri("https://clat.duckdns.org/login/oauth2/code/naver")
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.scope("name", "email")
.authorizationUri("https://nid.naver.com/oauth2.0/authorize")
.tokenUri("https://nid.naver.com/oauth2.0/token")
.userInfoUri("https://openapi.naver.com/v1/nid/me")
.userNameAttributeName("response")
.build();
}

public ClientRegistration googleClientRegistration() {

return ClientRegistration.withRegistrationId("google")
.clientId("52474534592-p74ookn70s1m2l1qjqvf66u63bslcglu.apps.googleusercontent.com")
.clientSecret("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")
.redirectUri("http://localhost:8080/login/oauth2/code/google")
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.scope("profile", "email")
.authorizationUri("https://accounts.google.com/o/oauth2/v2/auth")
.tokenUri("https://www.googleapis.com/oauth2/v4/token")
.jwkSetUri("https://www.googleapis.com/oauth2/v3/certs")
.issuerUri("https://accounts.google.com")
.userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo")
.userNameAttributeName(IdTokenClaimNames.SUB)
.build();
}
}
Loading

0 comments on commit de45b1d

Please sign in to comment.