Day 9

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์™€ OAuth 2.0์œผ๋กœ ๋กœ๊ทธ์ธ ๊ตฌํ˜„

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ

  • ๋ง‰๊ฐ•ํ•œ ์ธ์ฆ(Authentication)๊ณผ ์ธ๊ฐ€(Authorization) ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ํ”„๋ ˆ์ž„์›Œํฌ

  • ์‚ฌ์‹ค์ƒ ์Šคํ”„๋ง ๊ธฐ๋ฐ˜์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ๋ณด์•ˆ์„ ์œ„ํ•œ ํ‘œ์ค€

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์™€ ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ Oauth2 ํด๋ผ์ด์–ธํŠธ

ํ˜„์žฌ ๋งŽ์€ ์„œ๋น„์Šค์—์„œ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ์†Œ์…œ๋กœ๊ทธ์ธ ์„ ์‚ฌ์šฉ

freelec-springboot2-webservice๋ผ๋Š” ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑ,

OAuth ๋™์˜ํ™”๋ฉด์„ ์ƒ์„ฑํ•œ ํ›„,

์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด์— redirection url์„ ๋“ฑ๋กํ•˜๊ณ  ์ƒˆ๋กœ ์ƒ์„ฑํ•˜๊ฒŒ ๋˜๋ฉด

  • ํ•ด๋‹น ํ”„๋กœ์ ํŠธ์˜ clientId์™€ security์ฝ”๋“œ๊ฐ€ ๋‚˜์˜ค๊ฒŒ ๋œ๋‹ค

์ด์ œ๋Š” ํ˜„์žฌ๊นŒ์ง€ ์ง„ํ–‰ํ•ด์˜ค๋˜ ํ”„๋กœ์ ํŠธ์— application-oauth ์„ค์ •ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์ค˜์•ผ ํ•จ

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
  • scope = profile, email

    • ๋งŽ์€ ์˜ˆ์ œ์—์„œ ์‹ค์ œ๋กœ scope์„ ๋“ฑ๋กํ•˜์ง€ ์•Š๊ณ  ์‚ฌ์šฉํ•œ๋‹ค

    • ๊ธฐ๋ณธ๊ฐ’์ด openid, profile, email์ด๋‹ค

    • ํ•˜์ง€๋งŒ openid๋ผ๋Š” scope๊ฐ€ ์žˆ์œผ๋ฉด Open Id Provider๋กœ ์ธ์‹ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋บŒ

      • ์ด๋ ‡๊ฒŒ ๋˜๋ฉด OpenId Provider์ธ ๊ตฌ๊ธ€๊ณผ ๊ทธ๋ ‡์ง€ ์•Š์€ ์„œ๋น„์Šค(๋„ค์ด๋ฒ„/์นด์นด์˜ค)๋กœ ๋‚˜๋ˆ ์„œ OAuth2Service๋ฅผ ๋งŒ๋“ค์–ด์•ผํ•œ๋‹ค

์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ๋Š” properties์˜ ์ด๋ฆ„์„ application-๊ด€๋ฆฌํ• ํ•ญ๋ชฉ.properties๋กœ ๋งŒ๋“ค๊ฒŒ ๋˜๋ฉด

๊ด€๋ฆฌํ• ํ•ญ๋ชฉ ์ด๋ฆ„์˜ profile์ด ์ƒ์„ฑ๋˜์–ด ์ด๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ๋จ

profile=๊ด€๋ฆฌํ• ํ•ญ๋ชฉ ์ด๋ ‡๊ฒŒ ํ˜ธ์ถœํ•˜๋ฉด ํ•ด๋‹น properties์˜ ์„ค์ •๋“ค์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Œ

application.properties ์—์„œ application-oauth.properties๋ฅผ ํฌํ•จํ•˜๋„๋ก ๊ตฌ์„ฑ

spring.profiles.include=oauth
  • ์ด๋ ‡๊ฒŒ ์œ„์—์„œ ์„ค์ •ํ•œ ๊ฐ’๋“ค์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•ด์ง

์ด์ œ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋“ค์ด ํ”„๋กœ์ ํŠธ์— ๋“ค์–ด๊ฐ€๊ฒŒ ๋˜๋ฉด์„œ .gitignore์— ์ถ”๊ฐ€ํ•ด์ค˜ํ•  ์ฝ”๋“œ๊ฐ€ ์ƒ๊น€

