DevKim

[์Šคํ”„๋ง with AWS] ๊ตฌ๊ธ€,๋„ค์ด๋ฒ„ ๋กœ๊ทธ์ธ ์—ฐ๋™ ๋ณธ๋ฌธ

Spring Boot

[์Šคํ”„๋ง with AWS] ๊ตฌ๊ธ€,๋„ค์ด๋ฒ„ ๋กœ๊ทธ์ธ ์—ฐ๋™

on_doing 2021. 7. 12. 12:08
728x90

๐ŸƒCH.05 ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์™€ OAuth 2.0์œผ๋กœ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ ๊ตฌํ˜„๐Ÿƒ


* TODO : ๋ฆฌํŒฉํ† ๋ง *

IndexController์—์„œ ์„ธ์…˜๊ฐ’์„ ๊ฐ€์ ธ์˜ค๋Š” ๋ถ€๋ถ„, ์ฆ‰ (SessionUser) httpSession.getAttribute ~ ๋ถ€๋ถ„์€

๋‹ค๋ฅธ ์ปจํŠธ๋กค๋Ÿฌ๋‚˜ ๋ฉ”์†Œ๋“œ์—์„œ ์„ธ์…˜๊ฐ’์ด ํ•„์š”ํ•  ๋•Œ๋งˆ๋‹ค ๋ฐ˜๋ณต๋˜๋Š” ์ฝ”๋“œ๋กœ, ๊ฐœ์„ ์ด ํ•„์š”ํ•œ ๋‚˜์œ ์ฝ”๋“œ์ด๋‹ค.

์ด๋ฅผ ๋ฉ”์†Œ๋“œ ์ธ์ž๋กœ ์„ธ์…˜๊ฐ’์„ ๋ฐ”๋กœ ๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ๋ณ€๊ฒฝํ•˜๋Š” ๋ถ€๋ถ„์€ ์ง€๊ธˆ ๋‹จ๊ณ„์—์„œ ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ค์šด ๋ถ€๋ถ„์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‚˜์ค‘์— todo๋กœ ๋‚จ๊ฒจ๋‘๊ฒ ๋‹ค.


 

https://console.cloud.google.com/

 

Google Cloud Platform

ํ•˜๋‚˜์˜ ๊ณ„์ •์œผ๋กœ ๋ชจ๋“  Google ์„œ๋น„์Šค๋ฅผ Google Cloud Platform์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋กœ๊ทธ์ธํ•˜์„ธ์š”.

accounts.google.com

OAuth ํด๋ผ์ด์–ธํŠธ ID๋ฅผ ์ƒ์„ฑํ•ด์ฃผ๊ณ , ๊ทธ์— ๋”ฐ๋ฅธ ID์™€ ClientSecret ์ฝ”๋“œ๋ฅผ ๋ฐ›์•„๋‘์–ด์•ผํ•œ๋‹ค.


1. application.properties์™€ ๊ฐ™์€ ์œ„์น˜์— application-oauth-properties ์ƒ์„ฑ

 

* ์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ๋Š” properties์ด๋ฆ„์„ application-XXX.properties๋กœ ๋งŒ๋“ค๋ฉด,

XXX์ด๋ผ๋Š” profile์ด ์ƒ์„ฑ๋˜์–ด ์ด๋ฅผ ํ†ตํ•ด ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค.

spring.security.oauth2.client.registration.google.client-id=ํด๋ผ์ด์–ธํŠธ ID
spring.security.oauth2.client.registration.google.client-secret=ํด๋ผ์ด์–ธํŠธ ๋ณด์•ˆ ๋น„๋ฐ€
spring.security.oauth2.client.registration.scope=profile,email

scope์˜ ๊ธฐ๋ณธ๊ฐ’์€ openid,profile,email์ด๋‚˜, openid๋ผ๋Š” scope์ด ์žˆ์œผ๋ฉด ๊ตฌ๊ธ€๊ณผ ๊ตฌ๊ธ€์ด ์•„๋‹Œ ์„œ๋น„์Šค(๋„ค์ด๋ฒ„)๋กœ ๋‚˜๋ˆ ์„œ ๊ฐ๊ฐ์˜ OAuth2Service๋ฅผ ๋งŒ๋“ค์–ด์•ผํ•˜๋ฏ€๋กœ, ํ•˜๋‚˜๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด openid scope์„ ๋นผ๊ณ  ๋“ฑ๋กํ•œ๋‹ค.

 

 

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

spring.profiles.include=oauth

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

@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;
    }
}

 

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

 

* ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์—์„œ๋Š” ๊ถŒํ•œ ์ฝ”๋“œ์— ํ•ญ์ƒ ROLE_์ด ์•ž์— ์žˆ์–ด์•ผ๋งŒ ํ•œ๋‹ค.

@Getter
@RequiredArgsConstructor
public enum Role {

    GUEST("ROLE_GUEST","์†๋‹˜"),
    USER("ROLE_USER","์ผ๋ฐ˜ ์‚ฌ์šฉ์ž");

    private final String key;
    private final String title;
}

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

 

๐Ÿ”— @EnableWebSecurity

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

 

๐Ÿ”— authorizeRequests

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

 

๐Ÿ”— antMatchers

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

 

๐Ÿ”— anyRequest

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

 

๐Ÿ”— userInfoEndpoint

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

 

๐Ÿ”— userService

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

@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    private final CustomOAuth2UserService customOAuth2UserService;
    
    @Override
    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);
    }
}

 

