네이버 클라우드 부트캠프/복습 정리

스프링부트 [ 구글 소셜로그인 ]

유정♡ 2024. 6. 26. 11:25

1. spring.start.io에서 project 생성

 

2) setting -> Build -> Gradle -> JDK 17로 설정 / Project Structure에서 Project와 Modules 셋팅 설정 

 

3) application.properties -> applicationproperties.yml로 변경 

server:

  port: 8080



# JDBC

spring:

  datasource:

    driver-class-name: com.mysql.cj.jdbc.Driver

    url: jdbc:mysql://localhost:3306/yujung

    username: root

    password: 00000000

  # JPA

  jpa:

    database-platform: org.hibernate.dialect.MySQL8Dialect

    properties:

      hibernate:

        format_sql: true

    hibernate:

      ddl-auto: create

      naming:

        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

    show-sql : true

  #OAuth2 ??   - google ? ???? ????( ???? ?? ?? id, pwd)
  security:
    oauth2:
      client:
        registration:
          google:
            client-id:
            client-secret:
            scope: email,profile

 

4) 구글 클라우드 -> ouath2 프로젝트 생성 -> API 및 서비스 -> OAuth 동의 화면 -> 사용자 외부 선택 후 만들기 -> 저장후 계속 

 

5) 사용자 인증 정보 -> 사용자 인증 정보 만들기 -> OAuth 만들기 클릭 -> 애플리케이션 유형 -> 웹 애플리케이션 

 

6) 승인된 리디렉션 URI에 링크 추가 -> http://localhost:8080/login/oauth2/code/google -> 만들기

     -> 클라이언트 ID와 클라이언트 보안 비밀번호를 project로 돌아가서 application.yml 파일에 추가 (client-id: client-secret: )

    (contextPath설정할거면 8080/login사이에 설정, google까진 정해진 링크, port번호는 본인것 사용)

 

 

7) OAuth 동의 화면 -> PUBLISH APP -> 확인 

 

 


1) SecurityConfig -> Oauth2Controller 생성 후 코드 작성

package com.example.ouath2.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(authz->authz.requestMatchers("/","home").permitAll()
                .anyRequest().authenticated()
        )

                .oauth2Login(oauth2->oauth2
                .loginPage("/")
                .defaultSuccessUrl("/home")
                .permitAll()
                )
                .logout(logout->logout
                    .logoutUrl("/logout")
                    .logoutSuccessUrl("/home")
                    .invalidateHttpSession(true)
                    .deleteCookies("JSESSIONID")
                );
        return http.build();
    }
}
package com.example.ouath2.controller;

import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class Oauth2Controller {

    @GetMapping("/")
    public String index() {
        return "index";
    }
}

 

2) resources -> templates -> index.html 생성 후 코드 작성 

<!DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Bootstrap Example</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css">
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.slim.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>

<div class="container mt-3">
    <h2>Spring</h2>
    <div class="card">
        <div class="card-header">
            <form class="form-inline" th:action="@{/login}" method="post">
                <label for="username">아이디:</label>
                <input type="text" class="form-control" placeholder="Enter username" id="username" name="username">
                <label for="password">Password:</label>
                <input type="password" class="form-control" placeholder="Enter password" id="password" name="password">
                <button type="submit" class="btn btn-primary btn-sm">로그인</button>
                <a class="btn btn-sm btn-primary" href="/oauth2/authorization/google">구글 로그인</a>
            </form>

        </div>
        <div class="card-body">Content</div>
        <div class="card-footer">Footer</div>
    </div>
</div>

</body>
</html>

 

3) (구글 로그아웃 후) 서버 실행 -> 구글로그인 버튼 클릭 -> 로그인을 하게 되면 oauth2 서비스로 로그인 화면 뜸 -> 계속 누르면 오류         -> SecurityConfig에서 .oauth2Login에 성공시 /home으로 이동 해놨는데 아직 /home을 만들지 않아서

 

4) 구글 로그인 된 상태로 서버 끄고 브라우저 끄고(세션 끊어야해서) 다시 http://localhost:8080/manager로 들어가면

     .anyRequest().authenticated()로 인해 초기 로그인 화면 뜸 -> 그리고 구글 로그인 된 상태로 구글 로그인 버튼을 누르면

     이전에 입력했던 경로까지 뜨면서  리다이렉트 됨  

 

5) 구글 로그인한 상태라면 -> 로그인한 사용자의 정보를 가져올 수 있음 

     -> PrincipalOauth2Service 생성 후 extends DefaultOauth2UserService

     -> 로그아웃 상태에서 구글 로그인할 때(계속버튼누르면) 콘솔창에 사진처럼 출력됨(=user객체의 정보)

package com.example.ouath2.service;

import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;

@Service
public class PrincipalOauth2Service extends DefaultOAuth2UserService {

	//구글 로그인한 사용자 정보 가져오기 
    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        System.out.println(userRequest.getClientRegistration());
        System.out.println(userRequest.getAccessToken().getTokenValue());
        System.out.println(super.loadUser(userRequest).getAttributes());
        System.out.println(userRequest);
        
        return super.loadUser(userRequest);
    }
}

 

6) 강제로 회원가입 해보기(디비에 값을 넣어서) / Member와 Role Entity 생성 -> 7번

package com.example.ouath2.entity;

import jakarta.persistence.*;
import lombok.Data;
import org.hibernate.annotations.CreationTimestamp;

