-
Notifications
You must be signed in to change notification settings - Fork 240
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
🚀 2단계 - 깃헙 로그인 구현 #613
base: beyondorder
Are you sure you want to change the base?
🚀 2단계 - 깃헙 로그인 구현 #613
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,11 @@ | ||
# 지하철 노선도 미션 | ||
[ATDD 강의](https://edu.nextstep.camp/c/R89PYi5H) 실습을 위한 지하철 노선도 애플리케이션 | ||
[ATDD 강의](https://edu.nextstep.camp/c/R89PYi5H) 실습을 위한 지하철 노선도 애플리케이션 | ||
|
||
|
||
### 기능 요구사항 | ||
* 깃허브를 이용한 로그인 구현(토큰 발행) | ||
* 가입이 되어있지 않은 경우 회원 가입으로 진행 후 토큰 발행 | ||
|
||
|
||
### 프로그래밍 요구사항 | ||
* GitHub 로그인을 검증할 수 있는 인수 테스트 구현(실제 GitHub에 요청을 하지 않아도 됨) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package nextstep.auth.application; | ||
|
||
import nextstep.fake.GithubAccessTokenRequest; | ||
import nextstep.fake.GithubAccessTokenResponse; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.http.HttpEntity; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.HttpMethod; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.client.RestTemplate; | ||
|
||
import java.util.List; | ||
|
||
|
||
@Component | ||
public class GithubClient { | ||
|
||
@Value("${github.url}") | ||
private String githubUrl; | ||
|
||
public String requestGithubToken(String code) { | ||
RestTemplate restTemplate = new RestTemplate(); | ||
|
||
GithubAccessTokenRequest request = new GithubAccessTokenRequest( | ||
code, | ||
"client_id", | ||
"client_secret" | ||
); | ||
|
||
HttpHeaders headers = new HttpHeaders(); | ||
headers.setAccept(List.of(MediaType.APPLICATION_JSON)); | ||
|
||
HttpEntity httpEntity = new HttpEntity(request, headers); | ||
String accessToken = restTemplate.exchange(githubUrl, HttpMethod.POST, httpEntity, GithubAccessTokenResponse.class) | ||
.getBody() | ||
.getAccessToken(); | ||
|
||
return accessToken; | ||
} | ||
|
||
|
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package nextstep.auth.application; | ||
|
||
import nextstep.auth.application.dto.GitHubLoginRequest; | ||
import nextstep.auth.application.dto.GithubLoginResponse; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
public class GithubLoginService { | ||
|
||
private final GithubClient githubClient; | ||
|
||
public GithubLoginService(GithubClient githubClient) { | ||
this.githubClient = githubClient; | ||
} | ||
|
||
public GithubLoginResponse login(GitHubLoginRequest request) { | ||
return new GithubLoginResponse(githubClient.requestGithubToken(request.getCode())); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package nextstep.auth.application.dto; | ||
|
||
public class GitHubLoginRequest { | ||
private String code; | ||
|
||
public GitHubLoginRequest() { | ||
} | ||
|
||
public GitHubLoginRequest(String code) { | ||
this.code = code; | ||
} | ||
|
||
public String getCode() { | ||
return code; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package nextstep.auth.application.dto; | ||
|
||
public class GithubLoginResponse { | ||
private final String accessToken; | ||
|
||
public GithubLoginResponse(String accessToken) { | ||
this.accessToken = accessToken; | ||
} | ||
|
||
public String getAccessToken() { | ||
return accessToken; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package nextstep.auth.ui; | ||
|
||
import nextstep.auth.application.GithubLoginService; | ||
import nextstep.auth.application.dto.GitHubLoginRequest; | ||
import nextstep.auth.application.dto.GithubLoginResponse; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@RestController | ||
public class AuthController { | ||
|
||
private final GithubLoginService githubLoginService; | ||
|
||
public AuthController(GithubLoginService githubLoginService) { | ||
this.githubLoginService = githubLoginService; | ||
} | ||
|
||
@PostMapping("/login/github") | ||
public ResponseEntity<GithubLoginResponse> login( | ||
@RequestBody GitHubLoginRequest request | ||
) { | ||
return ResponseEntity.ok(githubLoginService.login(request)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package nextstep.fake; | ||
|
||
public class GithubAccessTokenRequest { | ||
private final String code; | ||
private final String clientId; | ||
private final String clientSecret; | ||
|
||
public GithubAccessTokenRequest(String code, String clientId, String clientSecret) { | ||
this.code = code; | ||
this.clientId = clientId; | ||
this.clientSecret = clientSecret; | ||
} | ||
|
||
public String getCode() { | ||
return code; | ||
} | ||
|
||
public String getClientId() { | ||
return clientId; | ||
} | ||
|
||
public String getClientSecret() { | ||
return clientSecret; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package nextstep.fake; | ||
|
||
public class GithubAccessTokenResponse { | ||
|
||
private String accessToken; | ||
|
||
public GithubAccessTokenResponse() { | ||
} | ||
|
||
public GithubAccessTokenResponse(String accessToken) { | ||
this.accessToken = accessToken; | ||
} | ||
|
||
public String getAccessToken() { | ||
return accessToken; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package nextstep.fake; | ||
|
||
public enum GithubResponse { | ||
사용자1("code1", "accessToken1","[email protected]"), | ||
사용자2("code2", "accessToken2","[email protected]"), | ||
사용자3("code3", "accessToken3","[email protected]"); | ||
Comment on lines
+3
to
+6
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fake 객체가 main에 있네요. 😅 |
||
|
||
private final String code; | ||
private final String accessToken; | ||
private final String email; | ||
|
||
GithubResponse(String code, String accessToken, String email) { | ||
this.code = code; | ||
this.accessToken = accessToken; | ||
this.email = email; | ||
} | ||
|
||
public static GithubResponse findByCode(String code) { | ||
for (GithubResponse githubResponse : GithubResponse.values()) { | ||
if (githubResponse.code.equals(code)) { | ||
return githubResponse; | ||
} | ||
} | ||
throw new IllegalArgumentException("해당하는 코드가 없습니다."); | ||
} | ||
|
||
public String getCode() { | ||
return code; | ||
} | ||
|
||
public String getAccessToken() { | ||
return accessToken; | ||
} | ||
|
||
public String getEmail() { | ||
return email; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package nextstep.fake; | ||
|
||
import org.springframework.http.ResponseEntity; | ||
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 | ||
@RequestMapping("/test") | ||
public class TestController { | ||
|
||
@PostMapping("/github/access-token") | ||
public ResponseEntity<GithubAccessTokenResponse> test(@RequestBody GithubAccessTokenRequest request) { | ||
return ResponseEntity.ok(new GithubAccessTokenResponse(GithubResponse.findByCode(request.getCode()).getAccessToken())); | ||
} | ||
Comment on lines
+13
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 인수테스트에서 fake(test/github/access-token)을 호출하는 부분이 보이지 않아요. 제가 못찾은 걸까요? 🤔 |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ | |
import nextstep.subway.applicaion.dto.PathResponse; | ||
import nextstep.subway.domain.Station; | ||
import nextstep.subway.domain.StationRepository; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
|
@@ -34,39 +35,26 @@ public FavoriteService(FavoriteRepository favoriteRepository, MemberRepository m | |
this.pathService = pathService; | ||
} | ||
|
||
/** | ||
* TODO: LoginMember 를 추가로 받아서 FavoriteRequest 내용과 함께 Favorite 를 생성합니다. | ||
* @param request, loginMember | ||
*/ | ||
@Transactional | ||
public void createFavorite(FavoriteRequest request, LoginMember loginMember) { | ||
public Favorite createFavorite(FavoriteRequest request, LoginMember loginMember) { | ||
Member member = getMember(loginMember); | ||
Station source = this.stationRepository.findById(request.getSource()).orElseThrow(() -> new IllegalArgumentException("존재하지 않는 출발역입니다.")); | ||
Station target = this.stationRepository.findById(request.getTarget()).orElseThrow(() -> new IllegalArgumentException("존재하지 않는 도착역입니다.")); | ||
Station source = this.stationRepository.findById(request.getSource()).orElseThrow(() -> new RuntimeException("존재하지 않는 출발역입니다.")); | ||
Station target = this.stationRepository.findById(request.getTarget()).orElseThrow(() -> new RuntimeException("존재하지 않는 도착역입니다.")); | ||
Comment on lines
+41
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exception의 범위가 넓어졌네요. RuntimeException으로 바꾼 이유가 있을까요? 🤔 |
||
|
||
PathResponse path = this.pathService.findPath(source.getId(), target.getId()); | ||
if(path.hasNoPath()){ | ||
throw new IllegalArgumentException("경로가 존재하지 않습니다."); | ||
} | ||
favoriteRepository.save(new Favorite(member, source, target)); | ||
return favoriteRepository.save(new Favorite(member, source, target)); | ||
} | ||
|
||
/** | ||
* TODO: StationResponse 를 응답하는 FavoriteResponse 로 변환해야 합니다. | ||
* | ||
* @return | ||
*/ | ||
public List<FavoriteResponse> findFavorites(LoginMember loginMember) { | ||
Member member = getMember(loginMember); | ||
List<Favorite> favorites = favoriteRepository.findAllByMemberId(member.getId()); | ||
return favorites.stream().map(FavoriteResponse::of).collect(Collectors.toList()); | ||
} | ||
|
||
/** | ||
* TODO: 요구사항 설명에 맞게 수정합니다. | ||
* @param id | ||
*/ | ||
|
||
@Transactional | ||
public void deleteFavorite(Long id, LoginMember loginMember) { | ||
Member member = getMember(loginMember); | ||
Favorite favorite = favoriteRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("존재하지 않는 즐겨찾기입니다.")); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
spring.jpa.properties.hibernate.show_sql=true | ||
spring.jpa.properties.hibernate.format_sql=true | ||
|
||
security.jwt.token.secret-key= atdd-secret-key | ||
security.jwt.token.expire-length= 3600000 | ||
|
||
github.url = http://localhost:8080/test/github/access-token |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
리뷰반영 💯