Skip to content

Commit 1ae6022

Browse files
committed
feat : Security Config 설정 및 OAuth 구현#2
1 parent cf5aafd commit 1ae6022

File tree

7 files changed

+88
-24
lines changed

7 files changed

+88
-24
lines changed

build.gradle.kts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,13 @@ dependencies {
2828
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
2929
implementation("org.springframework.boot:spring-boot-starter-validation")
3030
implementation("org.springframework.boot:spring-boot-starter-web")
31-
31+
implementation("io.github.cdimascio:java-dotenv:5.2.2")
3232
implementation("org.springframework.boot:spring-boot-starter-security")
3333
implementation("org.springframework.boot:spring-boot-starter-oauth2-client")
3434
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0")
3535
implementation("io.jsonwebtoken:jjwt-api:0.12.3")
3636
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.12.3")
3737
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.12.3")
38-
implementation("me.paulschwarz:spring-dotenv:4.0.0")
3938
compileOnly("org.projectlombok:lombok")
4039
developmentOnly("org.springframework.boot:spring-boot-devtools")
4140
runtimeOnly("com.h2database:h2")

src/main/java/com/back/BackApplication.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.back;
22

3+
import io.github.cdimascio.dotenv.Dotenv;
34
import org.springframework.boot.SpringApplication;
45
import org.springframework.boot.autoconfigure.SpringBootApplication;
56
import org.springframework.scheduling.annotation.EnableScheduling;
@@ -9,6 +10,11 @@
910
public class BackApplication {
1011

1112
public static void main(String[] args) {
13+
Dotenv dotenv = Dotenv.load();
14+
System.out.println("KAKAO_OAUTH2_CLIENT_ID: " + dotenv.get("KAKAO_OAUTH2_CLIENT_ID"));
15+
System.out.println("GOOGLE_OAUTH2_CLIENT_ID: " + dotenv.get("GOOGLE_OAUTH2_CLIENT_ID"));
16+
System.out.println("NAVER_OAUTH2_CLIENT_ID: " + dotenv.get("NAVER_OAUTH2_CLIENT_ID"));
17+
1218
SpringApplication.run(BackApplication.class, args);
1319
}
1420

src/main/java/com/back/domain/user/service/UserService.java

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public User findById(Long id) {
2323
.orElseThrow(() -> new IllegalArgumentException("User not found. id=" + id));
2424
}
2525

26-
// 소셜로그인으로 회원가입 & 회원 정보 수정
26+
// 소셜로그인으로 회원가입 & 회원 정보 수정
2727
public RsData<User> modifyOrJoin(String oauthId, String email, String nickname) {
2828

2929
//oauthId로 기존 회원인지 확인
@@ -46,11 +46,15 @@ public User joinSocial(String oauthId, String email, String nickname){
4646
throw new ServiceException(409, "이미 존재하는 계정입니다.");
4747
});
4848

49+
// 고유한 닉네임 생성
50+
String uniqueNickname = generateUniqueNickname(nickname);
51+
4952
User user = User.builder()
5053
.email(email)
54+
.nickname(uniqueNickname)
5155
.profileImgUrl(null)
5256
.abvDegree(0.0)
53-
.role("USER") //기본 권한 USER. 관리자면 "ADMIN"으로 설정하시면 됩니다
57+
.role("USER")
5458
.oauthId(oauthId)
5559
.build();
5660

@@ -66,17 +70,17 @@ public RsData<User> findOrCreateOAuthUser(String oauthId, String email, String n
6670
Optional<User> existingUser = userRepository.findByOauthId(oauthId);
6771

6872
if (existingUser.isPresent()) {
69-
// 기존 사용자 업데이트 (이메일, 닉네임 변경 가능)
73+
// 기존 사용자 업데이트 (이메일만 업데이트)
7074
User user = existingUser.get();
7175
user.setEmail(email);
72-
user.setNickname(nickname);
7376
return RsData.of(200, "기존 사용자 정보 업데이트", userRepository.save(user));
7477
} else {
75-
// 새 사용자 생성
78+
// 새 사용자 생성 - 고유한 닉네임 생성
79+
String uniqueNickname = generateUniqueNickname(nickname);
7680
User newUser = User.builder()
7781
.oauthId(oauthId)
7882
.email(email)
79-
.nickname(nickname)
83+
.nickname(uniqueNickname)
8084
.provider(provider)
8185
.role("USER")
8286
.createdAt(LocalDateTime.now())
@@ -85,4 +89,22 @@ public RsData<User> findOrCreateOAuthUser(String oauthId, String email, String n
8589
}
8690
}
8791

92+
private String generateUniqueNickname(String baseNickname) {
93+
// null이거나 빈 문자열인 경우 기본값 설정
94+
if (baseNickname == null || baseNickname.trim().isEmpty()) {
95+
baseNickname = "User";
96+
}
97+
98+
String nickname = baseNickname;
99+
int counter = 1;
100+
101+
// 중복 체크 및 고유한 닉네임 생성
102+
while (userRepository.findByNickname(nickname).isPresent()) {
103+
nickname = baseNickname + counter;
104+
counter++;
105+
}
106+
107+
return nickname;
108+
}
109+
88110
}

src/main/java/com/back/global/security/CustomAuthenticationFilter.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ private void work(HttpServletRequest request, HttpServletResponse response, Filt
5555
if (
5656
//추후 로그인 필요한 api 추가 설정
5757
uri.startsWith("/h2-console") ||
58-
uri.startsWith("/api/login/oauth2/") ||
59-
(method.equals("GET") && uri.equals("api/~~")) ||
60-
(method.equals("POST") && uri.equals("/api/user/login"))
58+
uri.startsWith("/login/oauth2/") ||
59+
(method.equals("GET") && uri.equals("/api/~~")) ||
60+
(method.equals("POST") && uri.equals("/api/~"))
6161

6262
) {
6363
filterChain.doFilter(request, response);

src/main/java/com/back/global/security/CustomOAuth2UserService.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,9 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic
3535
case "KAKAO" -> {
3636
Map<String, Object> attributes = oAuth2User.getAttributes();
3737
Map<String, Object> attributesProperties = (Map<String, Object>) attributes.get("properties");
38-
Map<String, Object> kakaoAccount = (Map<String, Object>) attributes.get("kakao_account");
3938

4039
oauthUserId = oAuth2User.getName();
4140
nickname = (String) attributesProperties.get("nickname");
42-
email = (String) kakaoAccount.get("email");
4341
}
4442
case "GOOGLE" -> {
4543
oauthUserId = oAuth2User.getName();
@@ -59,13 +57,25 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic
5957
// OAuth ID를 제공자와 함께 저장 (예: kakao_123456789)
6058
String uniqueOauthId = providerTypeCode.toLowerCase() + "_" + oauthUserId;
6159

60+
log.debug("OAuth2 user info - oauthUserId: {}, email: {}, nickname: {}, provider: {}",
61+
oauthUserId, email, nickname, providerTypeCode);
62+
6263
User user = userService.findOrCreateOAuthUser(uniqueOauthId, email, nickname, providerTypeCode).data();
6364

65+
log.debug("User from DB - id: {}, email: {}, nickname: {}",
66+
user.getId(), user.getEmail(), user.getNickname());
67+
68+
// null 체크 및 기본값 설정
69+
String userEmail = user.getEmail() != null && !user.getEmail().trim().isEmpty()
70+
? user.getEmail() : "[email protected]";
71+
String userNickname = user.getNickname() != null && !user.getNickname().trim().isEmpty()
72+
? user.getNickname() : "Unknown User";
73+
6474
// securityContext
6575
return new SecurityUser(
6676
user.getId(),
67-
user.getEmail(),
68-
user.getNickname(),
77+
userEmail,
78+
userNickname,
6979
user.getAuthorities(),
7080
oAuth2User.getAttributes()
7181
);

src/main/java/com/back/global/security/SecurityConfig.java

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@ public class SecurityConfig {
2121
private final CustomOAuth2UserService customOAuth2UserService;
2222
private final CustomOAuth2LoginSuccessHandler oauth2SuccessHandler;
2323
private final CustomOAuth2LoginFailureHandler oauth2FailureHandler;
24+
private final CustomOAuth2AuthorizationRequestResolver customOAuth2AuthorizationRequestResolver;
2425

2526
public SecurityConfig(CustomOAuth2UserService customOAuth2UserService,
2627
CustomOAuth2LoginSuccessHandler oauth2SuccessHandler,
27-
CustomOAuth2LoginFailureHandler oauth2FailureHandler) {
28+
CustomOAuth2LoginFailureHandler oauth2FailureHandler,
29+
CustomOAuth2AuthorizationRequestResolver customOAuth2AuthorizationRequestResolver) {
2830
this.customOAuth2UserService = customOAuth2UserService;
2931
this.oauth2SuccessHandler = oauth2SuccessHandler;
3032
this.oauth2FailureHandler = oauth2FailureHandler;
33+
this.customOAuth2AuthorizationRequestResolver = customOAuth2AuthorizationRequestResolver;
3134
}
3235

3336
@Bean
@@ -37,25 +40,46 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
3740
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
3841
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
3942
.authorizeHttpRequests(auth -> auth
40-
.requestMatchers("/","/api/**").permitAll()
43+
.requestMatchers("/").permitAll()
4144
.requestMatchers("/h2-console/**").permitAll()
4245
.requestMatchers("/oauth2/**").permitAll()
46+
.requestMatchers("/login/oauth2/**").permitAll()
4347
.requestMatchers("/swagger-ui/**", "/api-docs/**").permitAll()
48+
.requestMatchers("/api/user/**").permitAll()
49+
.requestMatchers("/api/cocktail/**").permitAll()
50+
51+
52+
// 회원 or 인증된 사용자만 가능
53+
.requestMatchers("/api/admin/**").hasRole("ADMIN")
54+
// .requestMatchers("/api/cocktail/detail~~").authenticated()
55+
56+
//그 외에는 인증해야함
4457
.anyRequest().authenticated()
4558
)
59+
.formLogin(AbstractHttpConfigurer::disable)
60+
.httpBasic(AbstractHttpConfigurer::disable)
4661
.oauth2Login(oauth2 -> oauth2
4762
.authorizationEndpoint(authorization -> authorization
48-
.baseUri("/api/oauth2/authorization")
49-
)
50-
.redirectionEndpoint(redirection -> redirection
51-
.baseUri("/api/login/oauth2/code/*")
63+
.authorizationRequestResolver(customOAuth2AuthorizationRequestResolver)
5264
)
5365
.userInfoEndpoint(userInfo -> userInfo
5466
.userService(customOAuth2UserService)
5567
)
5668
.successHandler(oauth2SuccessHandler)
5769
.failureHandler(oauth2FailureHandler)
5870
)
71+
.exceptionHandling(exceptionHandling -> exceptionHandling
72+
.authenticationEntryPoint((request, response, authException) -> {
73+
response.setContentType("application/json;charset=UTF-8");
74+
response.setStatus(401);
75+
response.getWriter().write("{\"code\":401,\"message\":\"로그인 후 이용해주세요.\"}");
76+
})
77+
.accessDeniedHandler((request, response, accessDeniedException) -> {
78+
response.setContentType("application/json;charset=UTF-8");
79+
response.setStatus(403);
80+
response.getWriter().write("{\"code\":403,\"message\":\"권한이 없습니다.\"}");
81+
})
82+
)
5983
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin));
6084

6185
return http.build();

src/main/resources/application.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ spring:
22
profiles:
33
active: dev
44

5+
config:
6+
import: optional:file:.env[.properties]
7+
58
security:
69
oauth2:
710
client:
@@ -11,11 +14,11 @@ spring:
1114
scope: profile_nickname # 카카오 닉네임만 가져옴
1215
client-name: Kakao
1316
authorization-grant-type: authorization_code
14-
redirect-uri: '${BASE_URL}/api/{action}/oauth2/code/{registrationId}'
17+
redirect-uri: '${BASE_URL}/{action}/oauth2/code/{registrationId}'
1518
google:
1619
client-id: ${GOOGLE_OAUTH2_CLIENT_ID}
1720
client-secret: ${GOOGLE_OAUTH2_CLIENT_SECRET}
18-
redirect-uri: '${BASE_URL}/api/{action}/oauth2/code/{registrationId}'
21+
redirect-uri: '${BASE_URL}/{action}/oauth2/code/{registrationId}'
1922
client-name: Google
2023
scope: profile
2124
naver:
@@ -24,7 +27,7 @@ spring:
2427
scope: profile_nickname # 네이버 닉네임만 가져옴
2528
client-name: Naver
2629
authorization-grant-type: authorization_code
27-
redirect-uri: '${BASE_URL}/api/{action}/oauth2/code/{registrationId}'
30+
redirect-uri: '${BASE_URL}/{action}/oauth2/code/{registrationId}'
2831
provider:
2932
kakao:
3033
authorization-uri: https://kauth.kakao.com/oauth/authorize

0 commit comments

Comments
 (0)