티스토리 뷰

반응형

자세한 코드: Github

 

구글, Github, 네이버 등과 같은 소셜 로그인 기능을 사용하는 경우를 발견할 수 있다.

 

로그인 기능을 직접 구현하면 다음과 같은 기능이 필요하다.

  • 로그인 시 보안
  • 회원가입 시 본인 인증
  • 비밀번호 찾기
  • 비밀번호 변경
  • 회원정보 변경

 

OAuth 로그인을 사용하면 위의 기능들을 직접구현 하는 것을 대체할 수 있다는 장점이 있다.

 

Google Cloud Platfrom 설정

Google Cloud Platform에 접속하여 상단 바에 있는 프로젝트 선택 탭을 클릭한다.

 

오른쪽 위의 새 프로젝트를 클릭한다.

 

프로젝트 이름을 입력하고 프로젝트를 만든다.

 

상단바에 왼쪽에 메뉴에 들어가서 API 및 서비스 메뉴로 이동한다.

 

사용자 인증 정보 메뉴에 들어간 다음, [+ 사용자 인증 정보 만들기] 버튼을 클릭한다.

 

[OAuth 클라이언트 ID]를 클릭한다.

 

동의 화면 구성을 클릭한다.

 

내부는 조직 내 사용자만, 외부는 Google 계정이 있는 모든 사용자가 사용할 수 있다.

 

User Type은 외부로 설정해서 만든다.

 

위와 같은 절차를 진행한다.

 

앱 정보에 앱 이름을 이볅하고 그 외의 필요한 정보들을 입력한다.

 

차례대로 진행하면 된다.

 

사용자 인증 정보 항목에 들어가서 [+ 사용자 인증 정보 만들기]에서 [OAuth 클라이언트 ID]를 클릭한다.

 

제작한 애플리케이션의 유형을 선택한다.

 

승인된 리디렉션 URI에 입력을 하고 [만들기]를 클릭한다.

 

승인된 리디렉션 URI란?

  • 서비스에서 파라미터로 인증 정보를 주었을 때 인증이 성공하면 구글에서 리다이렉트할 URL
  • 스프링 부트 2 버전의 시큐리티에서는 기본적으로 {도메인}/login/oauth2/code/{소셜서비스코드}로 리다이렉트 URL을 지원
  • 시큐리티에서 이미 구현해 놓은 상태이기 때문에 사용자가 별도로 URL을 지원하는 Controller를 만들 필요가 없음
  • 위의 이미지에서는 개발 단계이기 때문에 localhost:8080을 사용
  • AWS 서버에 배포할 시 localhost 외에 추가로 주소를 추가해야 함

 

만들기를 하면 위와 같은 항목을 볼 수 있다.

 

클라이언트 ID와 클라이언트 보안 비밀 코드를 프로젝트에서 설정

src/main/resources/ 디렉토리에 application-oauth.properties 파일을 생성한다.

 

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

= 오른쪽에 발급 받은 클라이언트ID와 클라이언트 보안 비밀을 입력한다.

 

 

scope=profile,email

  • 기본값이 openid, profile, email
  • 위의 코드에서 openid가 없는 이유는 openid가 있다면 Open Id Profider로 인식하기 때문
  • OpenId가 있다면 OpenId Provider인 서비스(구글)와 그렇지 않은 서비스(네이버/카카오 등)으로 나눠서 OAuth2Service를 만들어야 함
  • profile과 email만 등록함으로써 하나의 OAuth2Service를 사용

 

 

스프링부트에서 properties의 이름을 application-xxx.properties로 만들면 xxx라는 이름의 profile이 생성되고 이를 통해 관리할 수 있다. profile=xxx라는 식으로 호출하면 해당 properties의 설정을 가져올 수 있다.

spring.profiles.include=oauth

application.properties에 위의 내용을 추가하면 application-oauth.properties의 설정값을 사용할 수 있다.

 

구글 로그인을 위한 클라이언트 ID와 클라이언트 보안 비밀은 개인 정보이다. 이 정보들이 노출되면 개인정보가 유출되는 문제가 발생할 수 있다. .gitinore에 해당 파일을 등록하여 올라가는 것을 방지해야 한다.

 

vi .gitignore을 하여 application-oauth.properties 파일을 등록한다.

 

