-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from SWYP-4rd-6/feat/user
User 기능 추가
- Loading branch information
Showing
52 changed files
with
1,692 additions
and
101 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
79 changes: 79 additions & 0 deletions
79
src/main/java/com/swygbro/trip/backend/domain/auth/api/LoginController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package com.swygbro.trip.backend.domain.auth.api; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.swygbro.trip.backend.domain.auth.application.LoginService; | ||
import com.swygbro.trip.backend.domain.auth.dto.LoginRequest; | ||
import com.swygbro.trip.backend.domain.auth.dto.OAuth2LoginRequest; | ||
import com.swygbro.trip.backend.global.jwt.dto.TokenDto; | ||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import jakarta.validation.Valid; | ||
import lombok.RequiredArgsConstructor; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/api/v1/login") | ||
@Tag(name = "Auth", description = """ | ||
- 로그인 기능을 제공하는 API 입니다. | ||
## 주요 기능 | ||
- 로그인 | ||
- 구글 소셜 로그인(구현 예정) | ||
""") | ||
public class LoginController { | ||
|
||
private static final Logger log = LoggerFactory.getLogger(LoginController.class); | ||
private final LoginService loginService; | ||
|
||
@PostMapping | ||
@Operation(summary = "로그인", description = """ | ||
# 로그인 | ||
사용자의 이메일과 비밀번호를 입력하여 로그인합니다. | ||
## 응답 | ||
- 로그인 성공 시 `200` 코드와 함께 토큰을 반환합니다. | ||
- 토큰은 `access_token`과 `refresh_token`으로 구성되어 있습니다. | ||
- 로그인 실패 시 `400` 에러를 반환합니다. | ||
""") | ||
@ApiResponse( | ||
responseCode = "200", | ||
description = "로그인 성공 시 토큰을 반환합니다." | ||
) | ||
public TokenDto login(@Valid @RequestBody LoginRequest dto) { | ||
return loginService.login(dto); | ||
} | ||
|
||
@PostMapping(value = "/google") | ||
@Operation(summary = "구글 소셜 로그인", description = """ | ||
# 구글 소셜 로그인 | ||
구글 소셜 로그인을 통해 로그인합니다. | ||
클라이언트에서 구글 로그인 후 발급받은 code를 인자로 전달하면 소셜 로그인이 진행됩니다. | ||
s | ||
## 응답 | ||
- 로그인 성공 시 `200` 코드와 함께 토큰을 반환합니다. | ||
- 토큰은 `access_token`과 `refresh_token`으로 구성되어 있습니다. | ||
- 로그인 실패 시 `400` 에러를 반환합니다. | ||
""") | ||
public TokenDto googleLogin(@RequestBody OAuth2LoginRequest code) throws JsonProcessingException { | ||
return loginService.loginGoogle(code.getCode()); | ||
} | ||
} |
99 changes: 99 additions & 0 deletions
99
src/main/java/com/swygbro/trip/backend/domain/auth/application/LoginService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package com.swygbro.trip.backend.domain.auth.application; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.swygbro.trip.backend.domain.auth.dto.GoogleUserInfo; | ||
import com.swygbro.trip.backend.domain.auth.dto.LoginRequest; | ||
import com.swygbro.trip.backend.domain.auth.exception.LoginFailException; | ||
import com.swygbro.trip.backend.domain.user.domain.User; | ||
import com.swygbro.trip.backend.domain.user.domain.UserRepository; | ||
import com.swygbro.trip.backend.global.jwt.TokenService; | ||
import com.swygbro.trip.backend.global.jwt.dto.TokenDto; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.RequestEntity; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.security.crypto.password.PasswordEncoder; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.util.LinkedMultiValueMap; | ||
import org.springframework.util.MultiValueMap; | ||
import org.springframework.web.client.RestTemplate; | ||
import org.springframework.web.util.UriComponentsBuilder; | ||
|
||
import java.net.URI; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class LoginService { | ||
|
||
private final UserRepository userRepository; | ||
private final TokenService tokenService; | ||
private final PasswordEncoder passwordEncoder; | ||
private final RestTemplate restTemplate; | ||
|
||
|
||
@Value("${google.client.id}") | ||
private String clientId; | ||
@Value("${google.client.secret}") | ||
private String secretPassword; | ||
@Value("${google.client.redirect}") | ||
private String redirectUri; | ||
|
||
public TokenDto login(LoginRequest dto) { | ||
User user = userRepository.findByEmail(dto.getEmail()) | ||
.orElseThrow(LoginFailException::new); | ||
|
||
if (!passwordEncoder.matches(dto.getPassword(), user.getPassword())) { | ||
throw new LoginFailException(); | ||
} | ||
|
||
return tokenService.generateToken(user.getEmail()); | ||
} | ||
|
||
public TokenDto loginGoogle(String code) throws JsonProcessingException { | ||
String accessToken = getGoogleAccessToken(code); | ||
GoogleUserInfo userInfo = getGoogleUserInfo(accessToken); | ||
// TODO 회원등록 추가 | ||
return tokenService.generateToken(userInfo.getEmail()); | ||
} | ||
|
||
public String getGoogleAccessToken(String code) throws JsonProcessingException { | ||
URI uri = UriComponentsBuilder | ||
.fromUriString("https://oauth2.googleapis.com/token") | ||
.encode() | ||
.build() | ||
.toUri(); | ||
|
||
HttpHeaders headers = new HttpHeaders(); | ||
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8"); | ||
|
||
MultiValueMap<String, String> body = new LinkedMultiValueMap<>(); | ||
body.add("grant_type", "authorization_code"); | ||
body.add("client_id", clientId); | ||
body.add("client_secret", secretPassword); | ||
body.add("redirect_uri", redirectUri); | ||
body.add("code", code); | ||
|
||
RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity.post(uri).headers(headers).body(body); | ||
ResponseEntity<String> response = restTemplate.exchange(requestEntity, String.class); | ||
|
||
JsonNode jsonNode = new ObjectMapper().readTree(response.getBody()); | ||
|
||
return jsonNode.get("access_token").asText(); | ||
} | ||
|
||
private GoogleUserInfo getGoogleUserInfo(String googleAccessToken) throws JsonProcessingException { | ||
URI uri = UriComponentsBuilder | ||
.fromUriString("https://www.googleapis.com/oauth2/v2/userinfo") | ||
.queryParam("access_token", googleAccessToken) | ||
.encode() | ||
.build() | ||
.toUri(); | ||
|
||
ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class); | ||
return new ObjectMapper().readValue(responseEntity.getBody(), GoogleUserInfo.class); | ||
} | ||
|
||
} |
11 changes: 11 additions & 0 deletions
11
src/main/java/com/swygbro/trip/backend/domain/auth/dto/GoogleUserInfo.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.swygbro.trip.backend.domain.auth.dto; | ||
|
||
import lombok.Getter; | ||
|
||
@Getter | ||
public class GoogleUserInfo { | ||
private String id; | ||
private String email; | ||
private boolean verified_email; | ||
private String picture; | ||
} |
23 changes: 23 additions & 0 deletions
23
src/main/java/com/swygbro/trip/backend/domain/auth/dto/LoginRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package com.swygbro.trip.backend.domain.auth.dto; | ||
|
||
import com.swygbro.trip.backend.global.dto.RequestDto; | ||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import jakarta.validation.constraints.Email; | ||
import jakarta.validation.constraints.NotBlank; | ||
import jakarta.validation.constraints.Size; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
@AllArgsConstructor | ||
public class LoginRequest extends RequestDto { | ||
@NotBlank | ||
@Schema(description = "사용자 이메일", example = "[email protected]") | ||
private final String email; | ||
|
||
@NotBlank | ||
@Size(min = 8, max = 20) | ||
@Schema(description = "사용자 비밀번호", example = "password123!") | ||
private final String password; | ||
} |
8 changes: 8 additions & 0 deletions
8
src/main/java/com/swygbro/trip/backend/domain/auth/dto/OAuth2LoginRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.swygbro.trip.backend.domain.auth.dto; | ||
|
||
import lombok.Getter; | ||
|
||
@Getter | ||
public class OAuth2LoginRequest { | ||
private String code; | ||
} |
11 changes: 11 additions & 0 deletions
11
src/main/java/com/swygbro/trip/backend/domain/auth/exception/LoginFailException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.swygbro.trip.backend.domain.auth.exception; | ||
|
||
import com.swygbro.trip.backend.global.exception.BaseException; | ||
import org.springframework.http.HttpStatus; | ||
|
||
public class LoginFailException extends BaseException { | ||
|
||
public LoginFailException() { | ||
super(HttpStatus.BAD_REQUEST, "로그인에 실패했습니다."); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.