Skip to content

Commit 13316f4

Browse files
authored
Merge pull request #88 from Alpha-Damyo/develop
merge to main
2 parents bab418e + eec2ed8 commit 13316f4

19 files changed

+240
-85
lines changed

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ dependencies {
6565

6666
// spring batch
6767
implementation 'org.springframework.boot:spring-boot-starter-batch'
68+
69+
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
6870
}
6971
// QueryDSL setting ------------------------------------------------------------------------------------------------
7072
def generated = layout.buildDirectory.dir("generated/querydsl").get().asFile

src/main/java/com/damyo/alpha/api/auth/controller/AuthController.java

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.damyo.alpha.api.auth.controller.dto.SignUpRequest;
88
import com.damyo.alpha.api.auth.controller.dto.TokenResponse;
99
import com.damyo.alpha.api.auth.service.AuthService;
10+
import com.damyo.alpha.api.user.domain.UserRepository;
1011
import com.damyo.alpha.global.exception.error.ErrorResponse;
1112
import io.swagger.v3.oas.annotations.Operation;
1213
import io.swagger.v3.oas.annotations.Parameter;
@@ -17,14 +18,19 @@
1718
import io.swagger.v3.oas.annotations.responses.ApiResponses;
1819
import io.swagger.v3.oas.annotations.tags.Tag;
1920
import lombok.RequiredArgsConstructor;
21+
import lombok.extern.slf4j.Slf4j;
2022
import org.springframework.http.MediaType;
2123
import org.springframework.http.ResponseEntity;
2224
import org.springframework.web.bind.annotation.*;
2325
import org.springframework.web.multipart.MultipartFile;
2426

