OAuth2.0 을 이용하여 구글 로그인 기능을 구현하고, DB를 설계하여 제작할 것이다.
먼저 Google Cloud Platform에 가입하여 API 등록을 하고 Client Key 를 가져와 준다.
이는 Spring Boot 카테고리에 자세한 설명이 있다.
일단 기능을 먼저 따라 구현해보기 위해 이전에 학습했던 책에서 나온 코드를 많이 이용할 것이다.
필요한 dependencies 등록. 편의를 위해 롬복도 등록해준다.
application-oauth.properties를 추가하고 필요한 정보 기입.
그후 application.properties 에 h2 DB와 oauth 설정파일을 사용하기 위한 설정을 넣어준다.
User Entity를 만들 것이다. DB는 간단하게
이렇게 필요한 정보만 이용해서 만들 것이고, 나중에 기능을 확정한다면 더 만들 것이다.
package com.david.matjipfind.domain.user;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Getter
@NoArgsConstructor
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private String email;
private String last_position;
private String last_keyword;
@Builder
public User(String name,String email){
this.name = name;
this.email = email;
}
}
해당하는 코드.
레포지토리는 스프링 데이터 JPA의 도움으로 상속하나만 해주면 끝난다. 쿼리문 한줄 쓸 필요도 없다.
package com.david.matjipfind.config.auth;
import lombok.RequiredArgsConstructor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final CustomOAuth2UserService customOAuth2UserService;
@Override
protected void configure(HttpSecurity http) throws Exception{
http
.csrf().disable()
.headers().frameOptions().disable()
.and().authorizeRequests()
.antMatchers("/**").permitAll()
.anyRequest().authenticated()
.and()
.logout()
.logoutSuccessUrl("/")
.and()
.oauth2Login()
.userInfoEndpoint()
.userService(customOAuth2UserService);
}
}
SecurityConfig 파일인데, 이 부분이 가장 난해하다. 지금 이 Config 파일을 쓰는 문법을 알지를 못해서 책에서 나온 코드를 조금 바꿨다.
package com.david.matjipfind.config.auth;
import com.david.matjipfind.config.auth.dto.OAuthAttributes;
import com.david.matjipfind.config.auth.dto.SessionUser;
import com.david.matjipfind.domain.user.User;
import com.david.matjipfind.domain.user.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpSession;
import java.util.Collections;
@RequiredArgsConstructor
@Service
public class CustomOAuth2UserService implements OAuth2UserService
<OAuth2UserRequest,OAuth2User> {
private final UserRepository userRepository;
private final HttpSession httpSession;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest)
throws OAuth2AuthenticationException {
OAuth2UserService<OAuth2UserRequest,OAuth2User> delegate
= new DefaultOAuth2UserService();
OAuth2User oAuth2User = delegate.loadUser(userRequest);
// 어떤 서비스에서 로그인하는지 확인
String registrationId = userRequest.getClientRegistration().getRegistrationId();
// OAuth2 로그인 시 키가 되는 필드값
String userNameAttributeName = userRequest.getClientRegistration().
getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName();
// OAuth2UserService를 통해 가져온 OAuth2User의 Attribute를 담을 클래스
OAuthAttributes attributes = OAuthAttributes.of(registrationId,userNameAttributeName,
oAuth2User.getAttributes());
User user = getOrRegisterUser(attributes);
httpSession.setAttribute("user",new SessionUser(user));
return new DefaultOAuth2User(Collections.singleton(new
SimpleGrantedAuthority(user.getName())),
attributes.getAttributes(),
attributes.getNameAttributeKey());
}
private User getOrRegisterUser(OAuthAttributes attributes) {
User user = userRepository.findByEmail(attributes.getEmail())
.orElse(attributes.toEntity());
return userRepository.save(user);
}
}
로그인 서비스 클래스이다. Attribute를 가져와서 인스턴스를 만들고, 세션에 저장한다. 만약 findByEmail에서 해당 이메일에 해당하는 계정을 찾을 수 없다면 toEntity 메소드를 통해 계정을 생성한다.
package com.david.matjipfind.config.auth.dto;
import com.david.matjipfind.domain.user.User;
import lombok.Builder;
import lombok.Getter;
import java.util.Map;
@Getter
public class OAuthAttributes {
private Map<String,Object> attributes;
private String nameAttributeKey;
private String name;
private String email;
private String last_position;
private String last_keyword;
@Builder
public OAuthAttributes(Map<String,Object> attributes,
String nameAttributeKey,String name,
String email,String last_position,
String last_keyword){
this.attributes = attributes;
this.nameAttributeKey = nameAttributeKey;
this.name = name;
this.email = email;
this.last_position = last_position;
this.last_keyword = last_keyword;
}
public static OAuthAttributes of(String registrationId,
String userNameAttributeName,
Map<String, Object> attributes) {
return ofGoogle(userNameAttributeName, attributes);
}
private static OAuthAttributes ofGoogle(String userNameAttributeName,Map<String,Object> attributes){
return OAuthAttributes.builder()
.name((String) attributes.get("name"))
.email((String) attributes.get("email"))
.last_keyword((String) attributes.get("last_keyword"))
.last_position((String) attributes.get("last_position"))
.attributes(attributes)
.nameAttributeKey(userNameAttributeName)
.build();
}
public User toEntity(){
return User.builder()
.name(name)
.email(email)
.build();
}
}
Attribute 클래스이다.
package com.david.matjipfind.config.auth.dto;
import com.david.matjipfind.domain.user.User;
import lombok.Getter;
import java.io.Serializable;
@Getter
public class SessionUser implements Serializable {
private String name;
private String email;
private String last_position;
private String last_keyword;
public SessionUser(User user){
this.name = user.getName();
this.email = user.getEmail();
this.last_position = user.getLast_position();
this.last_keyword = user.getLast_keyword();
}
}
UserSession 클래스이다. 필요한 정보를 가져와서 프론트에서 사용할수 있게 저장하고 있는다.
이제 thymeleaf에서 사용할 수 있도록 mainController에서 세션 객체를 만들고 user 를 받아와서 이름을 가져온다.
그리고 main.html 에서 해당 코드를 추가해 주면...
이렇게 로그인 버튼이 뜬다. 이를 클릭하면
계정 선택 란이 나오고, 계정을 선택하면 성공적으로 로그인되면서 이름을 불러온다.
오늘 하면서 느낀것은..
1. html과 CSS 의 지식이 부족하여 원하는 것을 원하는 곳에 배치하기가 힘듦
원래 저 로그인 버튼을 넣고 싶은 위치가 있었는데, html과 CSS를 잘 다루지 못해서 원하는 위치에 넣지 못하고 저렇게 왼쪽 구석 위에 붙이는 형태가 되어버렸다. 그리고 지금 레이아웃도 모바일이나 다른 비율의 모니터에서 본다면 상당이 이상하게 보인다.
-> 이를 해결하기 위해 먼저 html과 CSS로 레이아웃을 잡는 법에 대해 더 철저히 배우고, 후에 React를 배워 적용해서 훨씬더 보기좋은 형태로 만들어야겠다. 일단은 핵심 기능 구현에 집중
2. OAuth2의 Config 파일을 작성하는 문법을 잘 모르겠음
SecurityConfig 파일을 작성하는 방법을 잘 모르겠어서 일단은 저번에 보았던 책에서 사용한 내용을 조금만 수정하고 그대로 사용하였다.
-> 해당 문법을 따로 공부하여 써먹어야겠다.
'Projects > matjipfind' 카테고리의 다른 글
유저 키워드 기억 기능, 기타 버그 수정 (0) | 2021.01.21 |
---|---|
Ajax를 이용한 최근키워드 실시간 DB저장 기능 (0) | 2021.01.20 |
내 위치 직접선택 기능, 최대 검색 거리, 네이버 검색 기능 (0) | 2021.01.16 |
키워드 검색기능과 리스트 얻기 기능 (1) | 2021.01.15 |
카카오맵 API로 변경, 내위치 가져오기 기능 (0) | 2021.01.14 |
댓글