application-oauth.properties

์ถ”๊ฐ€ํ•ด์ฃผ๊ณ  commit ํ•˜๋ฉด application-oauth.properties ์ด ํŒŒ์ผ์€ stage์— ์•ˆ์˜ฌ๋ผ์˜จ ๊ฒƒ์„ ํ™•์ธ๊ฐ€๋Šฅ

๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ ์—ฐ๋™ํ•˜๊ธฐ

์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋‹ด๋‹นํ•  ๋„๋ฉ”์ธ์ธ User ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑ

src/main/java/com/kyu/book/springboot/domain/user

package com.kyu.book.springboot.domain.user;

import com.kyu.book.springboot.domain.BaseTimeEntity;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.management.relation.Role;
import javax.persistence.*;

@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๋กœ ๋œ ์ˆซ์ž๊ฐ€ ์ €์žฅ

    • ์ˆซ์ž๋กœ ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ ํ™•์ธํ•  ๋•Œ ๊ทธ ๊ฐ’์ด ๋ฌด์Šจ ์ฝ”๋“œ๋ฅผ ์˜๋ฏธํ•˜๋Š”์ง€ ์•Œ ์ˆ˜๊ฐ€ ์—†์Œ

    • ๊ทธ๋ž˜์„œ ๋ฌธ์ž์—ด(EnumType.STRING)๋กœ ์ €์žฅ๋  ์ˆ˜ ์žˆ๋„๋ก ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค

๊ฐ ์‚ฌ์šฉ์ž์˜ ๊ถŒํ•œ์„ ๊ด€๋ฆฌํ•  Enum ํด๋ž˜์Šค Role์„ ์ƒ์„ฑ

src/main/java/com/kyu/book/springboot/domain/user/Role

package com.kyu.book.springboot.domain.user;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public enum Role {
    GUESR("ROLE_GUEST", "์†๋‹˜"),
    USER("ROLE_USER", "์ผ๋ฐ˜ ์‚ฌ์šฉ์ž");

    private final String key;
    private final String title;
}
  • ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์—๋Š” ๊ถŒํ•œ ์ฝ”๋“œ์— ํ•ญ์ƒ ROLE_ ์ด ์•ž์— ์žˆ์–ด์•ผํ•จ

  • ์ฝ”๋“œ๋ณ„ ํ‚ค ๊ฐ’์„ ROLE_GUEST, ROLE_USER ๋“ฑ ์œผ๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Œ

User์˜ CRUD๋ฅผ ์ฑ…์ž„์งˆ ์ธํ„ฐํŽ˜์ด์Šค UserRepository๋„ ์ƒ์„ฑ

src/main/com/kyu/book/springboot/domain/user/UserRepository

package com.kyu.book.springboot.domain.user;

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

import java.util.Optional;

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

    • ์†Œ์…œ ๋กœ๊ทธ์ธ์œผ๋กœ ๋ฐ˜ํ™˜๋˜๋Š” ๊ฐ’ ์ค‘ email์„ ํ†ตํ•ด ์ด๋ฏธ ์ƒ์„ฑ๋œ ์‚ฌ์šฉ์ž์ธ์ง€ ์ฒ˜์Œ ๊ฐ€์ž…ํ•˜๋Š” ์‚ฌ์šฉ์ธ์ง€ ํŒ๋‹จํ•˜๊ธฐ ์œ„ํ•œ ๋ฉ”์†Œ๋“œ

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ์„ค์ •

build.gradle์— ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ๊ด€๋ จ ์˜์กด์„ฑ ํ•˜๋‚˜๋ฅผ ์ถ”๊ฐ€

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

    • ์†Œ์…œ ๋กœ๊ทธ์ธ ๋“ฑ ํด๋ผ์ด์–ธํŠธ์˜ ์ž…์žฅ์—์„œ ์†Œ์…œ ๊ธฐ๋Šฅ ๊ตฌํ˜„ ์‹œ ํ•„์š”ํ•œ ์˜์กด์„ฑ

    • spring-security-oauth2-client์™€ spring-security-oauth2-jose๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ๊ด€๋ฆฌํ•ด์ค€๋‹ค

