[Spring] 스프링 Oauth2 구글 로그인과 jpa 사용하여 유저 정보 데이터베이스에 저장 및 권한 설정 (OAuth2 스프링 2편)

2023. 3. 24. 12:00·공부/Spring
728x90

https://growth-coder.tistory.com/135

 

[Spring] 스프링 Oauth2 구글 로그인과 jpa 사용하여 유저 정보 데이터베이스에 저장 (OAuth2 스프링 1편

OAuth2 구글 로그인을 해 볼 예정인데 mysql 데이터베이스와 jpa를 사용하여 데이터베이스에 유저 정보를 저장해보려 한다. 먼저 OAuth2에 대해 간단하게 알아보자면 로그인, 회원가입 구현 과정의 번

growth-coder.tistory.com

이전 포스팅에 이어서 구글로부터 받은 정보를 데이터베이스에 저장하고 권한 설정을 해보려한다.

 

이전 포스팅에서 jpa 및 데이터베이스 세팅을 해뒀기 때문에 이번 포스팅에서는 코드만 작성하면 된다.

 

구글로부터 회원 정보를 받으면 DefaultOAuth2UserService를 확장한 클래스의 loadUser 메소드가 실행된다.

 

즉 회원 정보를 데이터베이스에 저장하는 과정은 이 메소드에서 일어나게 된다.

 

새로운 Member를 생성하여 데이터베이스에 저장해야하기 때문에 생성자를 만들어준다.

 

생성자는 Builder 패턴을 사용한다.

@Entity
@NoArgsConstructor
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id; //기본키
    private String name; //유저 이름
    private String password; //유저 비밀번호
    private String email; //유저 구글 이메일
    private String role; //유저 권한 (일반 유저, 관리지ㅏ)
    private String provider; //공급자 (google, facebook ...)
    private String providerId; //공급 아이디
    @Builder
    public Member(String name, String password, String email, String role, String provider, String providerId) {
        this.name = name;
        this.password = password;
        this.email = email;
        this.role = role;
        this.provider = provider;
        this.providerId = providerId;
    }
}

 

OAuth2User를 사용해도 되지만 굳이 새로운 클래스를 만드는 이유는 OAuth2User에는 멤버 엔티티가 존재하지 않기 때문이다.

 

그래서 내부에 멤버 엔티티를 가지는 PrincipalDetails 클래스를 만드는 것이다.

 

@Getter
public class PrincipalDetails implements OAuth2User {
    private Member member;
    private Map<String, Object> attributes;

    public PrincipalDetails(Member member) {
        this.member=member;
    }

    public PrincipalDetails(Member member, Map<String, Object> attributes) {
        this.member=member;
        this.attributes=attributes;
    }


    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> collect = new ArrayList<>();
        collect.add(new GrantedAuthority() {
            @Override
            public String getAuthority() {
                return member.getRole();
            }
        });
        return collect;
    }

    @Override
    public String getName() {
        return "name";
    }
}

OAuth2로 로그인하는 것이 아니라 직접 로그인을 구현할 때는 UserDetails를 구현해야 한다.

만약 OAuth2도 사용하고 직접 로그인도 사용한다면 UserDetails와 OAuth2User를 동시에 구현하면 된다.

 

ex) public class PrincipalDetails extends UserDetails, OAuth2User {...}

 

 

BcryptPasswordEncoder를 빈으로 등록한다.

 

@Configuration 어노테이션이 붙은 곳에서 등록해도 되지만 간단하게 Application에서 등록을 했다.

@SpringBootApplication
public class Oauth2GoogleApplication {

   public static void main(String[] args) {
      SpringApplication.run(Oauth2GoogleApplication.class, args);
   }
   @Bean
   public BCryptPasswordEncoder bCryptPasswordEncoder() {
      return new BCryptPasswordEncoder();
   }

}

빈으로 등록한 encoder를 loadUser 메소드에서 사용한다.

@Service
@RequiredArgsConstructor
public class OAuth2MemberService extends DefaultOAuth2UserService {
    private final BCryptPasswordEncoder encoder;
    private final MemberRepository memberRepository;

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2User oAuth2User = super.loadUser(userRequest);
        String provider = userRequest.getClientRegistration().getClientId();
        String providerId = oAuth2User.getAttribute("sub");
        String username = provider + "_" + providerId; //중복이 발생하지 않도록 provider와 providerId를 조합
        String email = oAuth2User.getAttribute("email");
        String role = "ROLE_USER"; //일반 유저
        Optional<Member> findMember = memberRepository.findByName(username);
        if (findMember.isEmpty()) { //찾지 못했다면
            Member member = Member.builder()
                    .name(username)
                    .email(email)
                    .password(encoder.encode("password"))
                    .role(role)
                    .provider(provider)
                    .providerId(providerId).build();
            memberRepository.save(member);
        }
        return oAuth2User;
    }
}

username을 provider와 providerId를 조합해서 만들면 이 username이 존재하는지 확인해야한다.

 