import java.sql.Timestamp;
import java.util.Set;

@Entity
@Data
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(length = 50, unique = true, nullable = false)
    private String username; //google_561238...
    private String password; //암호화
    private String uname;    //구글로그인했을 떄의 이름
    private String email;
    private String provider;
    private String providerId; //sub=561238...

    @CreationTimestamp
    private Timestamp createdDate;

    @ManyToMany
    @JoinTable(name = "member_roles", joinColumns = @JoinColumn(name="member_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
    private Set<Role> roles;

}
package com.example.ouath2.entity;

import jakarta.persistence.*;
import lombok.Data;

@Entity
@Data
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true)
    private String name;
}

 

 

7) PrincipalOauth2Service로 돌아와서 디비에 값 집어 넣기 

package com.example.ouath2.service;

import com.example.ouath2.entity.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

@Service
public class PrincipalOauth2Service extends DefaultOAuth2UserService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    //구글 로그인한 사용자 정보 디비에 값 집어넣기
    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2User oAuth2User = super.loadUser(userRequest);
        String provider=userRequest.getClientRegistration().getRegistrationId();
        String providerId=oAuth2User.getAttribute("sub"); //10713626148...
        String username=provider + "-" + providerId;
        String password=passwordEncoder.encode("임의값");
        String uname=(String)oAuth2User.getAttribute("name");
        String email=(String)oAuth2User.getAttribute("email");
        
        Set<Role> roles=new HashSet<>();

        return super.loadUser(userRequest);
    }
}

 

8) RoleRepository Interface 생성 후 extends JpaRepository<Role, Long>

package com.example.ouath2.repository;

import com.example.ouath2.entity.Role;
import org.springframework.data.jpa.repository.JpaRepository;

public interface RoleRepository extends JpaRepository<Role, Long> {

    public Role findByName(String name);
    
}

 

9) PrincipalOauth2Service에서 Set에 userRole정보를 가져와서 담기 -> Set<Role> roles=new HashSet<>();

     -> Role userRole = rolseRepository.findByName("USER"); roles.add(userRole); 

package com.example.ouath2.service;

import com.example.ouath2.entity.Role;
import com.example.ouath2.repository.RoleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

@Service
public class PrincipalOauth2Service extends DefaultOAuth2UserService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private RoleRepository roleRepository;

    //구글 로그인한 사용자 정보 디비에 값 집어넣기
    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2User oAuth2User = super.loadUser(userRequest);
        String provider=userRequest.getClientRegistration().getRegistrationId();
        String providerId=oAuth2User.getAttribute("sub"); //10713626148...
        String username=provider + "-" + providerId;
        String password=passwordEncoder.encode("임의값");
        String uname=(String)oAuth2User.getAttribute("name");
        String email=(String)oAuth2User.getAttribute("email");

        Set<Role> roles=new HashSet<>();

        Role userRole = roleRepository.findByName("USER");
        roles.add(userRole);

        return super.loadUser(userRequest);
    }
}

 

10) DB에 사용자 데이터를 저장하기 전에 회원 유무 확인하기 

      -> MemberRepository Interface 생성 후 extends JpaRepository<Member, Long>

      -> Optional Member값이 있으면 true, 아니면 false

package com.example.ouath2.repository;

import com.example.ouath2.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface MemberRepository extends JpaRepository<Member, Long> {

    public Optional<Member> findByUsername(String username); //있냐 없냐 확인하는 Optional
    
}

 

11) PrincipalOauth2Service에서 사용자가 로그인을 한적이 있냐 없냐 즉, DB에 값이 있냐 없냐 확인 

package com.example.ouath2.service;

import com.example.ouath2.entity.Member;
import com.example.ouath2.entity.Role;
import com.example.ouath2.repository.MemberRepository;
import com.example.ouath2.repository.RoleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

@Service
public class PrincipalOauth2Service extends DefaultOAuth2UserService {
    // 1. 구글 로그인한 사용자 정보 가져오기 -> 회원가입 -> 디비에 삽입
// 2. 로그인을 한적이 있냐 없냐 즉, DB에 값이 있냐 없냐
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private RoleRepository roleRepository;

    @Autowired
    private MemberRepository memberRepository;

    //구글 로그인한 사용자 정보 디비에 값 집어넣기
    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2User oAuth2User = super.loadUser(userRequest);
        String provider = userRequest.getClientRegistration().getRegistrationId();
        String providerId = oAuth2User.getAttribute("sub"); //10713626148...
        String username = provider + "-" + providerId;

        Optional<Member> optional = memberRepository.findByUsername(username);
        Member member = null;

        if (optional.isPresent()) {
            System.out.println("already login");
            member = optional.get();
        } else {
            System.out.println("first oauth login");

            String password = passwordEncoder.encode("임의값");
            String uname = (String) oAuth2User.getAttribute("name");
            String email = (String) oAuth2User.getAttribute("email");

            Set<Role> roles = new HashSet<>();

            Role userRole = roleRepository.findByName("USER");
            roles.add(userRole);

            member = new Member();
            member.setUsername(username);
            member.setPassword(password);
            member.setUname(uname);
            member.setProvider(provider);
            member.setProviderId(providerId);
            member.setEmail(email);
            member.setRoles(roles);
            memberRepository.save(member);
        }
        return null;
    }
}

 

12) ddl-auto: update로 바꾸기 

728x90