์„ค์ •์„ ์™„๋ฃŒํ•ด ์ฃผ์—ˆ์œผ๋‹ˆ OAuth ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•œ ์†Œ์…œ ๋กœ๊ทธ์ธ ์„ค์ • ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑ

config.authํŒจํ‚ค์ง€๋ฅผ ์ƒ์„ฑ โ‡’ ์‹œํ๋ฆฌํ‹ฐ ๊ด€๋ จ ํด๋ž˜์Šค๋ฅผ ๋‹ด๋Š” ํŒจํ‚ค์ง€

์„ค์ • ์ฝ”๋“œ ์ž‘์„ฑ

src/main/java/kyu/book/springboot/config/auth/SecurityConfig

package com.kyu.book.springboot.config.auth;

import com.kyu.book.springboot.domain.user.Role;
import lombok.RequiredArgsConstructor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
    private final CustomOAuth2UserService customOAuth2UserService;

    protected void configure(HttpSecurity http) throws Exception{
        http
                .csrf().disable()
                .headers().frameOptions().disable()
                .and()
                    .authorizeRequests()
                    .antMatchers("/", "/css/**", "/images/**","/js/**", "/h2-console/**").permitAll()
                    .antMatchers("/api/v1/**").hasRole(Role.USER.name())
                    .anyRequest().authenticated()
                .and()
                    .logout()
                        .logoutSuccessUrl("/")
                .and()
                    .oauth2Login()
                        .userInfoEndpoint()
                            .userService(customOAuth2UserService);
    }
}

and()ํ•จ์ˆ˜๊ฐ€ ๋ฌด์—‡์ธ์ง€?

  • @EnableWebSecurity

    • Spring Security ์„ค์ •๋“ค์„ ํ™œ์„ฑํ™” ์‹œ์ผœ์คŒ

  • csrf().disable().headers().frameOptions().disable()

    • h2-console ํ™”๋ฉด์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ํ•ด๋‹น ์˜ต์…˜๋“ค์„ disable

  • authorizeRequests

    • URL๋ณ„ ๊ถŒํ•œ ๊ด€๋ฆฌ๋ฅผ ์„ค์ •ํ•˜๋Š” ์˜ต์…˜์˜ ์‹œ์ž‘์ 

    • authorizeRequests๊ฐ€ ์„ ์–ธ ๋˜์–ด์•ผ๋งŒ antMatchers ์˜ต์…˜์„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

  • antMatchers

    • ๊ถŒํ•œ ๊ด€๋ฆฌ ๋Œ€์ƒ์„ ์ง€์ •ํ•˜๋Š” ์˜ต์…˜

    • URL, HTTP ๋ฉ”์†Œ๋“œ๋ณ„๋กœ ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅ

    • "/" ๋“ฑ ์ง€์ •๋œ URL๋“ค์€ permitAll() ์˜ต์…˜์„ ํ†ตํ•ด ์ „์ฒด ์—ด๋žŒ ๊ถŒํ•œ์„ ์คŒ

    • "/api/v1/**" ์ฃผ์†Œ๋ฅผ ๊ฐ€์ง„ API๋Š” USER๊ถŒํ•œ์„ ๊ฐ€์ง„ ์‚ฌ๋žŒ๋งŒ ๊ฐ€๋Šฅํ•˜๋„๋ก ํ–ˆ์Œ

  • anyRequest

    • ์„ค์ •๋œ ๊ฐ’๋“ค ์ด์™ธ ๋‚˜๋จธ์ง€ URL๋“ค์„ ๋‚˜ํƒ€๋ƒ„

    • ์—ฌ๊ธฐ์„œ๋Š” authenticated()์„ ์ถ”๊ฐ€ํ•˜์—ฌ ๋‚˜๋จธ์ง€ URL๋“ค์€ ๋ชจ๋‘ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž๋“ค์—๊ฒŒ๋งŒ ํ—ˆ์šฉ

    • ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž ์ฆ‰, ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๋“ค์„ ์ด์•ผ๊ธฐํ•จ

  • logout().logoutSuccessURL("/")

    • ๋กœ๊ทธ์•„์›ƒ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ ์„ค์ •์˜ ์ง„์ž…์ 

    • ๋กœ๊ทธ์•„์›ƒ ์„ฑ๊ณต ์‹œ "/"๋กœ ์ด๋™

  • oauth2Login

    • OAuth2 ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ ์„ค์ •์˜ ์ง„์ž…์ 

  • userInfoEndPoint

    • OAuth2 ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์ดํ›„ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ์˜ ์„ค์ •๋“ค์„ ๋‹ด๋‹น

  • userService

    • ์†Œ์…œ ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ ํ›„์† ์กฐ์น˜๋ฅผ ์ง„ํ–‰ํ•  UserService ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์ฒด๋ฅผ ํ†ตํ•ด์„œ

    • ๋ฆฌ์†Œ์Šค ์„œ๋ฒ„(์†Œ์…œ ์„œ๋น„์Šค)์—์„œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜จ ์ƒํƒœ์—์„œ ์ถ”๊ฐ€๋กœ ์ง„ํ–‰ํ•˜๊ณ ์ž ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๋ช…์‹œ ๊ฐ€๋Šฅ

๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ ์ดํ›„ ๊ฐ€์ ธ์˜จ ์‚ฌ์šฉ์ž์˜ ์ •๋ณด(email, name, picture ๋“ฑ)์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐ€์ž…, ๋ฐ ์ •๋ณด ์ˆ˜์ • ์„ธ์…˜ ์ €์žฅ๋“ฑ์˜ ๊ธฐ๋Šฅ์„ ์ง€์›

src/main/java/com/kyu/book/springboot/config/auth/CustomOAuth2UserService

package com.kyu.book.springboot.config.auth;

import com.kyu.book.springboot.domain.user.User;
import com.kyu.book.springboot.domain.user.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpSession;
import java.util.Collections;

@RequiredArgsConstructor
@Service
public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
    private final UserRepository userRepository;
    private final HttpSession httpSession;

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2UserService<OAuth2UserRequest, OAuth2User> delegate = new DefaultOAuth2UserService();
        OAuth2User oAuth2User = delegate.loadUser(userRequest);

        String registrationId = userRequest.getClientRegistration().getRegistrationId();
        String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName();

        OAuthAttributes attributes = OAuthAttributes.of(registrationId, userNameAttributeName, OAuth2User.getAttributes());

        User user = saveOrUpdate(attributes);
        httpSession.setAttribute("user", new SessionUser(user));

        return new DefaultOAuth2User(Collections.singleton(new SimpleGrantedAuthority(user.getRoleKey())),attributes.getAttributes(), attributes.getNameAttributesKey());
    }

    private User saveOrUpdate(OAuthAttributes attribues){
        User user = userRepository.findByEmail(attribues.getEmail()).map(entity -> entity.update(attributes.getName(), attributes.getPicture())).orElse(attributes.toEntity());
        return userRepository.save(user);
    }
}
  • registrationId

    • ํ˜„์žฌ ๋กœ๊ทธ์ธ ์ง„ํ–‰ ์ค‘์ธ ์„œ๋น„์Šค๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” ์ฝ”๋“œ

    • ์ง€๊ธˆ๊นŒ์ง€๋Š” ๊ตฌ๊ธ€๋งŒ์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•„์š”์—†์ง€๋งŒ ๋‚˜์ค‘์— ๋„ค์ด๋ฒ„ ๋กœ๊ทธ์ธ๊นŒ์ง€ ์—ฐ๋™ ์‹œ ๊ตฌ๋ถ„์„ ์œ„ํ•ด์„œํ•„์š”

  • userNameAttributeName

    • OAuth2 ๋กœ๊ทธ์ธ ์ง„ํ–‰ ์‹œ ํ‚ค๊ฐ€ ๋˜๋Š” ํ•„๋“œ ๊ฐ’์„ ์ด์•ผ๊ธฐ = Primary Key

    • ๊ตฌ๊ธ€์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ง€์›(๊ตฌ๊ธ€์ฝ”๋“œ : sub)ํ•˜์ง€๋งŒ ๋„ค์ด๋ฒ„, ์นด์นด์˜ค๋Š” ๊ธฐ๋ณธ์ง€์›์„ ํ•˜์ง€ ์•Š์Œ

    • ๋„ค์ด๋ฒ„ ๋กœ๊ทธ์ธ๊ณผ ๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ์„ ๋™์‹œ์— ์ง€์›ํ•  ๋•Œ ์‚ฌ์šฉ

  • OAuthAttributes

    • OAuth2UserService๋ฅผ ํ†ตํ•ด์„œ ๊ฐ€์ ธ์˜จ OAuth2User์˜ attribute๋ฅผ ๋‹ด์„ ํด๋ž˜์Šค

    • ๋„ค์ด๋ฒ„ ๋“ฑ ๋‹ค๋ฅธ ์†Œ์…œ ๋กœ๊ทธ์ธ๋„ ์ด ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉ

  • SessionUser

    • ์„ธ์…˜์— ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ Dto ํด๋ž˜์Šค

    • ์™œ User ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์„œ ์“ฐ๋Š”์ง€ ์ดํ›„์— ์„ค๋ช…

  • ๊ตฌ๊ธ€ ์‚ฌ์šฉ์ž ์ •๋ณด๊ฐ€ ์—…๋ฐ์ดํŠธ ๋˜์—ˆ์„ ๋•Œ๋ฅผ ๋Œ€๋น„ํ•ด์„œ update๊ธฐ๋Šฅ๋„ ๊ฐ™์ด ๊ตฌํ˜„

