Skip to content

Commit a5f6fa4

Browse files
authored
Refactor : user테이블 AuthType 분리 (#72)
* refactor : SecurityPath 클래스로 public 경로 한곳에서 관리 * feature : userAuthType 테이블 생성 * feature : user-userAuthType 테이블 분리 및 연관관계 설정 * 중복 가입시, UserAuthType테이블에 Authtype만 추가 * refator : User의 AuthType을 UserAuthType 테이블로 분리 이메일, 소셜로그인 모두 해도, 이메일이 같다면 동일유저로 취급 * refactor : dev 머지 * refactor : 회원 가입 유형에 따른 비밀번호 변경 로직 변경 소셜 가입만 한 회원은 비번 변경 불가, 이메일 가입이 되어있으면 변경 가능. 이메일 가입을 한 경우에 비번이 회원이 설정한 비번으로 들어가게 됨. 소셜만 가입하면 UUID로 랜덤값 지정 * refactor : 코드래빗 의견 반영하여 자잘한 코드 변경 * refactor : 로그아웃 요청 경로 수정 '/api/auth/logout' -> '/api/logout'
1 parent 5e46ee6 commit a5f6fa4

File tree

18 files changed

+278
-118
lines changed

18 files changed

+278
-118
lines changed

src/main/java/org/ezcode/codetest/application/usermanagement/auth/service/AuthService.java

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.ezcode.codetest.application.usermanagement.auth.service;
22

3+
import java.util.List;
34
import java.util.concurrent.TimeUnit;
45

56
import org.ezcode.codetest.application.usermanagement.auth.dto.response.RefreshTokenResponse;
@@ -11,6 +12,7 @@
1112
import org.ezcode.codetest.domain.user.exception.AuthException;
1213
import org.ezcode.codetest.domain.user.exception.AuthExceptionCode;
1314
import org.ezcode.codetest.domain.user.model.entity.User;
15+
import org.ezcode.codetest.domain.user.model.entity.UserAuthType;
1416
import org.ezcode.codetest.domain.user.model.enums.AuthType;
1517
import org.ezcode.codetest.domain.user.service.UserDomainService;
1618
import org.ezcode.codetest.common.security.util.JwtUtil;
@@ -40,6 +42,7 @@ public class AuthService {
4042
*/
4143
@Transactional
4244
public SignupResponse signup(SignupRequest signupRequest) {
45+
4346
userDomainService.checkEmailUnique(signupRequest.getEmail());
4447

4548
if (!signupRequest.getPassword().equals(signupRequest.getPasswordConfirm())){
@@ -48,23 +51,46 @@ public SignupResponse signup(SignupRequest signupRequest) {
4851

4952
String encodedPassword = userDomainService.encodePassword(signupRequest.getPassword());
5053

54+
User existUser = userDomainService.getUserByEmail(signupRequest.getEmail());
5155

52-
User newUser = User.emailUser(
53-
signupRequest.getEmail(),
54-
encodedPassword,
55-
signupRequest.getUsername(),
56-
signupRequest.getNickname(),
57-
signupRequest.getAge()
58-
);
59-
60-
userDomainService.createUser(newUser);
56+
String bearToken;
6157

62-
String bearToken = jwtUtil.createToken(
63-
newUser.getId(),
64-
newUser.getEmail(),
65-
newUser.getRole(),
66-
newUser.getUsername(),newUser.getNickname(),
67-
newUser.getTier());
58+
//만약 아예 유저 테이블에 없으면 둘 다 저장
59+
if (existUser == null) {
60+
User newUser = User.emailUser(
61+
signupRequest.getEmail(),
62+
encodedPassword,
63+
signupRequest.getUsername(),
64+
signupRequest.getNickname(),
65+
signupRequest.getAge()
66+
);
67+
UserAuthType userAuthType = new UserAuthType(newUser, AuthType.EMAIL);
68+
userDomainService.createUser(newUser);
69+
userDomainService.createUserAuthType(userAuthType);
70+
71+
bearToken = jwtUtil.createToken(
72+
newUser.getId(),
73+
newUser.getEmail(),
74+
newUser.getRole(),
75+
newUser.getUsername(),newUser.getNickname(),
76+
newUser.getTier());
77+
} else {
78+
//유저 테이블에는 존재하다면 AuthType만 추가
79+
UserAuthType userAuthType = new UserAuthType(existUser, AuthType.EMAIL);
80+
userDomainService.createUserAuthType(userAuthType);
81+
82+
//로컬 가입(이메일)은 안되어있는데 소셜은 되어있는 경우이므로, UUID 비번을 사용자가 지정한 비번으로 변경한다. -> 이후 비번 변경하면 User테이블에서 변경하면됨.
83+
existUser.modifyPassword(encodedPassword);
84+
log.info("유저 타입 저장 완료 {}", userAuthType);
85+
86+
bearToken = jwtUtil.createToken(
87+
existUser.getId(),
88+
existUser.getEmail(),
89+
existUser.getRole(),
90+
existUser.getUsername(),
91+
existUser.getNickname(),
92+
existUser.getTier());
93+
}
6894

6995
return SignupResponse.from(bearToken);
7096
}
@@ -81,9 +107,10 @@ public SigninResponse signin(@Valid SigninRequest signinRequest) {
81107
User loginUser = userDomainService.getUser(signinRequest.getEmail());
82108

83109
userDomainService.isDeletedUser(loginUser);
110+
List<AuthType> userAuthTypes = userDomainService.getUserAuthTypes(loginUser);
84111

85112
//OAuth 가입 유저는 일반 로그인 불가능(향후 이메일과 소셜 모두 가입되어있는 회원의 경우 로그인 가능할 수 있도록 리팩토링)
86-
if (!loginUser.getAuthType().equals(AuthType.EMAIL)) {
113+
if (!userAuthTypes.contains(AuthType.EMAIL)) {
87114
throw new AuthException(AuthExceptionCode.AUTH_TYPE_MISMATCH);
88115
}
89116

@@ -115,6 +142,7 @@ public SigninResponse signin(@Valid SigninRequest signinRequest) {
115142
return SigninResponse.from(bearToken, refreshToken);
116143
}
117144

145+
118146
public LogoutResponse logout(Long userId, String token) {
119147

120148
Long expiration = jwtUtil.getRemainingTime(token);

src/main/java/org/ezcode/codetest/application/usermanagement/user/service/UserService.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public ChangeUserPasswordResponse modifyUserPassword(AuthUser authUser, ChangeUs
7979
User user = userDomainService.getUserById(authUser.getId());
8080

8181
//소셜로그인 회원은 변경 불가
82-
if (!user.getAuthType().equals(AuthType.EMAIL)) {
82+
if (!userDomainService.getUserAuthTypes(user).contains(AuthType.EMAIL)) {
8383
throw new AuthException(AuthExceptionCode.NOT_EMAIL_USER);
8484
}
8585

@@ -104,7 +104,6 @@ public WithdrawUserResponse withdrawUser(AuthUser authUser) {
104104

105105
user.setDeleted();
106106

107-
108107
redisTemplate.delete("RefreshToken:"+authUser.getId());
109108

110109
return new WithdrawUserResponse("탈퇴가 완료되었습니다");

src/main/java/org/ezcode/codetest/common/security/config/SecurityConfig.java

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package org.ezcode.codetest.common.security.config;
22

3+
import org.ezcode.codetest.common.security.util.SecurityPath;
34
import org.ezcode.codetest.domain.user.service.CustomOAuth2UserService;
45
import org.ezcode.codetest.common.security.hander.CustomSuccessHandler;
56
import org.ezcode.codetest.common.security.util.ExceptionHandlingFilter;
67
import org.ezcode.codetest.common.security.util.JwtFilter;
78
import org.ezcode.codetest.common.security.util.JwtUtil;
9+
import org.springframework.boot.web.servlet.FilterRegistrationBean;
810
import org.springframework.context.annotation.Bean;
911
import org.springframework.context.annotation.Configuration;
1012
import org.springframework.data.redis.core.RedisTemplate;
@@ -44,13 +46,11 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
4446
.formLogin(AbstractHttpConfigurer::disable)
4547
.httpBasic(AbstractHttpConfigurer::disable)
4648
.logout(AbstractHttpConfigurer::disable)
47-
.oauth2Login(outh2 -> outh2
48-
.userInfoEndpoint(userInfoEndpointConfig ->
49-
userInfoEndpointConfig.userService(customOAuth2UserService))
50-
.successHandler(customSuccessHandler)
51-
.loginPage("/login")
52-
)
53-
// JWT 사용을 위해 세션을 STATELESS로 설정
49+
.oauth2Login((outh2)-> outh2
50+
.userInfoEndpoint((userInfoEndpointConfig ->
51+
userInfoEndpointConfig.userService(customOAuth2UserService)))
52+
.successHandler(customSuccessHandler))
53+
// JWT 사용을 위해 세션을 STATELESS로 설정 (세션 정보 저장 x)
5454
.sessionManagement(session -> session
5555
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
5656
)
@@ -63,32 +63,13 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
6363
authorizeRequests
6464
.requestMatchers(new DispatcherTypeRequestMatcher(DispatcherType.ASYNC)).permitAll()
6565
.requestMatchers(
66-
"/api/auth/**",
67-
"/login",
68-
"/ezlogin",
69-
"/login/**",
70-
"/oauth2/**",
71-
"/login/oauth",
72-
"/login/oauth2/**",
73-
"/actuator/**",
74-
"/chatting",
75-
"/submit-test",
76-
"/problems/**",
77-
"/ws/**",
78-
"/swagger-ui/**",
79-
"/swagger-resources/**",
80-
"/v2/**",
81-
"/v3/**",
82-
"/webjars/**",
83-
"/searching",
84-
"/css/**",
85-
"/images/**"
86-
).permitAll()
87-
.requestMatchers("/admin/**").hasRole("ADMIN")
88-
.anyRequest().authenticated()
66+
SecurityPath.PUBLIC_PATH).permitAll()
67+
.requestMatchers("/admin/**").hasRole("ADMIN") //어드민 권한 필요 (문제 생성, 관리 등)
68+
.anyRequest().authenticated() //나머지는 일반 인증
8969
)
9070
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
9171
.addFilterBefore(exceptionFilter, JwtFilter.class)
72+
9273
.build();
9374
}
9475

src/main/java/org/ezcode/codetest/common/security/hander/CustomSuccessHandler.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo
4343
//OAuth2User
4444
CustomOAuth2User customUserDetails = (CustomOAuth2User) authentication.getPrincipal();
4545

46-
User loginUser = userDomainService.getOAuthUser(customUserDetails.getEmail(), customUserDetails.getProvider());
46+
User loginUser = userDomainService.getUserByEmail(customUserDetails.getEmail());
47+
log.info("loginUser: {}", loginUser);
4748

4849
String accessToken = jwtUtil.createToken(
4950
loginUser.getId(),

src/main/java/org/ezcode/codetest/common/security/util/JwtFilter.java

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,27 +32,17 @@ public class JwtFilter extends OncePerRequestFilter {
3232
private final JwtUtil jwtUtil;
3333
private final RedisTemplate<String, String> redisTemplate;
3434

35-
// 토큰 검증을 건너뛸 경로들
36-
private static final String[] WHITE_LIST = {
37-
"/api/auth/signin",
38-
"/api/auth/signup",
39-
"/api/auth/refresh",
40-
"/swagger-ui",
41-
"/v3/api-docs",
42-
};
43-
44-
4535
@Override
4636
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
4737
FilterChain filterChain) throws ServletException, IOException {
4838

49-
String requestURI = request.getRequestURI();
50-
51-
//whiteList 등록
52-
if (shouldSkipFilter(requestURI)) {
53-
filterChain.doFilter(request, response);
54-
return;
55-
}
39+
// String requestURI = request.getRequestURI();
40+
//
41+
// //whiteList 등록
42+
// if (shouldNotFilter(requestURI)) {
43+
// filterChain.doFilter(request, response);
44+
// return;
45+
// }
5646

5747
String bearerToken = request.getHeader("Authorization");
5848

@@ -101,9 +91,19 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
10191
filterChain.doFilter(request, response);
10292
}
10393

104-
private boolean shouldSkipFilter(String requestURI){
105-
return Arrays.stream(WHITE_LIST).anyMatch(requestURI::startsWith);
94+
// 필터링 제외할 경로 지정
95+
@Override
96+
protected boolean shouldNotFilter(HttpServletRequest request) {
97+
String requestURI = request.getRequestURI();
98+
return Arrays.stream(SecurityPath.PUBLIC_PATH)
99+
.anyMatch(path -> {
100+
if (path.endsWith("/**")) {
101+
return requestURI.startsWith(path.substring(0, path.length() - 3));
102+
}
103+
return requestURI.startsWith(path);
104+
});
106105
}
107106

107+
108108
}
109109

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.ezcode.codetest.common.security.util;
2+
3+
import org.springframework.stereotype.Component;
4+
5+
@Component
6+
public class SecurityPath {
7+
public static final String[] PUBLIC_PATH = {
8+
"/api/auth/**",
9+
"/login",
10+
"/ezlogin",
11+
"/login/**",
12+
"/oauth2/**",
13+
"/login/oauth",
14+
"/login/oauth2/**", //OAuth로그인 접근
15+
"/actuator/**",
16+
"/chatting",
17+
"/submit-test",
18+
"/problems/**",
19+
"/ws/**",
20+
"/swagger-ui/**",
21+
"/swagger-resources/**",
22+
"/v2/**",
23+
"/v3/**",
24+
"/webjars/**",
25+
"/searching",
26+
"/css/**", //html 화면 구성 접근
27+
"/images/**"
28+
};
29+
}

0 commit comments

Comments
 (0)