git status를 사용해서 파일이 보이지 않으면 성공이다.

 

💡 .gitignore이 적용되지 않는 경우

git rm -r --cached .
git add .
git commit -m "fixed untracked files"

위의 세 명령어로 캐시를 삭제하면 해결된다.

 

 

@Getter
@NoArgsConstructor
@Entity
public class User extends BaseTimeEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private String email;

    @Column
    private String picture;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private Role role;

    @Builder
    public User(String name, String email, String picture, Role role) {
        this.name = name;
        this.email = email;
        this.picture = picture;
        this.role = role;
    }

    public User update(String name, String picture) {
        this.name = name;
        this.picture = picture;

        return this;
    }

    public String getRoleKey() {
        return this.role.getKey();
    }
}

@Enumerated(EnumType.STRING)

  • JPA로 데이터베이스에 저장할 때 Enum 값을 어떤 형태로 저장할지 결정
  • 기본은 int
  • 숫자로 저장이 되면 값이 무슨 코드를 의미하는지 알 수 없음

 

public enum Role {

    GUEST("ROLE_GUEST", "손님");
    USER("ROLE_USER", "일반 사용자");

    private final String key;
    private final String title;
}

권한을 관리한는 Enum 클래스이다.

 

스프링 시큐리티에서는 권한 코드의 앞에 ROLE_이 있어야만 한다

 

import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
}

User의 CRUD를 담당하는 UserRepository이다.

 

Optional<User> findByEmail(String email);

  • 소셜 로그인으로 반환되는 값 중 email을 통해 이미 생성된 사용자인지 처음 가입하는 사용자인지 판단하는 메소드
// build.gradle 파일
// 라이브러리들을 받아올 원격 저장소 결정
dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    compile('org.projectlombok:lombok')
    compile('org.springframework.boot:spring-boot-starter-data-jpa')
    compile('com.h2database:h2')
    compile('org.springframework.boot:spring-boot-starter-mustache')
    compile('org.springframework.boot:spring-boot-starter-oauth2-client') // 시큐리티 관련 의존성
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

build.gradle에 dependencies에 아래에 있는 시큐리티 관련 의존성을 추가한다.

 

spring-boot-starter-oauth2-client

  • 클라이언트 입장에서 소셜 기능 구현 시 필요한 의존성
  • spring-security-oauth2-client와 spring-security-oauth2-jose를 기본으로 관리
💡 의존성을 추가하고 나서는 항상 새로고침 버튼을 누르도록 하자! 이것 때문에 문제를 찾느라 꽤 오래 걸렸다.

 

RequiredArgsConstructor
@EnableWebSecurity // Spring Security 설정들을 활정화
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private final CustomOAuth2UserService customOAuth2UserService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable().headers().frameOptions().disable() // h2-console 화면을 사용하기 위한 설정
                .and()
                    .authorizeRequests() // URL별 권한 관리를 설정하는 옵션의 시작점
                    .antMatchers("/", "/css/**", "/images/**", "/js/**", "/h2-console/**").permitAll() // 전체 열람 권한
                    .antMatchers("/api/v1/**").hasRole(Role.USER.name()) // USER 권한을 가진 사람만
                    .anyRequest().authenticated() // anyReQuest(): 설정된 값들 이외 나머지 URL, authenticated
                .and()
                    .logout() // 로그아웃 시
                        .logoutSuccessUrl("/") /// 로 이동
                .and()
                    .oauth2Login() // oauth2 로그인
                        .userInfoEndpoint() // 로그인 이후 사용자 정보를 가져올 때 설정
                            .userService(customOAuth2UserService); // 로그인 성공 시 진행할 인터페이스 구현체 등록
    }
}

@EnableWebSecurity

  • Spring Security 활성화

 

authorizeRequests()

  • antMatchers 옵션을 사용하기 위한 코드

 

antMatchers

  • URL, HTTP 메소드 등으로 권한 관리 대상 지정
  • "/"은 모두 허용
  • "/api/v1/**"는 USER 권한을 가지면 허용

 

anyRequest

  • 나머지 URL 처리

 

authenticated

  • 인증된 사용자들만

logout().logoutSuccessUrl("/")

  • 로그아웃시 이동

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함