스프링 시큐리티와 OAuth2.0으로 로그인 기능 구현하기(구글로그인)

Wintering·2022년 6월 15일
0

스프링 시큐리티

막강한 인증과 인가(권한 부여) 기능을 가진 프레임 워크
스프링 기반의 애플리케이션에서는 보안을 위한 표준
인터셉터, 필터 기반의 보안 기능을 구현하는 것 보다는 스프링 시큐리티를 통해 구현하는 걸 적극 권장

스프링 시큐리티와 스프링 시큐리티 Oauth2 클라이언트

  • 로그인 시 기본적으로 구현해야 하는 것

    로그인 시 보안
    회원가입 시 이메일 혹은 전화번호 인증
    비밀번호 찾기
    비밀번호 변경
    회원정보 변경

  • Spring Oauth2 연동

    spring-security-oauth2-autoconfigure

    • 위 라이브러리를 사용하면 스프링부트2에서도 1.5의 설정을 그대로 가져올 수 있음
    • 하지만, 해당 실습에서는 스프링부트2 방식인 Spring Security Oauth2 Client 라이브러리 사용
    • 스프링 부트 2 방식의 자료를 찾고 싶은 경우, 인터넷 자료들에서
      spring-security-oauth2-autoconfigure 라이브러리를 썻는지 확인하고,
      application.yml or application.properties의 설정을 확인
    • 1.5 방식에서는 url주소를 모두 명시
      2.0 방식에서는 client 인증 정보만 입력
      하면 된다. 이 외의 입력값들은 모두 CommonOAuth2Provider라는 enum이 추가 되어 기본설정값을 제공

1. 구글 서비스 등록

https://console.cloud.google.com

1-1. 구글 서비스에 신규 서비스를 생성

(clietId와 clientSecret)을 통해서 로그인 기능과 소셜 서비스 기능을 사용할 수 있음
프로젝트 생성 후 API 서비스 카테고리로 이동
사용자 인증정보 > 사용자 인증정보만들기 > OAuth 클라이언트 ID

  1. OAuth 동의 화면을 먼저 구성

  2. 사용자 인증 정보 > OAuth 클라이언트 ID 만들기

  • 승인된 리디렉션 URI
    • 서비스에서 파라미터로 인증 정보를 주었을 때 인증이 성공하면 구글에서 리다이렉트할 URL
    • 스프링부트2 버전의 시큐리티에서는 기본적으로 [도메인]/login/oauth2/code/[소셜서비스코드]로 리다이렉트 URL을 지원
    • 사용자가 별도로 리다이렉트 URL을 지원하는 Controller를 만들 필요가 X
    • AWS 서버에 배포하게 될 경우 localhost8080 이외의 주소를 추가해야 함

1-2.application-oauth 등록

resources에 application-oauth.properties 생성
위와 같이 코드를 입력하고, 생성한 클라이언트 ID와 클라이언트 비밀번호를 입력

  • scope = profile,email
    • 이 scope는 대체로 별도로 등록하지 X
    • 기본값이 openid, profile,email이기 때문
    • 강제로 profile, email을 등록한 이유는 openid라는 scope가 있으면 Open Id Provider로 인식하기 때문
      • 이 경우 OpenId Provider인 서비스(구글)과 그렇지 않은 서비스(네이버/카카오 등)로 나눠서 OAuth2Service를 만들어야함.
      • 하나의 Oauth2Service로 사용하기 위해서 일부로 openid scope를 빼고 등록
  • application-xxx.properties 로 클래스를 만들면 xxx라는 이름의 profile이 생성되어 이를 통해 관리할 수 있다. 즉 profile = xxx라는 식으로 호출하면 해당 properties의 설정들을 가져올 수 있음
  • application.properties에 application-oauth.properties를 포함하도록 구성

    spring.profiles.include=oauth

  • .gitigonre등록
    • 보안이 필요한 항목들 (구글 아이디, 비밀번호 등)이 깃허브에 올라가지 않도록

      application-oauth.properties


1-3. 구글 로그인 연동하기

  • @Enumerated(EnumType.STRING)
    • JPA로 데이터베이스로 저장할 때 Enum값을 어떤 형태로 저장할지를 결정
    • 기본적으로는 int형이 저장
    • 숫자로 저장되면 데이터베이스로 확인할 때 그 값이 무슨 코드를 의미인지 알 수 X
    • 그래서 문자열로 저장될 수 있도록 저장형을 바꿔줌

각 사용자의 권한을 관리할 Enum클래스 Role 생성

  • 스프링 시큐리티에서는 권한 코드에 항상 ROLE_이 앞에 붙어야함.

  • UserRepository생성

👉🏻 User 엔티티 관련 코드를 모두 작성완료!


1-4. 스프링 시큐리티 설정

1. build.gradle에 의존성 추가

implementation('org.springframework.boot:spring-boot-starter-oauth2-client')

  • 소셜 로그인 등 클라이언트 입장에서 소셜 기능 구현 시 필요한 의존성
  • spring-security-oauth2-client와 spring-security-oauth2-jose를 기본으로 관리해주는 역할

