구글 로그인을 연동하기 위해서 먼저 User class를 생성한다.


위 코드는 DB에 저장될 데이터 테이블 빌딩으로 이름, 이메일, 프로필, 역할을 테이블의 열 목록으로 한다.
그리고 위의 코드를 작성하는 과정에서 Role class부분에 컴파일 에러가 뜰 것이다. 그렇기에 Role class를 생성한다.

Role class는 열거형 클래스인 enum클래스로 생성한다.
추가적으로 annotaion인 @Enumerated(EnumType.STRING)은 JPA로 DB에 저장할때 Enum값을 String형태로 저장하는 것이다.(기본적으로는 int형태이다.) 이 설정을 하지 않는다면 DB에서 사용자의 Role부분의 값이 int형으로 되어 있어서 손님이라면 0으로 표시되고 일반사용자라면 1로 표시될 것이다. 그렇기에 그 값이 무슨 코드를 의미하는지 알 수가 없다.
그리고 jpa를 이용한 DB생성을 위해서 아래의 인터페이스를 구현한다.

이메일을 통한 사용자 찾기 메소드를 추가하여 설정한다. 이를 통해서 JPA가 분석하여 JPQL을 생성하여 이미 생성된 사용자인지 처음 가입하는 사용자인지 판단 할 수 있다.
그리고 스프링 시큐리티 의존성을 추가한다.
implements 'org.springframework.boot:spring-boot-starter-oauth2-client'
다음으로 시큐리티 권한 설정을 위한 Security config class를 작성한다.

annotation EnableSecurity를 통해서 springSecurity 설정들을 활성화 시켜줍니다.
@RequiredArgsConstructor는 final이 붙거나 @NotNull 이 붙은 필드의 생성자를 자동 생성해주는 롬복 어노테이션으로 생성자 주입방식을 간단하게 수행시켜준다.
그리고 이 class는 WebSecurityConfigurerAdapter를 상속받는다.
이를 통해서 세부적인 보안 기능을 설정하는 API를 제공받는다.
h2-console화면을 사용하기 위한 설정으로 csrf().disable.headers().frameOptions().disable을 추가하고
authorizeRequests는 URL별 권한 관리 설정하는 옵션의 시작점이다.
이 뒤에오는 authMathers는 권한 대상자를 지정하는 옵션이다. permitall()이 붙은 부분은 전체 열람권한을 부여하고 hasRole(Role.USER.name())는 USER 권한을 가진 사람에게만 열람권한을 부여한다. 여기서 말하는 USER권한은 위에서 생성한 Role enum class의 USER 부분을 말한다. 여기서 Role.USER.name()에서 .name()메소드는 enum의 상수를 string으로 반환하는 메소드이다.
그리고 나머지 request들은 권한이 있는 사람만 열람가능하도록 설정했다. 즉, Role이 USER인 사용자만 열람이 가능하다.
그 다음 로그아웃 시에는 welcomepage로 이동하도록한다. 그리고 로그인 시에
oauth2Login()=OAuth2 로그인 기능에 대한 여러 설정의 진입점,
userInfoEndpoint=OAuth2 로그인 성공 후 사용자 정보를 가져올 때의 설정 담당,
userService(customOAuth2UserService)=로그인 성공 시 후속 조치를 진행할 UserService 인터페이스의 구현체를 등록한다. 리소스 서버(소셜 서비스)에서 사용자 정보를 가져온 상태에서 추가로 진행하고자 하는 기능을 명시한다.
권한 설정 코드 작성이 끝났다. 하지만 여기서 컴파일 오류가 뜰것이다. 왜냐하면 customOAuth2Service class를 작성하지 않았기 때문이다. 이 클래스는 구글 로그인 이후 가져온 사용자 정보를 기반으로 가입 및 정보수정, 세션 저장 등의 기능을 지원한다. 그래서 customOAuth2Service를 작성하면 다음과 같다.

