https://localhost/login/oauth2/code/instagram
https://localhost/instaMember/cancelCallbackUrlByInstagram
https://localhost/instaMember/needToDeleteByInstagram
add or remove instagram testers
선택security:
oauth2:
client:
registration:
instagram:
provider: instagram
clientId: 'CLIENT ID'
client-secret: 'secret'
scope: user_profile,user_media
client-name: Instagram
authorization-grant-type: authorization_code
redirect-uri: '리다이렉트 url'
client-authentication-method: client_secret_post
provider:
instagram:
authorization-uri: https://api.instagram.com/oauth/authorize
token-uri: https://api.instagram.com/oauth/access_token
user-info-uri: https://graph.instagram.com/me?fields=id,username&access_token={access-token}
user-name-attribute: username
OAuth2AccessTokenResponseClient
객체를 수정하기 위해 복사해 새로운 객체를 생성 후 필요한 로직만 추가해줬다.package com.ll.gramgram.base.security;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequestEntityConverter;
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import static org.springframework.security.oauth2.core.OAuth2AccessToken.TokenType.BEARER;
@Component
public class CustomOAuth2AccessTokenResponseClient implements OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {
...
@Override
public OAuth2AccessTokenResponse getTokenResponse(
OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) {
Assert.notNull(authorizationCodeGrantRequest, "authorizationCodeGrantRequest cannot be null");
// 인스타 토큰값을 구하기 위한 if 문 추가
if (authorizationCodeGrantRequest.getClientRegistration().getClientName().equals("Instagram"))
return getInstagramTokenResponse(authorizationCodeGrantRequest);
RequestEntity<?> request = this.requestEntityConverter.convert(authorizationCodeGrantRequest);
ResponseEntity<OAuth2AccessTokenResponse> response = getResponse(request);
return response.getBody();
}
...
// 토큰 받는 로직 추가
public OAuth2AccessTokenResponse getInstagramTokenResponse(OAuth2AuthorizationCodeGrantRequest authorizationGrantRequest) {
RestTemplate restTemplate = new RestTemplate();
RequestEntity<?> request = this.requestEntityConverter.convert(authorizationGrantRequest);
ResponseEntity<InstagramAccessToken> response = restTemplate.exchange(request, InstagramAccessToken.class);
InstagramAccessToken body = response.getBody();
Map<String, Object> additionalParameters = new HashMap<>();
additionalParameters.put("access_token", body.getAccessToken());
additionalParameters.put("user_id", body.getUserId());
return OAuth2AccessTokenResponse
.withToken(body.getAccessToken())
.tokenType(BEARER)
.additionalParameters(additionalParameters)
.build();
}
}
tokenEndpoint
을 추가한다.@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class SecurityConfig {
// 새로 만든 객체를 DI 한다.
private final CustomOAuth2AccessTokenResponseClient oAuth2AccessTokenResponseClient;
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.formLogin(
formLogin -> formLogin
.loginPage("/usr/member/login")
)
.oauth2Login(
oauth2Login -> oauth2Login
.loginPage("/usr/member/login")
// 추가된 로직 - 주입받은 객체를 변수로 준다.
.tokenEndpoint(t -> t
.accessTokenResponseClient(oAuth2AccessTokenResponseClient)
)
)
.logout(
logout -> logout
.logoutUrl("/usr/member/logout")
);
return http.build();
}
🔗 기존 CustomOAuth2UserService 코드
INSTAGRAM
일 때 발생하는 로직을 추가해준다.@Override
@Transactional
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
String providerTypeCode = userRequest.getClientRegistration().getRegistrationId().toUpperCase();
// if 문 추가
if (providerTypeCode.equals("INSTAGRAM")) {
if (rq.isLogout()) {
throw new OAuth2AuthenticationException("로그인 후 이용해주세요.");
}
String userInfoUri = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri();
userInfoUri = userInfoUri.replace("{access-token}", userRequest.getAccessToken().getTokenValue());
RestTemplate restTemplate = new RestTemplate();
HttpEntity<String> entity = new HttpEntity<>(new HttpHeaders());
ResponseEntity<Map> response = restTemplate.exchange(userInfoUri, HttpMethod.GET, entity, Map.class);
Map<String, String> userAttributes = response.getBody();
String gender = rq.getSessionAttr("connectByApi__gender", "W");
rq.removeSessionAttr("connectByApi__gender");
instaMemberService.connect(rq.getMember(), gender, userAttributes.get("id"), userAttributes.get("username"), userRequest.getAccessToken().getTokenValue());
Member member = rq.getMember();
return new CustomOAuth2User(member.getUsername(), member.getPassword(), member.getGrantedAuthorities());
}
OAuth2User oAuth2User = super.loadUser(userRequest);
String oauthId = switch (providerTypeCode) {
case "NAVER" -> ((Map<String, String>) oAuth2User.getAttributes().get("response")).get("id");
default -> oAuth2User.getName();
};
String username = providerTypeCode + "__%s".formatted(oauthId);
Member member = memberService.whenSocialLogin(providerTypeCode, username).getData();
return new CustomOAuth2User(member.getUsername(), member.getPassword(), member.getGrantedAuthorities());
}