SpringBoot Security의 구글 로그인 구현만을 위한 velog입니다.
인증(Authentication) : 서비스를 사용하려는 유저의 신원을 확인하는 것
인가(Authorization) : Authorize(권한 부여). 말 그대로 권한을 확인하는 것
학교 웹사이트에서 공지를 보고 싶은 🙋🏻♂️ -> 웹사이트 들어가기 위해 학교 학생이메일로 로그인 하는 것이 인증(Authentication)
, 인증을 한 후 학교에서 해당 학생의 수업 정보 등 조회할 수 있는 권한을 확인하는 것이 인가(Authorization)
입니다.
Spring 어플리케이션의 보안(인증과 인가)를 담당하는 스프링에서 제공하는 Framework입니다.
google cloud console에 들어가서 프로젝트 생성해주고 OAuth Client ID를 만들어주세요!
어플리케이션 유형에 여러 종류가 있는데 JAVA로 구글 Oauth 로그인 구현한다 ?
->무조건 웹 어플리케이션 선택
kotilin으로 안드로이드 앱 만든다, swift로 iOS 앱 만들거니까 android 혹은 iOS 골라야지? 하면 큰일납니다! 여러분이 kotilin 혹은 swift를 이용해 자체적으로 google sign in 을 구현한다면 안드로이드 / iOS를 골라야 하지만, 그게 아니라 로그인 view로만 사용하고 스프링부트에서 로그인 구현하는거면 무조건 웹 어플리케이션 선택하셔야 합니다.
저의 다른 velog에도 정리되어 있는데 자세하게 보고 싶다? -> https://velog.io/@juice/SpringBoot-Oauth2-Google-LoginOauth2를-이용한-구글-로그인
승인된 리디렉션 URI는 구글 로그인 폼 주소가 아니라 유저가 로그인을 성공적으로 마치면 구글 서버에서 저희의 서버로 유저가 인증 되었다는 코드를 돌려주고, 저희는 이 코드를 가지고 유저의 정보에 접근,획득할 수 있는 access token을 얻는겁니다.
따라서 승인된 리디렉션 URI는? 코드를 얻을 수 있는 주소!!
spring.security.oauth2.client.registration.google.client-id=클라이언트 ID
spring.security.oauth2.client.registration.google.client-secret=클라이언트 보안 비밀
spring.security.oauth2.client.registration.google.scope=profile,email
여기까지 하면 코드 이외의 기본 설정들을 얼추 다 끝났습니다!
resources/templates/
에 loginForm.html
이라는 파일 생성
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>LoginForm</title>
</head>
<body>
<form action="/login" method="post">
<input type="text" name="username" placeholder="Username"> <br />
<input type="password" name="password" placeholder="Password"> <br />
<button type="submit">Login</button>
</form>
<a href="/oauth2/authorization/google">구글 로그인</a>
<a href="/joinForm">회원가입 아직 안하셨나요?</a>
</body>
</html>
main/java
안에 config
라는 폴더 만들고 그안에 WebMvcConfig
라는 자바 파일 만들어 줍니다. 이 파일은 mustache로 view 렌더링 하기위해 만들어주는 것입니다.
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry){
MustacheViewResolver resolver = new MustacheViewResolver();
resolver.setCharset("UTF-8");
resolver.setContentType("text/html;charset=UTF-8");
resolver.setPrefix("classpath:/templates/");
resolver.setSuffix(".html");
registry.viewResolver(resolver);
}
}
mustache가 .mustache로 되어 있는데, 이걸 .html로 view를 바꿔준다고 생각하시면 됩니다
우리 어플리케이션에서 하는 구글 로그인 관련 보안을 모두 관리하는 파일입니다.
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true)
@RequiredArgsConstructor
public class SecurityConfig {
private final PrincipalOauth2UserService principalOauth2UserService;
private final CorsConfig corsConfig;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
http.csrf(AbstractHttpConfigurer::disable);
http.addFilter(corsConfig.corsFilter());
http.authorizeHttpRequests(au -> au.anyRequest().permitAll());
http.oauth2Login(
oauth -> oauth.loginPage("/loginForm")
.defaultSuccessUrl("/home")
.userInfoEndpoint()
.userService(principalOauth2UserService)
);
return http.build();
}
}
사용자가 구글 로그인했을 떄 우리가 추가적으로 accesscode 요청하고 프로필 요청할 필요없이 한번에 유저의 프로필 가져다주는 굉장히 편리한 oauth2client 라이브러리의 userInfoEndpoint와 userService를 사용할건데, 아직 이와 관련해서 만든것이 아무것도 없기때문에 null 넣어놓고 넘어갑니다.
oauth
라는 폴더 만들고 그 안에 service 그리고 그안에 PrincipalOauth2UserService.java
라는 파일 만들어줍니다.
@Service
@Slf4j
public class PrincipalOauth2UserService extends DefaultOAuth2UserService {
// loadUser는 구글에서 유저 프로필 받아옴
@Override
public OAuth2User loadUser(OAuth2UserRequest oAuth2UserRequest) throws OAuth2AuthenticationException{
log.info("google 에서 받아온 userRequest : " + oAuth2UserRequest );
OAuth2User oAuth2User = super.loadUser(oAuth2UserRequest);
log.info("oauth에서 받아온 정보 : "+ oAuth2User.getAttributes());
return super.loadUser(oAuth2UserRequest);
}
}
url로 요청이 올테니 요청을 처리할 controller도 만들어 줘야겠죠? oauth
폴더 안에 LoginController.java
라는 파일을 만들어 주고 아까 우리가 만든 loginForm을 보여줍시다
@Controller
public class LoginController {
@GetMapping("/loginForm")
public String login(){
return "loginForm";
}
}
하고 서버 작동시키고 localhost:8080/oauth2/authorization/google
로 들어간다면??
구글 로그인 하는 폼 잘 나옵니다!! 로그인 한다면??
뭐야? 🥶
이게 앱이 만약 test단계여서 허용된 user들만 로그인 할 수 있다는 뜻이에요. 그래서 oauth 동의화면 탭에 가서 테스트 사용자에 가서 자기가 테스트할 때 사용할 이메일을 추가해주고 다시 한번 해봅시다.
🔨 추가추가
그러면 저희가 로그인 성공했을 떄 이동하게 만든 url인 /home에 이동하는 것 확인할 수 있고, 콘솔에도 잘 찍힌걸 볼 수 있습니다. 참고로 /home은 만든게 없으니 404 에러 나오는게 당연합니다?
⛔️ 404 Error? -> page를 찾을 수 없다는 에러. 서버와 관련된 것 x
이제 남은거는 받아온 userRequest에서 원하는 정보 추출해서 여러분들의 DB에 넣으시면 끝입니다! 이건 각 서비스 목적마다 저장하는게 달라서 따로 구현하지는 않겠습니다.
제가 올린 SecurityConfig에 CorsConfig라고 또다른 Bean으로 등록된 객체가 있었는데 이게 뭐냐면 프런트엔드와 협업할 때 CORs정책이라고 저희는 java로 만들었는데, react js로 만든 프런트엔드에게 request 보내는 권한을
주는것입니다.
위치는 SecurityConfig와 똑같은 폴더 안에 CorsConfig.java
라는 파일 만들고 추가하시면 끝입니다.
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter(){
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true); //frontEnd에서 axios로 처리 가능하게 만들겠다
config.addAllowedOrigin("*"); //모든 ip에 응답을 허용하겠다
config.addAllowedHeader("*"); //모든 header에 응답을 허용하겠다
config.addAllowedMethod("*"); //모든 post,get,put,delete,patch 요청을 허용하겠다
source.registerCorsConfiguration("*", config); //api로 들어오는 모든 요청은 이 config를 따르겠다
return new CorsFilter(source);
}
}