일단 권한설정에서 사용했던 userService의 parameter가 OAuth2UserService<OAuth2UserRequest,OAuth2User> userService 이기 때문에 이를 상속받고, 그렇게하면 여기에는 필수적으로 작성해야할 override 메소드인 loadUser가 있다.
이를 작성해야한다. 이 메소드는 하나의 OAuth2User를 반환해야한다.
코드를 분석해보면
OAuth2UserService delegate = new DefaultOAuth2UserService();
새로운 userservice 객체(delegate)를 만든다.
OAuth2User oAuth2User = delegate.loadUser(userRequest);
그리고 그 객체에서 oAuth2User객체를 가져온다.OAuth2UserService.loadUser()의 반환 형태는 OAuth2User 형태이다. 즉,userRequest에 포함되어 있던 UserInfo Endpoint를 통해서 얻은 자료로 User객체를 가져와서 OAuth2User형태의 객체를 반환한다. 여기까지가 user객체 가져오기이다.
그리고 추가적으로 UserInfo Endpoint를 얻은 userRequest에서 현재 로그인 진행중인 서비스를 구분하는 registrationId와 OAuth2로그인 진행시 키가 되는 필드값인 userNameAttributeName(Primary Key와 같은 의미다)을 가져온다.
그 다음에 OAuth2Attributes 클래스를 생성한다. 이 클래스는 OAuth2UserService를 통해서 가져온 OAuth2User의 attributes를 담을 클래스다. 그래서 위의 userRequest에서 얻은 registrationId와userNameAttributeName 그리고 oAuth2User의 attributes 값들을 여기에 attributes값으로서 저장하고, 이것들을 저장하거나 업데이트할 user에게 전달한다.
그 메소드는 saveOrUpdate()로 정의한다. DB에서 email을 통해서 존재유무를 파악하고 존재한다면 이름과 사진을 업데이트한다.
그렇지 않다면 새로 저장한다.
세션에 저장하는 것은 httpSession을 사용한다. User class대신 SessionUser class를 따로 생성하여서 사용한다.
이렇게 한 이유는 성능 이슈, 부수 효과 발생 확률등 때문에 에러가 뜨기때문에 엔티티 클래스인 User클래스를 직렬화 하는 대신에 새로 직렬화 기능을 가진 SessionUser class를 만드는 것이 효율적이기 때문이다.
이제 위의 코드에서 사용한 OAuth2Attribute class를 정의해야한다.
여기에는 .of(), .toEntity()메소드를 구현해야한다.


이 클래스의 builder를 보면 attributes와 nameAttributeKey, name, email, picture를 parameter로 받는다.
그래서 CustomOAuth2UserService에서는 .of(registrationId,userNameAttributeName)을 사용했다. 이걸로 ofGoogle메소드를 반환한다. 그렇게해서 위와 같은 코드를 구현하여 attributes값을 반환하고 이를 User의 값으로 저장하여 DB에 저장 or 업데이트한다.
그 다음으로 SessionUser class를 정의한다.

여기서는 인증된 사용자 정보만 필요하기 때문에 name, email, picture만 필드로 선언한다.(role,id는 필요없다.)
이렇게 백부분이 완성되었으니 프런트에서 접근할 수 있게 만들어야한다.
그러려면 index.mustache부분을 수정해야한다.

이 부분을 추가하였는데 {{#userName}}은 IndexController로 부터 전달 받은 model에 userName이 있다면 이 부분을 보여주는 코드이다. 해당값이 없다면 보여주는 코드는 아래의 {{^userName}}부분이다.
즉, userName이 있다면 Logged in as "사용자 이름"이 보일 것이고, 없다면 로그인을 하라는 Google Login버튼이 보일 것이다.
"/oauth2/authorization/google"은 스프링 시큐리티에서 기본적으로 지원하는 로그인 URL이다.
그렇다면 이제 indexController로 부터 model을 받아야 함으로 indexController 또한 수정하여야 한다.

수정하면 위의 코드와 같다.
앞서 작성된 CustomOAuth2UserService에서 로그인 성공 시 세션에 SessionUser를 저장하도록 구성했음(httpSession.setAttribute("user", new SessionUser(user));)으로 로그인 성공 시 httpSession.getAttribute("user")에서 값을 가져올 수 있다.
세션에 저장된 값이 있을 때만 model에 userName으로 등록한다. 세션에 저장된 값이 없으면 model엔 아무런 값이 없는 상태이니 로그인 버튼이 보이게 된다.
이렇게 해서 구글 로그인 연동을 했다. 하지만 여기서 로그인 한 후 글을 등록하려고하면 403(권한 거부)에러가 뜰 것이다. 그 이유는 로그인한 사용자의 권한을 GUEST를 기본값으로 했기 때문이다.
따라서 글을 등록하려면 h2-console에서 Role 값을 USER로 업데이트 해야한다.
SELECT * FROM USER;
update user set role = 'USER';
그리고 이를 수행하기 위해선
구글 클라우드를 이용해서 사용자 인증정보를 얻어야한다 그렇게 해서 application-oauth.properties에 클라이언트id와 pw를 작성하고 scope설정을 profile, email로 한다.
그리고 이를 application.properties에 연결한다.
spring.profiles.include=oauth이 코드를 삽입하면 된다.
'Spring' 카테고리의 다른 글
타임리프 활용 (0) | 2023.06.26 |
---|---|
타임리프 주석사용법 (1) | 2023.06.21 |
HTTP 에 대하여 (0) | 2023.02.17 |
수정 및 DB연결 관련 오류해결 (0) | 2022.11.07 |
게시글 사이트 조회 목록 만들기 오류 해결 (0) | 2022.11.07 |