๐Ÿ“Œ CustomOAuth2UserService ํด๋ž˜์Šค ์ƒ์„ฑ

 

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

@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 delegate = new DefaultOAuth2UserService();
        OAuth2User oAuth2User = delegate.loadUser(userRequest);

       //ํ˜„์žฌ ์ง„ํ–‰ ์ค‘์ธ ์„œ๋น„์Šค ๊ตฌ๋ถ„
        String registrationId = userRequest.getClientRegistration().getRegistrationId();
        
        //OAuth2 ๋กœ๊ทธ์ธ ์ง„ํ–‰์‹œ ํ‚ค๊ฐ€๋˜๋Š” ํ•„๋“œ๊ฐ’(=PK)
        String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails()
                .getUserInfoEndpoint().getUserNameAttributeName();
		
        //OAtuth2User์˜ ์†์„ฑ๋“ค์„ ๋‹ด์„ ํด๋ž˜์Šค(=dto)
        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.getNameAttributeKey());
    }


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

        return userRepository.save(user);
    }
}

๐Ÿ”— registrationId

: ํ˜„์žฌ ๋กœ๊ทธ์ธ ์ง„ํ–‰ ์ค‘์ธ ์„œ๋น„์Šค ๊ตฌ๋ถ„ (๊ตฌ๊ธ€,๋„ค์ด๋ฒ„..)

 

๐Ÿ”— userNameAttributeName

: OAuth2 ๋กœ๊ทธ์ธ ์ง„ํ–‰์‹œ ํ‚ค๊ฐ€๋˜๋Š” ํ•„๋“œ๊ฐ’(PK์™€ ์œ ์‚ฌ)

 

๐Ÿ”— OAuthAttributes

: OAuth2User์˜ ์†์„ฑ๋“ค์„ ๋‹ด์„ ํด๋ž˜์Šค(=dto)

 

๐Ÿ”— SessionUser

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

-> User ํด๋ž˜์Šค๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ด์œ ๋Š”, User๋Š” ์—”ํ‹ฐํ‹ฐ์ด๊ธฐ ๋•Œ๋ฌธ์— ์–ด๋–ค ๊ฒƒ๊ณผ ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ์„ฑ๋ฆฝ๋˜์–ด์žˆ์„ ์ง€ ๋ชจ๋ฅด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ( ์„ฑ๋Šฅ ์ด์Šˆ,๋ถ€์ˆ˜ ํšจ๊ณผ ๋ฐœ์ƒ ๋ฐฉ์ง€)

 

 

* ์ฐธ๊ณ ๋กœ SessionUser์—๋Š” ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž ์ •๋ณด๋งŒ ํ•„์š”ํ•˜๋ฏ€๋กœ, name/email/picture๋งŒ ํ•„๋“œ๋กœ ์„ ์–ธ *


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

<div class="row">
            <div class="col-md-6">
                <a href="/posts/save" role="button" class="btn btn-primary">๊ธ€ ๋“ฑ๋ก</a>
                {{#MyUser}}
                    Logged in as: <span id="user">{{MyUser}}</span>
                    <a href="/logout" class="btn btn-info active" role="button">Logout</a>
                {{/MyUser}}
                {{^MyUser}}
                    <a href="/oauth2/authorization/google" class="btn btn-success active" role="button">Google Login</a>
                {{/MyUser}}
            </div>
        </div>
        <br>

๐Ÿ”— {{^username}}

: username์ด ์—†๋‹ค๋ฉด, ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์—์„œ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๋กœ๊ทธ์ธ url๋กœ ์ด๋™

 

 

๐Ÿ“Œ Controller์— ์„ธ์…˜ user name๊ฐ’ ์ถ”๊ฐ€

private final HttpSession httpSession;

    @GetMapping("/")
    public String index(Model model)
    {
        List<Posts> postsList = postsRepository.findAllDesc();
        model.addAttribute("posts",postsList);

        //SessionUser์— ์ €์žฅํ•œ ๋กœ๊ทธ์ธ ์ •๋ณด
        SessionUser user = (SessionUser) httpSession.getAttribute("user");

        if(user!=null)
        {
             model.addAttribute("MyUser",user.getName());
        }

        return "index";
    }

 

*์ด๋•Œ ์ฃผ์˜ํ•  ์  *

window ์‚ฌ์šฉ์ž๋Š” userName ์ด๋ผ๋Š” ๋ณ€์ˆ˜๊ฐ€ ์‹œ์Šคํ…œ ๋ณ€์ˆ˜ ์†์—์„œ ์ด๋ฏธ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ,

userName์ด๋ผ๋Š” ๊ฐ’์œผ๋กœ ๋ฐ›์œผ๋ฉด ๋‚ด ์ปดํ“จํ„ฐ ์‚ฌ์šฉ์ž์˜ ์ด๋ฆ„์ด ๋ฐ›์•„์˜ค๊ฒŒ ๋œ๋‹ค.

userName๋ง๊ณ  ๋‹ค๋ฅธ ๋ณ€์ˆ˜(์—ฌ๊ธฐ์„œ๋Š” MyUser) ๋กœ ๊ฐ’์„ ๋„˜๊ฒจ์ฃผ์ž


๐Ÿ“Œ ๋™์ž‘ ํ™•์ธ

โ–ผ

๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ

โ–ผ


๋„ค์ด๋ฒ„ ๋กœ๊ทธ์ธ

 

728x90
Comments