2. Oauth 라이브러리를 이용한 소셜 로그인 설정 코드를 작성

  • config.auth패키지 생성 -> 시큐리티 관련 클래스는 모두 이곳에 담기
  • SecurityConfig.class
  • @EnableWEbSecurity
    • Spring Security 설정들을 활성화 시킴
  • csrf().disable().headers().frameOptions().disable()
    • h2-console 화면을 사용하기 위해 해당 옵션들을 disable함
  • authorizeRequests
    • URL별 권한 관리를 설정하는 옵션의 시작점
    • authorizeRequests가 선언되어야만 antMatchers 옵션 사용 가능
      -antMathcers
    • 권한 관리 대상 지정 옵션
    • URL, HTTP 메소드 별로 관리가능
    • "/"등 지정 된 URL들은 permitAll() 옵션을 통해 전체 열럼 권한을 줌
    • "/api/v1/**" 주소를 가진 api는 USER 권한을 가진 사람만 가능하도록 열어줌
  • anyRequest
  • 설정 된 값 이외의 나머지 URL
    • 여기서는 authenticated()를 추가하여 나머지 URL들은 모두 인증 된 사용자들에게만 허용하게 함
    • 인증된 사용자 즉, 로그인한 사용자를 의미
  • logout().logoutSuccessUrl("/")
    • 로그아웃 기능에 대한 여러 설정의 진입점
    • 로그아웃 성공 시 / 주소로 이동
  • oauth2Login
    • OAuth2 로그인 기능에 대한 여러 설정의 진입점
  • userInfoEndpoint
    • 로그인 성공 이후 사용자 정보를 가져올 때의 설정담당
  • userService
    • 소셜 로그인 성공시 후속 조치를 진행할 UserService인터페이스의 구현체 등록

    • 리소스 서버에서 사용자 정보를 가져온 상태에서 추가로 진행하고자 하는 기능을 명시!


    3. CustomOAuth2UserService 생성

    구글 로그인 후 가져온 사용자의 정보(email, name, picture등) 등을 기반으로 가입 및 정보수정, 세션 저장 등의 기능을 지원
    • registrationId

      • 현재 로그인 진행중인 서비스를 구분하는 코드
      • 다른 소셜 로그인 연동 시, 네이버인지 구글인지를 구분하기 위해 사용
    • userNameAttributeName

      • OAuth2 진행 시 키가 진행되는 필드값.
        Primary Key와 같은의미
      • 구굴의 경우 기본적인 코드를 지원(네이버, 카카오는 지원 x), 구글의 기본 코드는 "sub"
      • 이후 네이버 로그인과 구글 로그인을 동시 지원할 때 사용
    • OAuthAttributes (Dto로 취급함)

      • OAuth2UserService를 통해 가져온 OAuth2User의 Attribute를 담은 클래스
      • 다른 소셜 로그인에도 사용
    • SessionUser

      • 세션에 사용자 정보를 저장하기 위한 Dto 클래스

4. OAuthAttributes 생성

  • of()
    • OAuth2User에서 반환하는 사용자 정보는 Map이기 때문에 값 하나하나를 변환해줘야함
  • toEntity()
    • User엔티티를 생성
    • OAuthAttributes에서 엔티티를 생성하는 시점은 처음 가입할때임
    • 가입할 때의 기본권한을 GUEST로 주기 위해서 role 빌더값에 ROLE.GUEST를 넣어줌
    • OAuthAttributes 클래스 생성이 끝나면 SessionUser클래스 생성

5. SessionUser 생성


왜? User클래스를 그대로 사용하지 않고, SessionUser로 별도로 사용할까?

🔻error
Failed to convert from type [java.lang.Object] to type [byte[]] for value 'com.jojoldu.book.springboot.domain.user.User@4a43d6'

  • User 클래스를 그대로 사용하면 위와 같은 에러 발생
    User 클래스를 세션에 저장하려고하면 직렬화를 구현하지 않았다는 에러가 발생 (cf.자바의직렬화)
    User 클래스는 엔티티 클래스이기 때문에 언제 다른 엔티티와 무슨 관계가 생길지 알 수 없으므로 성능이슈, 부수 효과등 문제점이 발생. 따라서 직렬화 기능을 가진 세션 Dto를 추가로 만드는 거싱 이후 운영 및 유지보수에 유리

6. index.mustache에 로그인 기능 생성

  • {{#userName}}
    • 머스태치는 다른 언어와 같은 If문을 제공하지 않음 (ex. if userName != null)
    • true/false의 여부만 판단
    • 때문에 머스태치에는 항상 최종값을 넘겨준다.
  • a href = "/logout"
    • 스프링 시큐리티에서 기본적으로 제공하는 로그아웃 url
    • 개발자가 별도의 URL에 해당하는 컨트롤러를 만들 필요가 X
    • SecurityConfig에서 URL을 변경할 수는 있찌만 기본 URL을 사용해도 충분
  • {{^userName}}
    • 머스테치에서는 해당값이 존재하지 않으면 ^f를 사용
    • 즉, userName이 없다면 로그인 버튼을 노출시키는 코드
  • a href="/oauth2/authorization/google"
    • 스프링 시큐리티에서 기본적으로 제공하는 로그인 URL
    • 로그아웃 URL과 마찬가지로 개발자가 별도의 컨트롤러를 생성할 필요 X

7. index.mustache에서 userName을 사용할 수 있도록 IndexController에 userName을 model에 저장하는 코드 생성

Application을 실행하고 구글 로그인을 누르면 구글 로그인 창이 뜨고, UserDB에 가입정보가 잘 들어온 걸 확인할 수 있음
👉🏻 하지만 현재 로그인 된 권한이 GUEST이기 때문에 Post를 등록할 수가 없다.

update user set role = 'USER'을 사용해 role을 USER로 바꿔주면 글쓰기가 가능해진다.

0개의 댓글