-
Notifications
You must be signed in to change notification settings - Fork 36
[고정완] OAuth 2.0 리뷰 요청드립니다. #8
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
Merged
Merged
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
d6a4354
feat: application.properties 를 통해 OAuth2Client 를 등록하는 Repository 를 구현
ghojeong dbbc497
feat: endpoint 에서 사용되는 dto를 정의
ghojeong d65d82c
feat: OAuth2AccessTokenResponse 를 가져오는 OAuth2AccessTokenResponseClien…
ghojeong 68604ac
feat: OAuth2User 와 OAuth2UserService 구현
ghojeong f3581d5
feat: AuthenticationToken 과 AuthenticationProvider 를 구현
ghojeong b494728
feat: ProfileUser 를 정의함
ghojeong 6e84278
feat: State 를 위해 8자리 랜덤 문자열을 생성하는 Generator 구현
ghojeong cde753e
feat: OAuth2AuthorizedClientRepository 를 구현
ghojeong c547968
feat: OAuth2AuthorizationRequestRepository 구현
ghojeong a518b2d
feat: OAuth2AuthorizationRequestResolver 구현
ghojeong f6203fb
feat: OAuth2AuthorizationRequestRedirectFilter 구현
ghojeong c31f070
refactor: SecurityContextRepository 인터페이스를 따로 선언
ghojeong a0b1a60
feat: AbstractAuthenticationProcessingFilter 구현
ghojeong 3654c25
feat: OAuth2LoginAuthenticationFilter 구현
ghojeong 34ad282
feat: AuthenticationFilter 에 AuthenticationManager 를 활용하도록 Config 를 수정
ghojeong fba333b
test: FormLoginTest 에서 로그인 성공시 OK 가 아니라 302 Redirect 가 되도록 테스트를 수정
ghojeong 556d4c5
fix: 테스트 시 공통적으로 사용하는 Member 정보를 Fixture 로 분리
ghojeong c0715a9
test: OAuth2 LoginRedirect 에 관한 테스트 작성
ghojeong b1f894f
test: GitHub 와 Google 을 Stub 을 활용해 인증하는 테스트 작성
ghojeong bae948c
test: LoginToken 을 발급받을 시, user code 가 일치하는지 확인하는 테스트 추가
ghojeong b676536
refactor: Dao 대신에, Repository 의 구현방법을 유추할 수 있도록 네이밍
ghojeong 94b3a94
fix: 멤버의 네이밍을 대문자로 시작하게 한 오타를 수정
ghojeong 3db0484
fix: attemptAuthentication 시 removeAuthorizationRequest 를 하도록 로직 수정
ghojeong 5d68d0d
refactor: OAuth2UserService 의 설정을 DefaultOAuth2UserService 를 통해 구현하는 …
ghojeong 9a3a8f2
feat: OAuth2AuthorizationCodeAuthenticationToken 의 principal 과 creden…
ghojeong File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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,43 @@ | ||
package nextstep.app; | ||
|
||
import nextstep.oauth2.authentication.provider.OAuth2LoginAuthenticationProvider; | ||
import nextstep.oauth2.registration.ClientRegistrationRepository; | ||
import nextstep.oauth2.registration.OAuth2ClientProperties; | ||
import nextstep.oauth2.userinfo.OAuth2UserService; | ||
import nextstep.oauth2.web.authorizedclient.OAuth2AuthorizedClientRepository; | ||
import nextstep.security.authentication.AuthenticationManager; | ||
import nextstep.security.authentication.DaoAuthenticationProvider; | ||
import nextstep.security.authentication.ProviderManager; | ||
import nextstep.security.userdetails.UserDetailsService; | ||
import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
|
||
import java.util.List; | ||
|
||
@EnableConfigurationProperties(OAuth2ClientProperties.class) | ||
@Configuration | ||
public class OAuth2Config { | ||
@Bean | ||
public ClientRegistrationRepository registrationRepository( | ||
OAuth2ClientProperties oauth2ClientProperties | ||
) { | ||
return oauth2ClientProperties.createClientRegistrationDao(); | ||
} | ||
|
||
@Bean | ||
public OAuth2AuthorizedClientRepository authorizedClientRepository() { | ||
return OAuth2AuthorizedClientRepository.getInstance(); | ||
} | ||
|
||
@Bean | ||
public AuthenticationManager authenticationManager( | ||
UserDetailsService userDetailsService, | ||
OAuth2UserService oAuth2UserService | ||
) { | ||
return new ProviderManager(List.of( | ||
new DaoAuthenticationProvider(userDetailsService), | ||
new OAuth2LoginAuthenticationProvider(oAuth2UserService) | ||
)); | ||
} | ||
} |
This file contains hidden or 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 hidden or 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 |
---|---|---|
|
@@ -9,4 +9,6 @@ public interface MemberRepository { | |
List<Member> findAll(); | ||
|
||
Member save(Member member); | ||
|
||
void clear(); | ||
} |
This file contains hidden or 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
58 changes: 58 additions & 0 deletions
58
src/main/java/nextstep/app/service/DefaultOAuth2UserService.java
This file contains hidden or 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,58 @@ | ||
package nextstep.app.service; | ||
|
||
import nextstep.app.domain.Member; | ||
import nextstep.app.domain.MemberRepository; | ||
import nextstep.oauth2.profile.OAuth2ProfileUser; | ||
import nextstep.oauth2.userinfo.OAuth2User; | ||
import nextstep.oauth2.userinfo.OAuth2UserRequest; | ||
import nextstep.oauth2.userinfo.OAuth2UserService; | ||
import org.springframework.http.HttpEntity; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.HttpMethod; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.web.client.RestTemplate; | ||
|
||
import java.util.Map; | ||
import java.util.Set; | ||
|
||
@Service | ||
public class DefaultOAuth2UserService implements OAuth2UserService { | ||
private static final RestTemplate rest = new RestTemplate(); | ||
|
||
private final MemberRepository memberRepository; | ||
|
||
public DefaultOAuth2UserService(MemberRepository memberRepository) { | ||
this.memberRepository = memberRepository; | ||
} | ||
|
||
@Override | ||
public OAuth2User loadUser(OAuth2UserRequest userRequest) { | ||
final String userNameAttributeName = userRequest.clientRegistration() | ||
.providerDetails().userInfoEndpoint().userNameAttributeName(); | ||
final Map<String, Object> attributes = exchangeAttributes(userRequest); | ||
final OAuth2ProfileUser profileUser = OAuth2ProfileUser.of( | ||
userRequest.clientRegistration().registrationId(), | ||
attributes | ||
); | ||
final Set<String> authorities = memberRepository.findByEmail( | ||
attributes.get(userNameAttributeName).toString() | ||
).orElseGet( | ||
() -> memberRepository.save(new Member( | ||
profileUser.email(), "", profileUser.name(), | ||
profileUser.imageUrl(), Set.of("USER") | ||
)) | ||
).getRoles(); | ||
return OAuth2User.of(authorities, attributes, userNameAttributeName); | ||
} | ||
|
||
private Map exchangeAttributes(OAuth2UserRequest userRequest) { | ||
final HttpHeaders headers = new HttpHeaders(); | ||
headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + userRequest.accessToken().token()); | ||
return rest.exchange( | ||
userRequest.clientRegistration().providerDetails().userInfoEndpoint().uri(), | ||
HttpMethod.GET, | ||
new HttpEntity<>(headers), | ||
Map.class | ||
).getBody(); | ||
} | ||
} |
This file contains hidden or 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
3 changes: 3 additions & 0 deletions
3
src/main/java/nextstep/oauth2/authentication/OAuth2AccessToken.java
This file contains hidden or 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,3 @@ | ||
package nextstep.oauth2.authentication; | ||
|
||
public record OAuth2AccessToken(String token) {} |
43 changes: 43 additions & 0 deletions
43
...extstep/oauth2/authentication/provider/OAuth2AuthorizationCodeAuthenticationProvider.java
This file contains hidden or 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,43 @@ | ||
package nextstep.oauth2.authentication.provider; | ||
|
||
import nextstep.oauth2.authentication.OAuth2AccessToken; | ||
import nextstep.oauth2.authentication.token.OAuth2AuthorizationCodeAuthenticationToken; | ||
import nextstep.oauth2.endpoint.OAuth2AccessTokenResponseClient; | ||
import nextstep.oauth2.endpoint.dto.OAuth2AuthorizationCodeGrantRequest; | ||
import nextstep.oauth2.endpoint.dto.OAuth2AuthorizationExchange; | ||
import nextstep.oauth2.exception.OAuth2AuthenticationException; | ||
import nextstep.oauth2.registration.ClientRegistration; | ||
import nextstep.security.authentication.Authentication; | ||
import nextstep.security.authentication.AuthenticationException; | ||
import nextstep.security.authentication.AuthenticationProvider; | ||
|
||
public class OAuth2AuthorizationCodeAuthenticationProvider implements AuthenticationProvider { | ||
private final OAuth2AccessTokenResponseClient client = new OAuth2AccessTokenResponseClient(); | ||
|
||
@Override | ||
public Authentication authenticate(Authentication authentication) throws AuthenticationException { | ||
final OAuth2AuthorizationCodeAuthenticationToken codeToken = (OAuth2AuthorizationCodeAuthenticationToken) authentication; | ||
if (!codeToken.isValid()) { | ||
throw new OAuth2AuthenticationException(); | ||
} | ||
final ClientRegistration registration = codeToken.getClientRegistration(); | ||
final OAuth2AuthorizationExchange exchange = codeToken.getAuthorizationExchange(); | ||
return new OAuth2AuthorizationCodeAuthenticationToken( | ||
registration, exchange, getAccessToken(registration, exchange) | ||
); | ||
} | ||
|
||
@Override | ||
public boolean supports(Class<?> authentication) { | ||
return OAuth2AuthorizationCodeAuthenticationToken.class.isAssignableFrom(authentication); | ||
} | ||
|
||
private OAuth2AccessToken getAccessToken( | ||
ClientRegistration registration, | ||
OAuth2AuthorizationExchange exchange | ||
) { | ||
return client.getTokenResponse( | ||
new OAuth2AuthorizationCodeGrantRequest(registration, exchange) | ||
).accessToken(); | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
src/main/java/nextstep/oauth2/authentication/provider/OAuth2LoginAuthenticationProvider.java
This file contains hidden or 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,51 @@ | ||
package nextstep.oauth2.authentication.provider; | ||
|
||
import nextstep.oauth2.authentication.OAuth2AccessToken; | ||
import nextstep.oauth2.authentication.token.OAuth2AuthorizationCodeAuthenticationToken; | ||
import nextstep.oauth2.authentication.token.OAuth2LoginAuthenticationToken; | ||
import nextstep.oauth2.userinfo.OAuth2User; | ||
import nextstep.oauth2.userinfo.OAuth2UserRequest; | ||
import nextstep.oauth2.userinfo.OAuth2UserService; | ||
import nextstep.security.authentication.Authentication; | ||
import nextstep.security.authentication.AuthenticationException; | ||
import nextstep.security.authentication.AuthenticationProvider; | ||
|
||
public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider { | ||
private final OAuth2AuthorizationCodeAuthenticationProvider provider = new OAuth2AuthorizationCodeAuthenticationProvider(); | ||
private final OAuth2UserService userService; | ||
|
||
public OAuth2LoginAuthenticationProvider(OAuth2UserService userService) { | ||
this.userService = userService; | ||
} | ||
|
||
@Override | ||
public Authentication authenticate(Authentication authentication) throws AuthenticationException { | ||
final OAuth2LoginAuthenticationToken loginToken = (OAuth2LoginAuthenticationToken) authentication; | ||
final OAuth2AccessToken accessToken = getAccessToken(loginToken); | ||
final OAuth2User oauth2User = loadUser(loginToken, accessToken); | ||
return new OAuth2LoginAuthenticationToken( | ||
loginToken.getClientRegistration(), loginToken.getAuthorizationExchange(), | ||
oauth2User, oauth2User.authorities(), accessToken | ||
); | ||
} | ||
|
||
@Override | ||
public boolean supports(Class<?> authentication) { | ||
return OAuth2LoginAuthenticationToken.class.isAssignableFrom(authentication); | ||
} | ||
|
||
private OAuth2AccessToken getAccessToken(OAuth2LoginAuthenticationToken loginToken) { | ||
return ((OAuth2AuthorizationCodeAuthenticationToken) provider.authenticate( | ||
new OAuth2AuthorizationCodeAuthenticationToken( | ||
loginToken.getClientRegistration(), | ||
loginToken.getAuthorizationExchange() | ||
) | ||
)).getAccessToken(); | ||
} | ||
|
||
private OAuth2User loadUser(OAuth2LoginAuthenticationToken loginToken, OAuth2AccessToken accessToken) { | ||
return userService.loadUser( | ||
new OAuth2UserRequest(loginToken.getClientRegistration(), accessToken) | ||
); | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
src/main/java/nextstep/oauth2/authentication/token/OAuth2AuthenticationToken.java
This file contains hidden or 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,36 @@ | ||
package nextstep.oauth2.authentication.token; | ||
|
||
import nextstep.oauth2.userinfo.OAuth2User; | ||
import nextstep.security.authentication.Authentication; | ||
|
||
import java.util.Set; | ||
|
||
public final class OAuth2AuthenticationToken implements Authentication { | ||
private final OAuth2User principal; | ||
private final Set<String> authorities; | ||
|
||
public OAuth2AuthenticationToken(OAuth2User principal, Set<String> authorities) { | ||
this.principal = principal; | ||
this.authorities = authorities; | ||
} | ||
|
||
@Override | ||
public Set<String> getAuthorities() { | ||
return authorities; | ||
} | ||
|
||
@Override | ||
public Object getCredentials() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public Object getPrincipal() { | ||
return principal; | ||
} | ||
|
||
@Override | ||
public boolean isAuthenticated() { | ||
return true; | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
반영 잘 해주셨습니다!
DefaultOAuth2UserService에서 제공해주는 것 외에 커스텀이 필요한 부분은 어떤 부분인지 확인해보고 구현을 해보면
추후 스프링 시큐리티를 사용할 때 조금 더 색다르게 다가올 것 같네요 :)
어떤 부분이 있을 지 고민만 해보셔요~