▤ 목차
🙇♂️ Flutter 개발자가 아니라 코드 작성부분은 다소 생략하였습니다.ㅠㅠ
Flutter로 하는 OAuth2 로그인 방법
Spring security로 OAuth2로그인을 구현 하려고 하면 보통 web으로 로그인 하는게 많이 보입니다. 하지만 kakao developer
문서를 보면 네이티브 앱에서는 리다이렉트 방식을 사용 할 수 없다고 나옵니다.
그래서 공식 문서를 참고하여 프론트(Flutter)
와 백엔드(Spring)
의 역할을 나눠 개발하기로 했습니다.
Google
과 Kakao
로그인 개발을 하기 위해 아래 두 문서를 참고했습니다.
[참고]
- 프론트 역할
access token
발급 후 api 호출access token
만료 처리
- 백엔드 역할
access token
으로 사용자 정보 조회 후 로그인 / 회원가입 처리- token 발급
위 내용을 토대로 시퀀스 다이어 그램을 그려봤습니다.
시퀀스 다이어그램 설명
[flutter]
OAuth 로그인(Kakao, Google sdk 사용)[spring]
로그인 후 받은 access_token으로 application(spring server)에 token 발급 api 요청[spring]
access_token으로 google 및 kakao 사용자 조회 api 호출[spring]
조회된 사용자로 회원 검증 및 임시 회원가입 및 토큰 발급[flutter]
access_token 만료 처리
Flutter 설정
pubspec.yaml
파일에 sdk 파일을 추가합니다.
dependencies:
...
kakao_flutter_sdk_user: ^1.2.1
google_sign_in: ^5.0.7
...
그 다음 소셜 로그인을 위한 IOS/Android
세팅을 해야 합니다.
저는 아래 블로그 글을 참고하여 세팅해줬습니다.
Flutter 로그인 코드 작성
카카오 developer 문서나 구글 SDK api 문서를 참고하여 코드를 작성해줍니다.
Future<void> googleLogin() async {
var googleLoginHelper = new GoogleLoginHelper();
googleLoginHelper.login()
.then((accessToken) {
log('accessToken: $accessToken');
if (accessToken == null) {
EasyLoading.showError('로그인/회원가입에 실패했습니다.',
duration: const Duration(seconds: 3),
maskType: EasyLoadingMaskType.black,
dismissOnTap: false);
return;
}
autoLogin(accessToken, LoginPlatform.GOOGLE)
.then((value) => afterLogin(value))
.then((value) => googleLoginHelper.logout(accessToken));
});
}
Future<void> kakaoLogin() async {
var kakaoLoginHelper = new KakaoLoginHelper();
kakaoLoginHelper.login().then((accessToken) {
log('accessToken: $accessToken');
if (accessToken == null) {
EasyLoading.showError('로그인/회원가입에 실패했습니다.',
duration: const Duration(seconds: 3),
maskType: EasyLoadingMaskType.black,
dismissOnTap: false);
return;
}
autoLogin(accessToken, LoginPlatform.KAKAO)
.then((value) => afterLogin(value))
.then((value) => kakaoLoginHelper.logout());
});
}
Future<AuthModel> autoLogin(String token, LoginPlatform platform) {
return repo.login(code: token, platform: platform);
}
// repo ...
Future<AuthModel> login({required String code, required LoginPlatform platform}) async {
try {
Response response = await apiClient.request(
'/auth/login/${platform.name}',
options: Options(method: 'POST'),
data: {'code': code},
);
return AuthModel.fromJson(response.data['data']);
} on DioException catch (ex) {
log(ex.response.toString());
String errorMessage = json.decode(ex.response.toString())["errorMessage"];
throw Exception(errorMessage);
}
}
이런 식으로 Controller
쪽 코드를 작성해주고 kakao, google 각각 helper 클래스
를 생성하여 로그인을 할 수 있도록 구현합니다.
class KakaoLoginHelper {
Future<String> getKakaoKeyHash() async {
var key = await KakaoSdk.origin;
return key;
}
Future<String?> login() async {
var key = await getKakaoKeyHash();
log('kakaologinHelper + $key');
if (await isKakaoTalkInstalled()) {
try {
OAuthToken token = await UserApi.instance.loginWithKakaoTalk();
log('카카오톡으로 로그인 성공 : ${token.toString()}');
return token.accessToken;//토큰 정보
} catch (error) {
print('카카오톡으로 로그인 실패 $error');
// 사용자가 카카오톡 설치 후 디바이스 권한 요청 화면에서 로그인을 취소한 경우,
// 의도적인 로그인 취소로 보고 카카오계정으로 로그인 시도 없이 로그인 취소로 처리 (예: 뒤로 가기)
if (error is PlatformException && error.code == 'CANCELED') {
return null;
}
// 카카오톡에 연결된 카카오계정이 없는 경우, 카카오계정으로 로그인
try {
OAuthToken token = await UserApi.instance.loginWithKakaoAccount();
log('카카오계정으로 로그인 성공 : ${token.toString()}');
return token.accessToken; //토큰 정보
} catch (error) {
print('카카오계정으로 로그인 실패 $error');
}
}
} else {
try {
OAuthToken token = await UserApi.instance.loginWithKakaoAccount();
log('카카오계정으로 로그인 성공 : ${token.toString()}');
return token.accessToken; //토큰 정보
} catch (error) {
print('카카오계정으로 로그인 실패 $error');
}
}
return null;
}
Future<void> logout() async {
try {
UserApi.instance.logout();
} catch (error) {
print('카카오 로그인 실패 $error');
}
}
}
...
class GoogleLoginHelper {
final GoogleSignIn googleSignIn = GoogleSignIn();
Future<String?> login() async {
try {
final GoogleSignInAccount? googleSignInAccount = await googleSignIn
.signIn();
final GoogleSignInAuthentication googleSignInAuthentication = await googleSignInAccount!
.authentication;
print(googleSignInAuthentication.accessToken);
return googleSignInAuthentication.accessToken;
} catch (error) {
print(error);
}
}
Future<void> logout(String? accessToken) async {
await revokeToken(accessToken!);
await googleSignIn.signOut();
print('User signed out');
}
Future<void> revokeToken(String token) async {
final response = await http.post(
Uri.parse('https://oauth2.googleapis.com/revoke'),
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'token=$token',
);
if (response.statusCode == 200) {
print('Token revoked successfully');
} else {
print('Failed to revoke token');
}
}
}
flutter는 여기까지 마무리하고 login 함수에서 호출하고 있는 API를 다음 블로그 글에서 만들어 보겠습니다.
'Backend > Spring' 카테고리의 다른 글
이커머스 서비스에서 동시성 문제 처리해보기 (0) | 2024.05.08 |
---|---|
Spring Transactional Isolation (0) | 2023.01.04 |
Spring Transactional Propagation (0) | 2022.12.28 |
Spring Bean 설정 방법 (0) | 2021.08.29 |
스프링 빈(Bean) (0) | 2021.08.20 |