Dto์˜ ์—ญํ• ์„ ํ•˜๋Š” OAuthAttributes

src/main/java/com/kyu/book/springboot/config/auth/dto/OAuthAttributes

package com.kyu.book.springboot.config.auth.dto;

import com.kyu.book.springboot.domain.user.Role;
import com.kyu.book.springboot.domain.user.User;
import lombok.Builder;
import lombok.Getter;

import java.util.Map;

@Getter
public class OAuthAttributes {
    private Map<String, Object> attributes;
    private String name;
    private String nameAttributeKey;
    private String email;
    private String picture;

    @Builder
    public OAuthAttributes(Map<String, Object> attributes, String nameAttributeKey, String name, String email, String picture){
        this.attributes = attributes;
        this.nameAttributeKey = nameAttributeKey;
        this.name = name;
        this.email = email;
        this.picture = picture;
    }

    public static OAuthAttributes of(String registrationId, String userNameAttributename, Map<String, Object> attributes){
        return OAuthAttributes.builder()
                .name((String)attributes.get("name"))
                .email((String)attributes.get("email"))
                .picture((String)attributes.get("picture"))
                .attributes(attributes)
                .nameAttributeKey(userNameAttributename)
                .build();
    }

    public User toEntity(){
        return User.builder().name(name).email(email).picture(picture).role(Role.GUESR).build();
    }
}
  • of()

    • OAuth2User์—์„œ ๋ฐ˜ํ™˜ํ•˜๋Š” ์‚ฌ์šฉ์ž ์ •๋ณด๋Š” Map์ด๊ธฐ ๋–„๋ฌธ์— ๊ฐ’ ํ•˜๋‚˜ํ•˜๋‚˜๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผํ•จ

  • toEntity()

    • User์—”ํ‹ฐํ‹ฐ๋ฅผ ์ƒ์„ฑ

    • OAuthAttributes์—์„œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์‹œ์ ์€ ์ฒ˜์Œ ๊ฐ€์ž…ํ•  ๋•Œ

    • ๊ฐ€์ž…ํ•  ๋•Œ์˜ ๊ธฐ๋ณธ ๊ถŒํ•œ์„ GUEST๋กœ ์ฃผ๊ธฐ ์œ„ํ•ด์„œ role ๋นŒ๋” ๊ฐ’์—๋Š” Role.GUEST๋ฅผ ์‚ฌ์šฉ

    • OAuthAttributes ํด๋ž˜์Šค ์ƒ์„ฑ์ด ๋๋‚ฌ์œผ๋ฉด ๊ฐ™์€ ํŒจํ‚ค์ง€์—์„œ SessionUserํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑ

SessionUserํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€

src/main/java/com/kyu/book/springboot/config/auth/dto/SessionUser

package com.kyu.book.springboot.config.auth.dto;

import com.kyu.book.springboot.domain.user.User;
import lombok.Getter;

import java.io.Serializable;

@Getter
public class SessionUser implements Serializable {
    private String name;
    private String email;
    private String picture;

    public SessionUser(User user){
        this.name = user.getName();
        this.email = user.getEmail();
        this.picture = user.getPicture();
    }
}
  • ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž์˜ ์ •๋ณด๋งŒ ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— name, email, picture๋งŒ ํ•„๋“œ๋กœ ์„ ์–ธ

User ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ด์œ 

์„ธ์…˜์— ๋Œ€ํ•ด์„œ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•˜๋‹ˆ๊นŒ User ํด๋ž˜์Šค์— ์ง๋ ฌํ™”๋ฅผ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•˜๋‹ค๋Š” ์˜๋ฏธ์˜ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒ

โ†’ ์ด ์ ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด User ํด๋ž˜์Šค์— ์ง๋ ฌํ™” ์ฝ”๋“œ๋ฅผ ๋„ฃ์œผ๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?

โ†’์ข€ ๊ทธ๋ ‡๋‹ค = User ํด๋ž˜์Šค๊ฐ€ ์—”ํ‹ฐํ‹ฐ์ด๊ธฐ ๋•Œ๋ฌธ

  • ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค์—๋Š” ์–ธ์ œ ๋‹ค๋ฅธ ์—”ํ‹ฐํ‹ฐ์™€ ๊ด€๊ณ„๊ฐ€ ํ˜•์„ฑ๋ ์ง€ ๋ชจ๋ฅด๊ธฐ ๋–„๋ฌธ์— ์ข‹์ง€ ์•Š๋‹ค

    • ๋งŒ์•ฝ ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋ฉด ์ง๋ ฌํ™” ๋Œ€์ƒ์— ์ž์‹๋“ค๋„ ํฌํ•จ์ด ๋˜๊ธฐ ๋–„๋ฌธ์— ์„ฑ๋Šฅ ์ด์Šˆ, ๋ถ€์ˆ˜ ํšจ๊ณผ๊ฐ€ ๋ฐœ์ƒ

โ†’ ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋ƒฅ ์ง๋ ฌํ™” ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ์„ธ์…˜ dto๋ฅผ ํ•˜๋‚˜ ์ถ”๊ฐ€๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ๋” ์ข‹์Œ

๋กœ๊ทธ์ธ ํ…Œ์ŠคํŠธ

src/main/resources/templates/index.mustache