username을 검색해서 존재하지 않는다면 데이터베이스에 저장하고 존재한다면 저장하지 않는다.

 

그리고 이전 포스팅에서 관리자만 접근 가능한 페이지를 만들었는데 관리자만 접근을 할 수 있도록 설정을 하지 않아서 SecurityConfig에 해당 설정을 해준다.

 

@Configuration
@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfig {
    private final OAuth2MemberService oAuth2MemberService;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception{
        return httpSecurity
                .httpBasic().disable()
                .csrf().disable()
                .cors().and()
                .authorizeRequests()
                .requestMatchers("/private/**").authenticated() //private로 시작하는 uri는 로그인 필수
                .requestMatchers("/admin/**").access("hasRole('ROLE_ADMIN')") //admin으로 시작하는 uri는 관릴자 계정만 접근 가능
                .anyRequest().permitAll() //나머지 uri는 모든 접근 허용
                .and().oauth2Login()
                .loginPage("/loginForm") //로그인이 필요한데 로그인을 하지 않았다면 이동할 uri 설정
                .defaultSuccessUrl("/") //OAuth 구글 로그인이 성공하면 이동할 uri 설정
                .userInfoEndpoint()//로그인 완료 후 회원 정보 받기
                .userService(oAuth2MemberService).and().and().build(); //
    }
}

보다시피 admin 하위 경로는 role이 "ROLE_ADMIN"인 유저만 접근할 수 있다.

 

우선은 데이터베이스에 유저 정보를 저장할 때 무조건 "ROLE_USER"를 저장하도록 구현했는데 이 유저로 로그인하면 private는 접근 가능해야하고 admin은 접근할 수 없어야한다.

 

이후 데이터베이스에 저장된 유저 정보를 삭제하고 loadUser 메소드에서 "ROLE_USER"가 아닌 "ROLE_ADMIN"으로 바꿔서 구글 로그인을 통해 데이터베이스에 유저 정보를 저장하면 admin 경로에 접근이 가능하다.

 

참고

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0/dashboard

 

[무료] 스프링부트 시큐리티 & JWT 강의 - 인프런 | 강의

스프링부트 시큐리티에 대한 개념이 잡힙니다., - 강의 소개 | 인프런

www.inflearn.com

 

728x90

'공부 > Spring' 카테고리의 다른 글

[Spring] 스프링 OAuth2 네이버 로그인 (OAuth2 스프링 4편)  (0) 2023.04.01
[Spring] 스프링 OAuth2 페이스북 로그인 (OAuth2 스프링 3편)  (6) 2023.03.30
[Spring] 스프링 Oauth2 구글 로그인과 jpa 사용하여 유저 정보 데이터베이스에 저장 (OAuth2 스프링 1편)  (0) 2023.03.23
[Spring][인프런 스프링 MVC] 파일 업로드 및 저장 방법  (0) 2023.03.20
[Spring][인프런 스프링 MVC] 필터와 인터셉터의 차이  (0) 2023.03.12
'공부/Spring' 카테고리의 다른 글
  • [Spring] 스프링 OAuth2 네이버 로그인 (OAuth2 스프링 4편)
  • [Spring] 스프링 OAuth2 페이스북 로그인 (OAuth2 스프링 3편)
  • [Spring] 스프링 Oauth2 구글 로그인과 jpa 사용하여 유저 정보 데이터베이스에 저장 (OAuth2 스프링 1편)
  • [Spring][인프런 스프링 MVC] 파일 업로드 및 저장 방법
웅대
웅대
알고리즘과 백엔드를 중심으로 열심히 공부 중입니다! 같이 소통하며 공부해요!
    250x250
  • 웅대
    웅대 개발 블로그
    웅대
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 백준 알고리즘
        • dp
        • 문자열
        • 정렬
        • 스택
        • 브루트 포스
        • 이진 탐색
        • 정리
        • 우선순위 큐
        • 자료구조
        • 그래프
        • 기타
        • 그리디
      • 컴퓨터 언어
        • Kotlin
        • Python
        • C#
      • 공부
        • Database
        • Android Studio
        • Algorithm
        • 컴퓨터 구조론
        • Spring
        • lombok
        • AWS
        • Network
        • OS
        • Git & GitHub
        • AI
        • Computer Vision
        • 보안
        • Nginx
        • 프론트
        • express
        • GCP
        • grokking concurrency
        • DevOps
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    codetree
    스프링 OAuth2
    RNN
    AWS Lambda
    binary search
    bfs
    influxDB CLI
    code tree
    ChatPromptTemplate
    푸쉬 알람
    nn.RNN
    파이썬
    다익스트라
    parametric search
    Merge
    Vector Store
    embedding
    openvidu 배포
    ci/cd
    스택
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
웅대
[Spring] 스프링 Oauth2 구글 로그인과 jpa 사용하여 유저 정보 데이터베이스에 저장 및 권한 설정 (OAuth2 스프링 2편)
상단으로

티스토리툴바