27+
import java.util.Map;
28+
import java.util.UUID;
29+
2530
@RequestMapping("/api/auth")
2631
@RequiredArgsConstructor
2732
@RestController
33+
@Slf4j
2834
@Tag(name = "AuthController")
2935
public class AuthController {
3036

@@ -35,39 +41,48 @@ public class AuthController {
3541
@Operation(summary = "회원가입", description = "토큰을 반환한다.")
3642
@ApiResponses(value = {
3743
@ApiResponse(responseCode = "200", description = "회원가입에 성공함", content = @Content(schema = @Schema(implementation = TokenResponse.class))),
38-
@ApiResponse(responseCode = "A101", description = "이미 가입된 계정이 존재할 때(email 중복)", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
44+
@ApiResponse(responseCode = "A101", description = "이미 가입된 계정이 존재할 때(providerId 중복)", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
3945
})
4046
public ResponseEntity<TokenResponse> signUp(
4147
@Parameter(description = "프로필 사진", in = ParameterIn.DEFAULT)
4248
@RequestPart(value = "image", required = false) MultipartFile image,
4349
@Parameter(description = "회원가입 요청사항", in = ParameterIn.DEFAULT, required = true)
4450
@RequestPart SignUpRequest signUpRequest) {
4551

46-
if(image != null) {
47-
String profileUrl = s3ImageService.upload(image);
48-
authService.signUp(signUpRequest, profileUrl);
49-
}
50-
else {
51-
authService.signUp(signUpRequest, null);
52+
String profileUrl = null;
53+
if (image != null) {
54+
profileUrl = s3ImageService.upload(image);
5255
}
5356

54-
User user = authService.login(signUpRequest);
55-
String token = authService.generateToken(user);
56-
return ResponseEntity.ok().body(new TokenResponse(token));
57+
User user = authService.signUp(signUpRequest, profileUrl);
58+
String jwt = authService.generateToken(user.getId());
59+
return ResponseEntity.ok().body(new TokenResponse(jwt));
5760
}
5861

5962

60-
@PostMapping("/login")
63+
@PostMapping("/login/{provider}")
6164
@Operation(summary = "로그인", description = "토큰을 반환한다.")
6265
@ApiResponses(value = {
6366
@ApiResponse(responseCode = "200", description = "회원가입에 성공함", content = @Content(schema = @Schema(implementation = TokenResponse.class))),
64-
@ApiResponse(responseCode = "A102", description = "해당 이메일이 DB에 존재하지 않을 때.", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
67+
@ApiResponse(responseCode = "A102", description = "해당 토큰으로 받아온 providerId DB에 존재하지 않을 때.", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
6568
})
6669
public ResponseEntity<TokenResponse> login(
6770
@Parameter(description = "로그인 요청사항", in = ParameterIn.DEFAULT, required = true)
68-
@RequestBody LoginRequest loginRequest) {
69-
User user = authService.login(loginRequest);
70-
String token = authService.generateToken(user);
71-
return ResponseEntity.ok().body(new TokenResponse(token));
71+
@RequestParam String token,
72+
@PathVariable String provider) {
73+
Map<String, Object> userInfo = authService.getUserInfo(provider, token);
74+
String providerId = authService.getAttributesId(provider, userInfo);
75+
UUID id = authService.checkIsMember(providerId);
76+
String jwt = authService.generateToken(id);
77+
return ResponseEntity.ok().body(new TokenResponse(jwt));
78+
}
79+
80+
@GetMapping("/token")
81+
@Operation(summary = "소셜 로그인 생략하고 토큰 발급받기(테스트용)", description = "바뀐 로그인 방식으로 회원가입된 3개의 계정에 대해 토큰을 발급받는다." +
82+
"'106362899132468449802', '3399007981', 'tmO5sDw_IaLlon7-M7CesK43rgFDdAnogEKq-ubl_9c' 중에 하나를 골라 providerId에 넣는다")
83+
public ResponseEntity<TokenResponse> getToken(@RequestParam String providerId) {
84+
UUID id = authService.checkIsMember(providerId);
85+
String jwt = authService.generateToken(id);
86+
return ResponseEntity.ok().body(new TokenResponse(jwt));
7287
}
7388
}

src/main/java/com/damyo/alpha/api/auth/controller/dto/SignUpRequest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66

77
@Schema(description = "회원가입 시 요청으로 오는 DTO")
88
public record SignUpRequest (
9-
@Schema(description = "소셜 로그인 후 인증서버에서 받아오는 이메일", example = "[email protected]")
10-
@Email String email,
9+
@Schema(description = "소셜 로그인 후 발급 받은 토큰", example = "이거 지우고 소셜에서 받은 토큰 복붙하기")
10+
@Email String token,
11+
@Schema(description = "소셜 로그인 인증 제공자 (google, naver, kakao)", example = "kakao")
12+
String provider,
1113
@Schema(description = "사용자의 이름, 실명은 아니고 서비스에서 사용할 이름", example = "홍길동")
1214
@NotBlank String name,
1315
@Schema(description = "사용자의 성별", example = "남자")

src/main/java/com/damyo/alpha/api/auth/exception/AuthErrorCode.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,18 @@
55
import lombok.RequiredArgsConstructor;
66
import org.springframework.http.HttpStatus;
77

8-
import static org.springframework.http.HttpStatus.NOT_FOUND;
9-
import static org.springframework.http.HttpStatus.UNAUTHORIZED;
8+
import static org.springframework.http.HttpStatus.*;
109

1110
@Getter
1211
@RequiredArgsConstructor
1312
public enum AuthErrorCode implements ErrorCode {
14-
EMAIL_ALREADY_EXIST(NOT_FOUND, "A101","해당 이메일이 이미 존재합니다"),
15-
EMAIL_NOT_FOUND(NOT_FOUND, "A102", "해당 이메일이 존재하지 않습니다"),
13+
ACCOUNT_ALREADY_EXIST(NOT_FOUND, "A101","해당 계정이 이미 존재합니다"),
14+
ACCOUNT_NOT_FOUND(NOT_FOUND, "A102", "해당 계정이 존재하지 않습니다"),
1615
EXPIRED_TOKEN(UNAUTHORIZED, "A103", "이미 만료된 토큰입니다"),
17-
INVALID_TOKEN(UNAUTHORIZED, "A104", "토큰이 유효하지 않습니다")
16+
INVALID_TOKEN(UNAUTHORIZED, "A104", "토큰이 유효하지 않습니다"),
17+
INVALID_PROVIDER(BAD_REQUEST, "A105", "인증 제공자가 올바르지 않습니다."),
18+
FAIL_GET_INFO(BAD_REQUEST, "A106", "OAuth 토큰이 잘못되었거나 만료되었습니다."),
19+
UNKNOWN_ERROR(BAD_REQUEST, "A107", "예상하지 못한 에러입니다.")
1820
;
1921

2022
private final HttpStatus httpStatus;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.damyo.alpha.api.auth.exception;
2+
3+
import io.jsonwebtoken.JwtException;
4+
5+
public class TokenException extends JwtException {
6+
public TokenException(String message) {
7+
super(message);
8+
}
9+
}
Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,43 @@
11
package com.damyo.alpha.api.auth.jwt;
22

3+
import com.damyo.alpha.api.auth.exception.AuthErrorCode;
34
import jakarta.servlet.ServletException;
45
import jakarta.servlet.http.HttpServletRequest;
56
import jakarta.servlet.http.HttpServletResponse;
67
import lombok.extern.slf4j.Slf4j;
7-
import org.springframework.beans.factory.annotation.Qualifier;
8+
import net.minidev.json.JSONObject;
89
import org.springframework.security.core.AuthenticationException;
910
import org.springframework.security.web.AuthenticationEntryPoint;
1011
import org.springframework.stereotype.Component;
11-
import org.springframework.web.servlet.HandlerExceptionResolver;
1212

1313
import java.io.IOException;
1414

15+
import static com.damyo.alpha.api.auth.exception.AuthErrorCode.*;
16+
1517
@Slf4j
1618
@Component
1719
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
18-
private final HandlerExceptionResolver resolver;
19-
20-
public JwtAuthenticationEntryPoint(@Qualifier("handlerExceptionResolver") HandlerExceptionResolver resolver) {
21-
this.resolver = resolver;
22-
}
23-
2420
@Override
2521
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
26-
resolver.resolveException(request, response, null, (Exception) request.getAttribute("exception"));
22+
AuthErrorCode errorCode = (AuthErrorCode) request.getAttribute("exception");
23+
if (errorCode.equals(EXPIRED_TOKEN)) {
24+
setResponse(response, EXPIRED_TOKEN);
25+
} else if (errorCode.equals(INVALID_TOKEN)) {
26+
setResponse(response, INVALID_TOKEN);
27+
} else {
28+
log.info("unknown error message: " + errorCode.getMessage());
29+
setResponse(response, UNKNOWN_ERROR);
30+
}
31+
}
32+
33+
private void setResponse(HttpServletResponse response, AuthErrorCode errorCode) throws IOException {
34+
response.setContentType("application/json;charset=UTF-8");
35+
response.setStatus(errorCode.getHttpStatus().value());
36+
37+
JSONObject responseJson = new JSONObject();
38+
responseJson.put("code", errorCode.getExceptionCode());
39+
responseJson.put("message", errorCode.getMessage());
40+
41+
response.getWriter().print(responseJson);
2742
}
2843
}

src/main/java/com/damyo/alpha/api/auth/jwt/JwtProvider.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package com.damyo.alpha.api.auth.jwt;
22

3+
import com.damyo.alpha.api.auth.exception.AuthErrorCode;
4+
import com.damyo.alpha.api.auth.exception.TokenException;
35
import com.damyo.alpha.api.auth.service.UserDetailServiceImpl;
46
import io.jsonwebtoken.*;
57
import io.jsonwebtoken.security.Keys;
8+
import io.jsonwebtoken.security.SecurityException;
69
import jakarta.annotation.PostConstruct;
710
import jakarta.servlet.http.HttpServletRequest;
811
import lombok.RequiredArgsConstructor;
@@ -18,6 +21,7 @@
1821
import java.time.LocalDateTime;
1922
import java.time.ZoneId;
2023
import java.util.Date;
24+
import java.util.UUID;
2125

2226
@Slf4j
2327
@RequiredArgsConstructor
@@ -26,7 +30,7 @@ public class JwtProvider {
2630

2731
@Value("${jwt.secret}")
2832
private String secret;
29-
private static final int EXPIRED_DURATION = 24;
33+
private static final int EXPIRED_DURATION = 24 * 365;
3034
private static final String AUTHORIZATION_HEADER = "Authorization";
3135
private static final String GRANT_TYPE = "Bearer ";
3236
private Key key;
@@ -38,9 +42,9 @@ private void init() {
3842
key = Keys.hmacShaKeyFor(secret.getBytes());
3943
}
4044

41-
public String generate(String email) {
45+
public String generate(String id) {
4246
Claims claims = Jwts.claims();
43-
claims.put("email", email);
47+
claims.put("id", id);
4448
return generateToken(claims);
4549
}
4650

@@ -71,17 +75,17 @@ public String resolveToken(HttpServletRequest request) {
7175
return null;
7276
}
7377

74-
public String validateTokenAndGetEmail(String token) {
78+
public String validateTokenAndGetId(String token) {
7579
return Jwts.parserBuilder()
7680
.setSigningKey(key)
7781
.build()
7882
.parseClaimsJws(token)
7983
.getBody()
80-
.get("email", String.class);
84+
.get("id", String.class);
8185
}
8286

83-
public Authentication createAuthentication(String email) {
84-
UserDetails userDetails = userDetailService.loadUserByUsername(email);
87+
public Authentication createAuthentication(UUID id) {
88+
UserDetails userDetails = userDetailService.loadUserByUsername(id.toString());
8589
return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
8690
}
8791
}
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,54 @@
1-
package com.damyo.alpha.api.auth.jwt;
1+
package com.damyo.alpha.api.auth.jwt.filter;
22

33
import com.damyo.alpha.api.auth.exception.AuthException;
4+
import com.damyo.alpha.api.auth.exception.TokenException;
5+
import com.damyo.alpha.api.auth.jwt.JwtProvider;
46
import io.jsonwebtoken.ExpiredJwtException;
57
import io.jsonwebtoken.MalformedJwtException;
68
import io.jsonwebtoken.security.SecurityException;
7-
import io.jsonwebtoken.security.SignatureException;
89
import io.jsonwebtoken.UnsupportedJwtException;
910
import jakarta.servlet.FilterChain;
1011
import jakarta.servlet.ServletException;
1112
import jakarta.servlet.http.HttpServletRequest;
1213
import jakarta.servlet.http.HttpServletResponse;
1314
import lombok.RequiredArgsConstructor;
1415
import lombok.extern.slf4j.Slf4j;
15-
import org.springframework.beans.factory.annotation.Value;
1616
import org.springframework.security.core.Authentication;
1717
import org.springframework.security.core.context.SecurityContextHolder;
18-
import org.springframework.security.core.userdetails.UsernameNotFoundException;
18+
import org.springframework.security.web.access.ExceptionTranslationFilter;
1919
import org.springframework.stereotype.Component;
20+
import org.springframework.util.StringUtils;
2021
import org.springframework.web.filter.OncePerRequestFilter;
22+
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
2123

2224
import java.io.IOException;
25+
import java.util.UUID;
2326

2427
import static com.damyo.alpha.api.auth.exception.AuthErrorCode.*;
25-
import static com.damyo.alpha.global.exception.error.CommonErrorCode.INTERNAL_SERVER_ERROR;
28+
2629

2730
@RequiredArgsConstructor
28-
@Component
2931
@Slf4j
32+
@Component
3033
public class JwtAuthenticationFilter extends OncePerRequestFilter {
3134

3235
private final JwtProvider jwtProvider;
3336
@Override
3437
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
3538
String token = jwtProvider.resolveToken(request);
39+
log.info(request.getRequestURI());
3640
try {
37-
String email = jwtProvider.validateTokenAndGetEmail(token);
38-
Authentication authentication = jwtProvider.createAuthentication(email);
41+
String id = jwtProvider.validateTokenAndGetId(token);
42+
Authentication authentication = jwtProvider.createAuthentication(UUID.fromString(id));
3943
SecurityContextHolder.getContext().setAuthentication(authentication);
4044
} catch (ExpiredJwtException e) {
41-
request.setAttribute("exception", new AuthException(EXPIRED_TOKEN));
45+
request.setAttribute("exception", EXPIRED_TOKEN);
4246
} catch (SecurityException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException e) {
43-
request.setAttribute("exception", new AuthException(INVALID_TOKEN));
47+
log.info(e.getMessage());
48+
request.setAttribute("exception", INVALID_TOKEN);
49+
} catch (Exception e) {
50+
request.setAttribute("exception", UNKNOWN_ERROR);
4451
}
45-
4652
filterChain.doFilter(request, response);
4753
}
4854
}

0 commit comments

Comments
 (0)