{{>layout/header}}
    <h1>์Šคํ”„๋ง ๋ถ€ํŠธ๋กœ ์‹œ์ž‘ํ•˜๋Š” ์›น ์„œ๋น„์Šค</h1>
    <div class="col-md-12">
        <div class="col-md-6">
            <a href="/posts/save" role="button" class="btn btn-primary">๊ธ€ ๋“ฑ๋ก</a>
            {{#userName}}
                Logged in as: <span id="user">{{userName}}</span>
                <a href="/logout" class="btn btn-info active" role="button">Logout</a>
            {{/userName}}
            {{^userName}}
                <a href="/oauth2/authorization/google" class="btn btn-success active" role="button">Google</a>
            {{/userName}}
        </div>
        <br>
       ...
  • {{#userName}}

    • ๋จธ์Šคํ…Œ์น˜๋Š” ๋‹ค๋ฅธ ์–ธ์–ด์™€ ๊ฐ™์€ if๋ฌธ์„ ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค

    • true/false ์—ฌ๋ถ€๋งŒ ํŒ๋‹จํ•จ

    • ๋จธ์Šคํ…Œ์น˜์—์„œ๋Š” ํ•ญ์ƒ ์ตœ์ข…๊ฐ’์„ ๋„˜๊ฒจ์ค˜์•ผ ํ•œ๋‹ค

    • userName์ด ์žˆ๋‹ค๋ฉด userName์„ ๋…ธ์ถœ์‹œํ‚ค๋„๋ก ๊ตฌ์„ฑ

  • a href="/logout"

    • ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์—์„œ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๋กœ๊ทธ์•„์›ƒ URL

      • ๊ฐœ๋ฐœ์ž๊ฐ€ ๋ณ„๋„๋กœ logout์— ํ•ด๋‹นํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋งŒ๋“ค ํ•„์š”๊ฐ€ ์—†์Œ

    • SecurityConfig๋ฅผ ํ†ตํ•ด์„œ url๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜๋Š” ์žˆ์ง€๋งŒ ๊ธฐ๋ณธ URL์œผ๋กœ ์ถฉ๋ถ„ํ•˜๋‹ค

  • {{^userName}}

    • ๋จธ์Šคํ…Œ์น˜์—์„œ ํ•ด๋‹น ๊ฐ’์ด ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ์—๋Š” ^์„ ์‚ฌ์šฉ

    • ์—ฌ๊ธฐ์—์„œ๋Š” userName์ด ์—†๋‹ค๋ฉด ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์„ ๋…ธ์ถœ์‹œํ‚จ๋‹ค๋Š” ์˜๋ฏธ

  • a href="/oauth2/authorization/google"

    • ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๋กœ๊ทธ์ธ URL

    • ๋กœ๊ทธ์•„์›ƒ URL๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋ณ„๋„์˜ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋งŒ๋“ค ํ•„์š”๊ฐ€ ์—†์Œ

์ด์ œ๋Š” index.mustache์—์„œ userName์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” IndexController์—์„œ userName์„ model์— ์ €์žฅํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€

src/main/java/com/kyu/book/springboot/web/dto/IndexController

@GetMapping("/")
public String index(Model model){
    model.addAttribute("posts", postsService.findAllDesc());
    SessionUser user = (SessionUser)httpSession.getAttribute("user");
    if(user != null){
        model.addAttribute("username", user.getName());
    }
    return "index";
}
  • (SessionUser)httpSession.getAttribute("user")

    • ์•ž ์„œ ์ž‘์„ฑ๋œ CustomOAuth2UserService์—์„œ ๋กœ๊ทธ์ธ ์„ฑ๊ณต์‹œ ์„ธ์…˜์— SessionUser๋ฅผ ์ €์žฅํ•˜๋„๋ก ๊ตฌ์„ฑ

    • ๋กœ๊ทธ์ธ ์„ฑ๊ณต โ†’ httpSession.getAttribute("user")๋กœ ๊ฐ’์„ ๊ฐ€์ ธ์˜ด

  • if(userโ‰ null)

    • ์„ธ์…˜์— ์ง€์ •ํ•œ ๊ฐ’์ด ์žˆ์„ ๋•Œ๋งŒ model์— userName์œผ๋กœ ๋“ฑ๋ก

    • ์„ธ์…˜์— ์ €์žฅ๋œ ๊ฐ’์ด ์—†๋‹ค๋ฉด index.mustache์—์„œ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์ด ๋ณด์ด์ง€ ์•Š๋„๋ก ํ•ด๋‘ 

์ด๋ ‡๊ฒŒ๊นŒ์ง€ ํ•˜๊ณ 

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰์‹œํ‚ค๊ณ  โ†’ login์„ ํ•ด๋ณด์ž โ†’ ๊ตฌ๊ธ€๋กœ๊ทธ์ธapi๋กœ ๋„˜์–ด๊ฐ€๊ณ  โ†’ ๋กœ๊ทธ์ธํ•˜๊ฒŒ๋˜๋ฉด โ†’ h2-console๋กœ ์ ‘๊ทผํ•ด์„œ user table์— ํšŒ์›๊ฐ€์ž…๋˜์—ˆ๊ณ  โ†’ ์ •์ƒ์ ์œผ๋กœ ์ด๋ฆ„์„ ๊ฐ€์ ธ์™€์„œ ์ด๋ฆ„์„ ํ‘œ์‹œํ•ด์ค€๋‹ค

ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์—์„œ ๊ธ€์“ฐ๋Š”๊ฑด ์•ˆ๋˜๋Š”๋ฐ(json์—์„œ ์—๋Ÿฌ๊ฐ€ 403๊ฐ€ ๋‚˜์˜ค๊ฒŒ ๋จ) โ†’ ์ด๊ฑด ๊ถŒํ•œ์„ user๋กœ ๋งž์ถฐ๋‘์—ˆ๊ธฐ ๋–„๋ฌธ์ด๋‹ค โ†’ h2-console๋กœ ์ ‘๊ทผํ•ด์„œ

update user set role='USER';

๋กœ ๊ถŒํ•œ์„ USER๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ๊ณ  โ†’ ๋‹ค์‹œ ๋กœ๊ทธ์ธํ•˜๊ณ  ๊ธ€์“ฐ๊ธฐํ•ด๋ณด๋ฉด ์ •์ƒ์ ์œผ๋กœ ๊ฐ€๋Šฅ!

Last updated

Was this helpful?