BackEnd๐Ÿงต

[Spring Security] ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ๊ธฐ๋ณธ - 1

hae02y 2023. 11. 7. 18:34
๋ฐ˜์‘ํ˜•

๋“ค์–ด๊ฐ€๊ธฐ์ „์—

์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ ๋ฉค๋ฒ„ ๋ถ€๋ถ„์„ ๊ตฌํ˜„ํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. ๊ทธ์ „์— ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์— ๋Œ€ํ•ด์„œ ์ž์„ธํ•˜๊ฒŒ ํ•™์Šตํ•˜๊ณ  ๊ฐ€๋ณด์ž!

 

Spring Security๋ž€?

 

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š์•˜์„์‹œ์— ๋ฌธ์ œ์ ์„ ๋จผ์ € ์•Œ์•„๋ณด์ž. 

 

1. ๋กœ๊ทธ์ธ๊ธฐ๋Šฅ์— Authentication(์ธ์ฆ) ์ด ์—†์Œ.

2. API์— ๋Œ€ํ•œ ๊ถŒํ•œ ๋ถ€์—ฌ Authirization(์ธ๊ฐ€) ๊ธฐ๋Šฅ์ด ์—†์Œ.

3. ์›น ๋ณด์•ˆ ์ทจ์•ฝ์ ์— ๋Œ€ํ•œ ๋Œ€๋น„(CSRF, ํด๋ฆญ์ œํ‚น ๋“ฑ)๊ฐ€ ์—†์Œ.

 

๊ทธ๋Ÿผ ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋ฅผ ์ ์šฉํ•˜๋ฉด ์–ด๋–ค์ ์ด ๋‹ฌ๋ผ์งˆ๊นŒ?

  • ๋‹ค์–‘ํ•œ ์œ ํ˜•(ํผ ๋กœ๊ทธ์ธ ์ธ์ฆ, ํ† ํฐ ๊ธฐ๋ฐ˜ ์ธ์ฆ, OAuth 2 ๊ธฐ๋ฐ˜ ์ธ์ฆ, LDAP ์ธ์ฆ)์˜ ์‚ฌ์šฉ์ž ์ธ์ฆ ๊ธฐ๋Šฅ ์ ์šฉ
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‚ฌ์šฉ์ž์˜ ์—ญํ• (Role)์— ๋”ฐ๋ฅธ ๊ถŒํ•œ ๋ ˆ๋ฒจ ์ ์šฉ
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ์ ‘๊ทผ ์ œ์–ด
  • ๋ฏผ๊ฐํ•œ ์ •๋ณด์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™”
  • SSL ์ ์šฉ
  • ์ผ๋ฐ˜์ ์œผ๋กœ ์•Œ๋ ค์ง„ ์›น ๋ณด์•ˆ ๊ณต๊ฒฉ ์ฐจ๋‹จ
  • SSO, ํด๋ผ์ด์–ธํŠธ ์ธ์ฆ๊ธฐ๋ฐ˜์ธ์ฆ, ๋ฉ”์„œ๋“œ๋ณด์•ˆ, ์ ‘๊ทผ ์ œ์–ด ๋ชฉ๋ก ๋“ฑ

๋‹ค์‹œ๋งํ•ด์„œ ์Šคํ”„๋ง์‹œํ๋ฆฌํ‹ฐ๋Š” ์ธ์ฆ, ๊ถŒํ•œ๋ถ€์—ฌ ๋ฐ ๋ณดํ˜ธ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ํ”„๋ ˆ์ž„ ์›Œํฌ์ด๋‹ค. ๊ฐœ๋ฐœ์„ ํ• ๋•Œ ๋ณด์•ˆ๋ถ„์•ผ๋Š” ์ •๋ง ์ค‘์š”ํ•˜์ง€๋งŒ ๋งŽ์€ ์‹œ๊ฐ„์ด ์†Œ์š”๋œ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ๋งŒ๋“ค์–ด์ง„ ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋ฅผ ํ†ตํ•ด ์ธ์ฆ, ๊ถŒํ•œ ๊ถŒํ•œํ™•์ธ ๋“ฑ์— ํ•„์š”ํ•œ ๊ธฐ๋Šฅ๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.  

 

 

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ์ด์™ธ์˜ Java ๋ณด์•ˆ ์†”๋ฃจ์…˜๋„ ์กด์žฌํ•œ๋‹ค.

- Apache Shiro

- OACC

 

๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ ๊ธฐ๋ณธ(InMemory ์‚ฌ์šฉ)

์ง€๊ธˆ๋ถ€ํ„ฐ ์•Œ์•„๋ณผ ๋ถ€๋ถ„์€ DB๊ฐ€ ์—†์ด InMemory๋กœ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.Inmemory ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ํ™•์ธํ•˜๊ณ  DB๋ฅผ ์—ฐ๋™ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์ž.

 

๊ธฐ๋ณธ ์„ค์ •

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

@Configuration //์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ์ง€์ •
public class SecurityConfiguration {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable()                 // (1) csrf ๊ณต๊ฒฉ ๋น„ํ™œ์„ฑ
            .formLogin()                      // (2) ๋กœ๊ทธ์ธ ๋ฐฉ์‹ ์ง€์ •
            .loginPage("/auths/login-form")   // (3) ์ง€์ •ํ•ด๋‘” ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ์‚ฌ์šฉ
            .loginProcessingUrl("/process_login")    // (4) ๋กœ๊ทธ์ธ์š”์ฒญ ์ˆ˜ํ–‰ url ์ง€์ •
            .failureUrl("/auths/login-form?error")   // (5) ์‹คํŒจ์‹œ์— redirect ํ• ๊ณณ
            .and()                                   // (6) ๋ฉ”์„œ๋“œ์ฒด์ธํ˜•ํƒœ๋กœ ์‚ฌ์šฉ
            .authorizeHttpRequests()                     // (7) ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ ์ ‘๊ทผ ๊ถŒํ•œ ํ™•์ธ
            .anyRequest()                            // (8) ๋ชจ๋“  ์š”์ฒญ์„ ์ ‘๊ทผ ํ—ˆ์šฉ
            .permitAll();                            // (9) ํ—ˆ์šฉ

        return http.build();
    }

/*
InMemoryUserDetailsManager๋ฅผ ์ด์šฉํ•ด 
๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๋™ ์—†์ด ํ…Œ์ŠคํŠธ ๋ชฉ์ ์˜ 
InMemory User๋ฅผ ์ƒ์„ฑ
*/
    @Bean
    public InMemoryUserDetailsManager userDetailsService() { 
        UserDetails user =
                User.withDefaultPasswordEncoder()
                        .username("kevin@gmail.com") //๊ณ„์ • username 
                        .password("1111") //๊ณ„์ •๋น„๋ฐ€๋ฒˆํ˜ธ
                        .roles("USER") //๊ณ„์ • ๊ถŒํ•œ
                        .build();
        return new InMemoryUserDetailsManager(user);
    }
}

 

URI ์ ‘๊ทผ ๊ถŒํ•œ ๋ถ€์—ฌ

 @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .formLogin()
            .loginPage("/auths/login-form")
            .loginProcessingUrl("/process_login")
            .failureUrl("/auths/login-form?error")
            .and()
            .exceptionHandling().accessDeniedPage("/auths/access-denied")   // (1)
            .and()
            .authorizeHttpRequests(authorize -> authorize                  // (2)
                    .antMatchers("/orders/**").hasRole("ADMIN")        // (2-1)
                    .antMatchers("/members/my-page").hasRole("USER")   // (2-2)
                    .antMatchers("⁄**").permitAll()                    // (2-3)
            );
        return http.build();
    }

 

(1)์„ ํ†ตํ•ด ์ ‘๊ทผ๊ถŒํ•œ์ด ์—†๋Š” ๊ฒฝ์šฐ exception์ด ๋ฐœ์ƒํ•˜๊ณ  ์ด๊ฒƒ์„ ์ฒ˜๋ฆฌํ•  page๋ฅผ ์ง€์ •ํ•œ๋‹ค.

(2)๋ฅผ ํ†ตํ•ด ๋žŒ๋‹ค ํ‘œํ˜„์‹์œผ๋กœ request URI์— ์ ‘๊ทผ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•œ๋‹ค.

(2-1) .antMatchers("/orders/**").hasRole("ADMIN")์€ ADMIN Role์„ ๋ถ€์—ฌ๋ฐ›์€ ์‚ฌ์šฉ์ž๋งŒ /orders๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ชจ๋“  URL์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค.

 

/orders/์—์„œ `๋Š” /orders๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ชจ๋“  ํ•˜์œ„ URL์„ ํฌํ•จํ•œ๋‹ค.
์˜ˆ๋ฅผ ๋“ค์–ด/orders/1,/orders/1/coffees,/orders/1/coffees/1` ๊ฐ™์€ ๋ชจ๋“  ํ•˜์œ„ URL์„ ํฌํ•จํ•œ๋‹ค. ๋งŒ์•ฝ /orders/*๋ผ๋Š” URL์„ ์ง€์ •ํ–ˆ๋‹ค๋ฉด /orders/1๊ณผ ๊ฐ™์ด /orders์˜ ํ•˜์œ„ URL์˜ depth๊ฐ€ 1์ธ URL๋งŒ ํฌํ•จํ•œ๋‹ค.


(2-2)์˜ antMatchers("/members/my-page").hasRole("USER")์€ USER Role์„ ๋ถ€์—ฌ๋ฐ›์€ ์‚ฌ์šฉ์ž๋งŒ /members/my-page URL์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Œ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
(2-3)์˜ .antMatchers("/**").permitAll()์€ ์•ž์—์„œ ์ง€์ •ํ•œ URL ์ด์™ธ์˜ ๋‚˜๋จธ์ง€ ๋ชจ๋“  URL์€ Role์— ์ƒ๊ด€์—†์ด ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•จ์„ ์˜๋ฏธํ•œ๋‹ค.

 

antMatcher() ๋ฅผ ํ†ตํ•ด ์ž‘์„ฑ๋ ๋•Œ ๊ตฌ์ฒด์ ์ธ URL ๊ฒฝ๋กœ๋ถ€ํ„ฐ ์ ‘๊ทผ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜๊ณ , ๋œ๊ตฌ์ฒด์ ์ธ URL ๊ฒฝ๋กœ์— ์ ‘๊ทผ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•ด์•ผํ•œ๋‹ค.

 

๋กœ๊ทธ์•„์›ƒ ์„ค์ •

 @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .formLogin()
            .loginPage("/auths/login-form")
            .loginProcessingUrl("/process_login")
            .failureUrl("/auths/login-form?error")
            .and()
            .logout()                        // (1) ๋กœ๊ทธ์•„์›ƒ ์„ค์ •์„ ํ•จ
            .logoutUrl("/logout")            // (2) ๋กœ๊ทธ์•„์›ƒ url์„ ์ง€์ •ํ•œ๋‹ค.
            .logoutSuccessUrl("/")  // (3) ๋กœ๊ทธ์•„์›ƒ ์„ฑ๊ณต์‹œ์— redirect url
            .and()
            .exceptionHandling().accessDeniedPage("/auths/access-denied")
            .and()
            .authorizeHttpRequests(authorize -> authorize
                    .antMatchers("/orders/**").hasRole("ADMIN")
                    .antMatchers("/members/my-page").hasRole("USER")
                    .antMatchers("⁄**").permitAll()
            );
        return http.build();
    }

 

ํšŒ์›๊ฐ€์ž… ์„ค์ •

ํšŒ์›๊ฐ€์ž…์„ ํผ์„ ํ†ตํ•ด ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์ž‘์—…์ด ํ•„์š”ํ•˜๋‹ค.

  • PasswordEncoder Bean ๋“ฑ๋ก
  • MemberService Bean ๋“ฑ๋ก์„ ์œ„ํ•œ JavaConfiguration ๊ตฌ์„ฑ
  • MemberService ํด๋ž˜์Šค ๊ตฌํ˜„

 

1. PasswordEncoder Bean ๋“ฑ๋ก

 

PasswordEncoder ๋Š” ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์—์„œ ์ œ๊ณตํ•˜๋Š” ํŒจ์Šค์›Œ๋“œ ์•”ํ˜ธํ™” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์ด๋‹ค. ํšŒ์›๊ฐ€์ž…ํผ์„ ํ†ตํ•ด์„œ ์ž…๋ ฅ๋˜๋Š” ๋ฐ์ดํ„ฐ๋Š” ์•”ํ˜ธํ™”๋˜์ง€์•Š์€ Plain Text ์ด๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ์ด๋ฅผ DB์— ๊ธฐ๋กํ•˜๊ธฐ์ „์— ์•”ํ˜ธํ™”๋ฅผ ํ•ด์•ผํ•œ๋‹ค. ๋‹ค์–‘ํ•œ ์•”ํ˜ธํ™” ๋ฐฉ์‹์„ ์ œ๊ณตํ•˜์ง€๋งŒ default๋Š” bycrypt ๋ฐฉ์‹์ด๋‹ค.

@Configuration
public class SecurityConfiguration {
    ...
    ...

    @Bean
    public UserDetailsManager userDetailsService() {
        UserDetails user =
                User.withDefaultPasswordEncoder()
                        .username("kevin@gmail.com")
                        .password("1111")
                        .roles("USER")
                        .build();

        UserDetails admin =
                User.withDefaultPasswordEncoder()
                        .username("admin@gmail.com")
                        .password("2222")
                        .roles("ADMIN")
                        .build();

        return new InMemoryUserDetailsManager(user, admin);
    }

    // (1)
    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();  // (1-1)
    }
}

(1-1)์„ ๋ณด๋ฉด PasswordEncoder๋ฅผ Bean์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  createDelegatingPasswordEncoder()๋ฅผ ํ†ตํ—ค์„œ DelegatingPasswordEncoder๋ฅผ ๋จผ์ € ์ƒ์„ฑํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๋Ÿฌํ•œ DelegatingPasswordEncoder๊ฐ€ ์‹ค์งˆ์ ์œผ๋กœ PasswordEncoder ๊ตฌํ˜„์ฒด๋ฅผ ์ƒ์„ฑํ•ด ์ค€๋‹ค. 

 

2.MemberService Bean ๋“ฑ๋ก์„ ์œ„ํ•œ JavaConfiguration ๊ตฌ์„ฑ

 

@Configuration
public class JavaConfiguration {
    // (1)
    @Bean
    public MemberService inMemoryMemberService(UserDetailsManager userDetailsManager, 
                                               PasswordEncoder passwordEncoder) {
        return new InMemoryMemberService(userDetailsManager, passwordEncoder);
    }
}

(1)์—์„œ Bean์œผ๋กœ ๋“ฑ๋ก์„ ํ•œ๋‹ค. User ๋“ฑ๋ก์‹œ์— ํŒจ์Šค์›Œ๋“œ๋ฅผ ์•”ํ˜ธํ™” ํ•œ ํ›„์— ๋“ฑ๋กํ•ด์•ผ๋˜๋ฏ€๋กœ, PasswordEncoder ๊ฐ์ฒด๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ์ด๋Ÿฌํ•œ ๋‘๊ฐ€์ง€ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ์‹œ์— DI ํ•ด์ค€๋‹ค.

 

3. MemberService ๊ตฌํ˜„

public class InMemoryMemberService implements MemberService {  // (1)
    private final UserDetailsManager userDetailsManager;
    private final PasswordEncoder passwordEncoder;

    // (2) UserDetails, PasswordEncoder๋ฅผ DI ๋ฐ›๋Š”๋‹ค.
    public InMemoryMemberService(UserDetailsManager userDetailsManager, PasswordEncoder passwordEncoder) {
        this.userDetailsManager = userDetailsManager;
        this.passwordEncoder = passwordEncoder;
    }

    public Member createMember(Member member) {
        // (3) User์˜ ๊ถŒํ•œ์„ ์ง€์ •ํ•œ๋‹ค.
        List<GrantedAuthority> authorities = createAuthorities(Member.MemberRole.ROLE_USER.name());

        // (4) PasswordEncoder๋ฅผ ์ด์šฉํ•ด ๋“ฑ๋กํ•  User์˜ ํŒจ์Šค์›Œ๋“œ๋ฅผ ์•”ํ˜ธํ™”ํ•œ๋‹ค.
        String encryptedPassword = passwordEncoder.encode(member.getPassword());

        // (5) User๋กœ ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•ด UserDetails๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
        UserDetails userDetails = new User(member.getEmail(), encryptedPassword, authorities);

        // (6) UserDetailsManager์˜ create ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉ, ์œ ์ €๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
        userDetailsManager.createUser(userDetails);

        return member;
    }

    private List<GrantedAuthority> createAuthorities(String... roles) {
        // (3-1)
        return Arrays.stream(roles)
                .map(role -> new SimpleGrantedAuthority(role))
                .collect(Collectors.toList());
    }
}

 

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

User ์˜ ์ธ์ฆ์ •๋ณด๋ฅผ ํ…Œ์ด๋ธ”์— ์ €์žฅํ•˜๊ณ , ํ…Œ์ด๋ธ”์— ์ €์žฅ๋œ ์ธ์ฆ์ •๋ณด๋ฅผ ์ด์šฉํ•ด์„œ ์ธ์ฆ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ง„ํ–‰ํ•  ์ˆ˜์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž. ๊ทธ์ค‘ ํ•œ๊ฐ€์ง€๊ฐ€ Custom UserDetailService๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

 

์‹œํ๋ฆฌํ‹ฐ์—์„œ ์ธ์ฆ์„ ์‹œ๋„ํ•˜๋Š” ์ฃผ์ฒด๋ฅผ User(Principal)์ด๋ผ๊ณ  ํ•œ๋‹ค. Principal์€ User์˜ ๊ตฌ์ฒด์ ์ธ ์ •๋ณด๋ฅผ ์˜๋ฏธํ•˜๊ณ , ์ผ๋ฐ˜์ ์œผ๋กœ ์‹œํ๋ฆฌํ‹ฐ์—์„œ์˜ Username์„ ์˜๋ฏธํ•œ๋‹ค.  

 

1. SecurityConfiguration ์˜ ์„ค์ • ๋ณ€๊ฒฝ ๋ฐ ์ถ”๊ฐ€

@Configuration
public class SecurityConfiguration {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .headers().frameOptions().sameOrigin() // (1) //์›น์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ์„ค์ •
            .and()
            .csrf().disable()
            .formLogin()
            .loginPage("/auths/login-form")
            .loginProcessingUrl("/process_login")
            .failureUrl("/auths/login-form?error")
            .and()
            .logout()
            .logoutUrl("/logout")
            .logoutSuccessUrl("/")
            .and()
            .exceptionHandling().accessDeniedPage("/auths/access-denied")
            .and()
            .authorizeHttpRequests(authorize -> authorize
                    .antMatchers("/orders/**").hasRole("ADMIN")
                    .antMatchers("/members/my-page").hasRole("USER")
                    .antMatchers("⁄**").permitAll()
            );
        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
}

(1)์˜ .frameOptions().sameOrigin()์„ ํ˜ธ์ถœํ•˜๊ฒŒ ๋˜๋ฉด, ๋™์ผ ์ถœ์ฒ˜๋กœ ๋ถ€ํ„ฐ ๋“ค์–ด์˜ค๋Š” request๋งŒ ํŽ˜์ด์ง€ ๋ Œ๋”๋ง์„ ํ—ˆ์šฉํ•œ๋‹ค. 

 

 

2. Java Configuration์˜ Bean ๋“ฑ๋ก ๋ณ€๊ฒฝ

@Configuration
public class JavaConfiguration {
    // (1)
    @Bean
    public MemberService dbMemberService(MemberRepository memberRepository,
                                         PasswordEncoder passwordEncoder) {
        return new DBMemberService(memberRepository, passwordEncoder); (1-1)
    }
}

(1-1) ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์— User์˜ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด MemberRepository์™€ PasswordEncoder ๊ฐ์ฒด๋ฅผ DIํ•˜๋Š” dbMemberService ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.

 

3. DB MemberService ๊ตฌํ˜„

@Transactional
public class DBMemberService implements MemberService {
    private final MemberRepository memberRepository;
    private final PasswordEncoder passwordEncoder;

    // (1) ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด Bean๊ฐ์ฒด๋ฅผ DI ๋ฐ›์Œ.
    public DBMemberService(MemberRepository memberRepository,
                             PasswordEncoder passwordEncoder) {
        this.memberRepository = memberRepository;
        this.passwordEncoder = passwordEncoder;
    }

    public Member createMember(Member member) {
        verifyExistsEmail(member.getEmail());
        String encryptedPassword = passwordEncoder.encode(member.getPassword());  // (2) ํŒจ์Šค์›Œ๋“œ ์•”ํ˜ธํ™”
        member.setPassword(encryptedPassword);    // (3) ์•”ํ˜ธํ™”๋œ ํŒจ์Šค์›Œ๋“œ๋ฅผ password ํ•„๋“œ์— ํ• ๋‹น

        Member savedMember = memberRepository.save(member);

        System.out.println("# Create Member in DB");
        return savedMember;
    }
    
    ...
    ...
}

(2), (3)ํŒจ์Šค์›Œ๋“œ๋Š” ์•”ํ˜ธํ™”๋œ ์ƒํƒœ์—์„œ ๋ณตํ˜ธํ™”ํ•  ํ•„์š”๊ฐ€ ์—†์œผ๋ฏ€๋กœ ๋‹จ๋ฐฉํ–ฅ ์•”ํ˜ธํ™”๋ฐฉ์‹์œผ๋กœ ์ ์šฉ๋˜์•ผ ํ•œ๋‹ค!

 

 

4. Custom UserDetailService ๊ตฌํ˜„

@Component
public class HelloUserDetailsServiceV1 implements UserDetailsService {   // (1)
    private final MemberRepository memberRepository;
    private final HelloAuthorityUtils authorityUtils;

    // (2)
    public HelloUserDetailsServiceV1(MemberRepository memberRepository, HelloAuthorityUtils authorityUtils) {
        this.memberRepository = memberRepository;
        this.authorityUtils = authorityUtils;
    }

    // (3)loadUserByUsername ์ถ”์ƒ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Optional<Member> optionalMember = memberRepository.findByEmail(username);
        Member findMember = optionalMember.orElseThrow(() -> new BusinessLogicException(ExceptionCode.MEMBER_NOT_FOUND));

        // (4)์ด๋ฉ”์ผ ์ •๋ณด๋ฅผ ์ด์šฉํ•ด Role๊ธฐ๋ฐ˜ ๊ถŒํ•œ์ •๋ณด ์ปฌ๋ ‰์…˜ ์ƒ์„ฑ
        Collection<? extends GrantedAuthority> authorities = authorityUtils.createAuthorities(findMember.getEmail());

        // (5)User๊ฐ์ฒด๋ฅผ ๋ฆฌํ„ดํ•˜๋ฉด Security๊ฐ€ ๋ฆฌํ„ด๋œ ์ •๋ณด๋กœ ์ธ์ฆ์ ˆ์ฐจ๋ฅผ ์ˆ˜ํ–‰
        return new User(findMember.getEmail(), findMember.getPassword(), authorities);
    }
}

์ผ๋‹จ ๊ณต๋ถ€๋ฅผ ์œ„ํ•ด์„œ V1 ์œผ๋กœ ์ž‘์„ฑ์„ ํ•˜์˜€๋‹ค. UserDetails์˜ ๊ตฌํ˜„ํด๋ž˜์Šค์ธ User๊ฐ์ฒด๋ฅผ (5)์—์„œ ์ง์ ‘์ ์œผ๋กœ ์ƒ์„ฑํ•ด์„œ ๋ฆฌํ„ดํ•˜๊ณ ์žˆ๋Š”๋ฐ, ์ด๋ถ€๋ถ„์„ ์•„๋ž˜์—์„œ ๊ณ ์ณ๋ณผ๊ฒƒ์ด๋‹ค.

 

User DetailsService?
spring security์—์„œ ์ œ๊ณตํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ ์ค‘ ํ•˜๋‚˜๋กœ, User ์ •๋ณด๋ฅผ ๋กœ๋“œํ•˜๋Š” ํ•ต์‹ฌ ์ธํ„ฐํŽ˜์ด์Šค์ด๋‹ค. load ๋Š” ์ธ์ฆ์— ํ•„์š”ํ•œ User ์ •๋ณด๋ฅผ DB๋‚˜ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ๊ฐ€์ ธ์˜จ๋‹ค. ๋Š” ์˜๋ฏธ์ด๋‹ค. 

 

UserDetails?
UserDetails ๋Š” UserDetailsService์— ์˜ํ•ด ๋กœ๋“œ ๋˜์–ด ์ธ์ฆ์„ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ํ•ต์‹ฌ User ์ •๋ณด๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ด๋‹ค. UserDetails ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์ฒด๋Š” Spring Security์—์„œ ๋ณด์•ˆ ์ •๋ณด ์ œ๊ณต์„ ๋ชฉ์ ์œผ๋กœ ์ง์ ‘ ์ œ๊ณต๋˜์ง€ ์•Š๊ณ , Authentication ๊ฐ์ฒด๋กœ ์บก์Šํ™” ๋˜์–ด ์ œ๊ณต๋œ๋‹ค.

 

5. HelloAuthorityUtils ์ž‘์„ฑ

@Component
public class HelloAuthorityUtils {
    @Value("${mail.address.admin}") // (1) application.yml์—์„œ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ฐ€์ ธ์˜ค๊ธฐ
    private String adminMailAddress; //๊ด€๋ฆฌ์ž๊ถŒํ•œ ์ด๋ฉ”์ผ์„ ๊ฐ€์ง€๊ณ  ์˜จ๋‹ค.

    // (2) AuthorityUtils ์ด์šฉ, ๊ด€๋ฆฌ์ž์šฉ ๊ถŒํ•œ ๋ชฉ๋ก์„ List ๊ฐ์ฒด๋กœ ์ƒ์„ฑ
    private final List<GrantedAuthority> ADMIN_ROLES = AuthorityUtils.createAuthorityList("ROLE_ADMIN", "ROLE_USER");

    // (3)AuthorityUtils ์ด์šฉ, ์ผ๋ฐ˜ ์‚ฌ์šฉ์ž์šฉ ๊ถŒํ•œ ๋ชฉ๋ก์„ List ๊ฐ์ฒด๋กœ ์ƒ์„ฑ
    private final List<GrantedAuthority> USER_ROLES = AuthorityUtils.createAuthorityList("ROLE_USER");
    
    public List<GrantedAuthority> createAuthorities(String email) {
        // (4) ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌ๋ฐ›์€ ์ด๋ฉ”์ผ์ฃผ์†Œ์™€ application.yml์˜ ๊ด€๋ฆฌ์ž์™€ ๋™์ผํ•˜๋ฉด admin return
        if (email.equals(adminMailAddress)) {
            return ADMIN_ROLES;
        }
        return USER_ROLES;
    }
}

HelloAuthorityUtils๋Š” Role ๊ธฐ๋ฐ˜์˜ User ๊ถŒํ•œ์„ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.

 

 

6. CustomUserDetails ๊ตฌํ˜„ ๋ฐ ๊ฐœ์„ 

@Component
public class HelloUserDetailsServiceV2 implements UserDetailsService {
    private final MemberRepository memberRepository;
    private final HelloAuthorityUtils authorityUtils;

    public HelloUserDetailsServiceV2(MemberRepository memberRepository, HelloAuthorityUtils authorityUtils) {
        this.memberRepository = memberRepository;
        this.authorityUtils = authorityUtils;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Optional<Member> optionalMember = memberRepository.findByEmail(username);
        Member findMember = optionalMember.orElseThrow(() -> new BusinessLogicException(ExceptionCode.MEMBER_NOT_FOUND));

        return new HelloUserDetails(findMember);  // (1) ๊ฐœ์„ ๋œ ๋ถ€๋ถ„
    }

    // (2) HelloUserDetails ํด๋ž˜์Šค ์ถ”๊ฐ€
    private final class HelloUserDetails extends Member implements UserDetails { // (2-1)
        // (2-2)
        HelloUserDetails(Member member) {
            setMemberId(member.getMemberId());
            setFullName(member.getFullName());
            setEmail(member.getEmail());
            setPassword(member.getPassword());
        }

        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return authorityUtils.createAuthorities(this.getEmail());  // (2-3) ๋ฆฌํŒฉํ† ๋ง ํฌ์ธํŠธ
        } //helloAuthorityUtils์˜ createAuthorities ์ด์šฉํ•˜์—ฌ User ๊ถŒํ•œ์ •๋ณด๋ฅผ ์ƒ์„ฑ.

        // (2-4)
        @Override
        public String getUsername() { //getUsername์˜ ๋ฆฌํ„ด๊ฐ’์€ null์ผ์ˆ˜ ์—†์Œ.
            return getEmail();
        }

        @Override
        public boolean isAccountNonExpired() {
            return true;
        }

        @Override
        public boolean isAccountNonLocked() {
            return true;
        }

        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }

        @Override
        public boolean isEnabled() {
            return true;
        }
    }

}

๊ธฐ์กด์— loadUserByUsername()๋ฉ”์„œ๋“œ์˜ ๋ฆฌํ„ด๊ฐ’์œผ๋กœ new User(...)์„ ๋ฆฌํ„ดํ–ˆ์ง€๋งŒ, ๊ฐœ์„ ๋œ ์ฝ”๋“œ์—์„œ๋Š” (1)์ฒ˜๋Ÿผ HelloUserDetails(findMember) ๋ผ๋Š” Custom UserDetails ํด๋ž˜์Šค์˜ ์ƒ์„ฑ์ž๋กœ findMember๋ฅผ ์ „๋‹ฌํ•œ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ๊ฐ€ ํ›จ์”ฌ ๊น”๋”ํ•ด์กŒ๊ณ , ๊ธฐ์กด์— ๊ถŒํ•œ ์ •๋ณด๋ฅผ ์ƒ์„ฑํ•˜๋˜Collection<? extends GrantedAuthority> authorities = authorityUtils.createAuthorities(findMember) ์ฝ”๋“œ๋ฅผ (2)์˜ HelloUserDetails ํด๋ž˜์Šค์˜ ๋‚ด๋ถ€๋กœ ํฌํ•จ์‹œ์ผฐ๋‹ค. 

 

(2)์˜ HelloUserDetails ํด๋ž˜์Šค๋Š” UserDetails ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ , Member์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค๋ฅผ ์ƒ์†ํ•˜๊ณ  ์žˆ๋‹ค(2-1). ์ด๋ ‡๊ฒŒ ๊ตฌ์„ฑํ•˜๊ฒŒ ๋˜๋ฉด, ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒํ•œ ํšŒ์›์˜ ์ •๋ณด๋ฅผ SpringSecurity์˜ User ์ •๋ณด๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •๊ณผ User์˜ ๊ถŒํ•œ ์ •๋ณด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ณผ์ •์„ ์บก์Šํ™” ๊ฐ€๋Šฅํ•˜๋‹ค. ๊ทธ๋ฆฌ๊ณ  Member๋ฅผ ์ƒ์†ํ•˜๋ฏ€๋กœ HelloUserDetails๋ฅผ ๋ฆฌํ„ด๋ฐ›์•„ ์‚ฌ์šฉํ•˜๋Š” ์ธก์—์„œ ๋‘๊ฐœ ํด๋ž˜์Šค์˜ ๊ฐ์ฒด๋ฅผ ๋ชจ๋‘ ๋‹ค ์‰ฝ๊ฒŒ ์บ์ŠคํŒ… ํ•ด์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

 

7.User์˜ Role์„ DB์—์„œ ๊ด€๋ฆฌ

์ผ๋ฐ˜์ ์ธ User์˜ ์ธ์ฆ์ •๋ณด ๊ฐ™์€ ๋ณด์•ˆ๊ด€๋ จ ์ •๋ณด๋Š” DB์ฒ˜๋Ÿผ ์˜๊ตฌ์ €์žฅ์†Œ์— ์•ˆ์ „ํ•˜๊ฒŒ ๋ณด๊ด€๋œ๋‹ค. ํ•˜์ง€๋งŒ ํ˜„์žฌ๊นŒ์ง€์˜ ์ฝ”๋“œ๋Š” User์˜ ๊ถŒํ•œ ์ •๋ณด๋ฅผ DB์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ, DB์—์„œ ์กฐํšŒํ•œ User์˜ ์ •๋ณด๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ฝ”๋“œ์ƒ์—์„œ ์ƒ์„ฑํ•˜๊ณ ์žˆ๋‹ค. ์ด๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋กœ์ง์„ ์•Œ์•„๋ณด์ž.

 

1. User ์˜ ๊ถŒํ•œ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ ํ…Œ์ด๋ธ” ์ƒ์„ฑ

2. ํšŒ์› ๊ฐ€์ž…์‹œ , User์˜ ๊ถŒํ•œ ์ •๋ณด(Role)์„ DB์— ์ €์žฅํ•˜๋Š” ์ž‘์—…

3. ๋กœ๊ทธ์ธ ์ธ์ฆ์‹œ, User์˜ ๊ถŒํ•œ ์ •๋ณด๋ฅผ DB์—์„œ ์กฐํšŒํ•˜๋Š” ์ž‘์—…

 

7-1. User์˜ ๊ถŒํ•œ ์ •๋ณด ํ…Œ์ด๋ธ” ์ƒ์„ฑ

@NoArgsConstructor
@Getter
@Setter
@Entity
public class Member extends Auditable implements Principal{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long memberId;

    @Column(length = 100, nullable = false)
    private String fullName;

    @Column(nullable = false, updatable = false, unique = true)
    private String email;

    @Column(length = 100, nullable = false)
    private String password;

    @Enumerated(value = EnumType.STRING)
    @Column(length = 20, nullable = false)
    private MemberStatus memberStatus = MemberStatus.MEMBER_ACTIVE;

    // (1) User์˜ ๊ถŒํ•œ ์ •๋ณด ํ…Œ์ด๋ธ”๊ณผ ๋งคํ•‘๋˜๋Š” ์ •๋ณด
    @ElementCollection(fetch = FetchType.EAGER)
    private List<String> roles = new ArrayList<>();

    public Member(String email) {
        this.email = email;
    }

    public Member(String email, String fullName, String password) {
        this.email = email;
        this.fullName= fullName;
        this.password = password;
    }

    @Override
    public String getName() {
        return getEmail();
    }

    public enum MemberStatus {
        MEMBER_ACTIVE("ํ™œ๋™์ค‘"),
        MEMBER_SLEEP("ํœด๋ฉด ์ƒํƒœ"),
        MEMBER_QUIT("ํƒˆํ‡ด ์ƒํƒœ");

        @Getter
        private String status;

        MemberStatus(String status) {
           this.status = status;
        }
    }

    public enum MemberRole {
        ROLE_USER,
        ROLE_ADMIN
    }
}

User์˜ ๊ถŒํ•œ์ •๋ณด๋ฅผ ๋งคํ•‘ํ•˜๋Š” ๊ฒƒ์€ (1)๊ณผ ๊ฐ™์ด ์ฒ˜๋ฆฌํ•˜๋ฉด๋œ๋‹ค. List, Set ๋“ฑ์˜ ์ปฌ๋ ‰์…˜ ํƒ€์ž…์˜ ํ•„๋“œ๋Š” @ElementCollection ์• ๋„ˆํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•˜์—ฌ User ๊ถŒํ•œ ์ •๋ณด์™€ ๊ด€๋ จ๋œ ๋ณ„๋„์˜ ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค๋ฅผ ์ƒ์„œํ•˜์ง€ ์•Š์•„๋„ ๊ฐ„๋‹จํžˆ ๋งคํ•‘์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

 

์ด๋ฅผ DB์—์„œ ์กฐํšŒํ•˜๊ฒŒ ๋˜๋ฉด, ์•„๋ž˜ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์ด Member ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค์™€ ์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘์— ๋Œ€ํ•œ ํ…Œ์ด๋ธ”์ด ์ƒ์„ฑ๋œ๋‹ค.

 ์ด๋•Œ ํ•œ๋ช…์˜ ํšŒ์›์ด ํ•œ๊ฐœ์ด์ƒ์˜ Role์„ ๊ฐ€์งˆ์ˆ˜์žˆ์œผ๋ฏ€๋กœ, Member์™€ Member_ROLES ํ…Œ์ด๋ธ”์€ 1:N ์˜ ๊ด€๊ณ„์ด๋‹ค. ํšŒ์›๊ฐ€์ž…์„ ํ†ตํ•ด ํšŒ์› ์ •๋ณด๊ฐ€ ์ƒ์„ฑ๋˜๋ฉด์„œ, Member_ROLES ํ…Œ์ด๋ธ”์˜ MEMBER_MEMBER_ID ์—ด์— Member ํ…Œ์ด๋ธ”์˜ ๊ธฐ๋ณธํ‚ค ๊ฐ’, ROLES ์—ด์—๋Š” ๊ถŒํ•œ ์ •๋ณด๊ฐ€ ์ €์žฅ๋ ๊ฒƒ์ด๋‹ค.

 

7-2. ํšŒ์›๊ฐ€์ž…์‹œ, User์˜ ๊ถŒํ•œ ์ •๋ณด(Role)์„ DB์— ์ €์žฅ

@Component
public class HelloAuthorityUtils {
    @Value("${mail.address.admin}")
    private String adminMailAddress;

    ...
    ...

    private final List<String> ADMIN_ROLES_STRING = List.of("ADMIN", "USER");
    private final List<String> USER_ROLES_STRING = List.of("USER");

    ...
    ...

    // (1) DB ์ €์žฅ์šฉ
    public List<String> createRoles(String email) {
        if (email.equals(adminMailAddress)) {
            return ADMIN_ROLES_STRING;
        }
        return USER_ROLES_STRING;
    }
}

(1)์„ ๋ณด๋ฉด ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌ๋œ ์ด๋ฉ”์ผ๊ณผ, application.yml์˜ admin ์ด๋ฉ”์ผ ์ฃผ์†Œ์™€ ์ผ์น˜ํ•œ์ง€ ํ™•์ธํ•˜๊ณ , ๋™์ผํ•˜๋‹ค๋ฉด, ADMIN ROLE, ์•„๋‹ˆ๋ฉด USER ROLE ์„ ๋ฆฌํ„ดํ•œ๋‹ค.

 

@Transactional
public class DBMemberService implements MemberService {
    ...
    ...
  
    private final HelloAuthorityUtils authorityUtils;

    ...
    ...

    public Member createMember(Member member) {
        verifyExistsEmail(member.getEmail());
        String encryptedPassword = passwordEncoder.encode(member.getPassword());
        member.setPassword(encryptedPassword);

        // (1) Role์„ DB์— ์ €์žฅ
        List<String> roles = authorityUtils.createRoles(member.getEmail());
        member.setRoles(roles);

        Member savedMember = memberRepository.save(member);

        return savedMember;
    }

    ...
    ...
}

์œ„์—์„œ ์ž‘์„ฑํ–ˆ๋˜ createRoles๋ฅผ ์ด์šฉํ•ด์„œ ํšŒ์›์˜ ๊ถŒํ•œ์„ ์ƒ์„ฑํ•˜๊ณ , member์˜ ๊ฐ์ฒด์— Set ํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ Role๊นŒ์ง€ ์„ธํŒ…๋œ Member ๊ฐ์ฒด๋ฅผ DB์— ์ €์žฅ์‹œํ‚จ๋‹ค.

 

7-3. ๋กœ๊ทธ์ธ ์ธ์ฆ์‹œ, User์˜ ๊ถŒํ•œ ์ •๋ณด๋ฅผ DB์—์„œ ์กฐํšŒํ•˜๋Š” ์ž‘์—…

@Component
public class HelloUserDetailsServiceV3 implements UserDetailsService {
    private final MemberRepository memberRepository;
    private final HelloAuthorityUtils authorityUtils;

    public HelloUserDetailsServiceV3(MemberRepository memberRepository, HelloAuthorityUtils authorityUtils) {
        this.memberRepository = memberRepository;
        this.authorityUtils = authorityUtils;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Optional<Member> optionalMember = memberRepository.findByEmail(username);
        Member findMember = optionalMember.orElseThrow(() -> new BusinessLogicException(ExceptionCode.MEMBER_NOT_FOUND));

        return new HelloUserDetails(findMember);
    }

    private final class HelloUserDetails extends Member implements UserDetails {
        HelloUserDetails(Member member) {
            setMemberId(member.getMemberId());
            setFullName(member.getFullName());
            setEmail(member.getEmail());
            setPassword(member.getPassword());
            setRoles(member.getRoles());        // (1)
        }

        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            // (2) DB์— ์ €์žฅ๋œ Role ์ •๋ณด๋กœ User ๊ถŒํ•œ ๋ชฉ๋ก ์ƒ์„ฑ
            return authorityUtils.createAuthorities(this.getRoles());
        }

        ...
        ...
    }

}

์œ„์—์„œ ์ž‘์„ฑํ–ˆ์—ˆ๋˜ HelloUserDetails๋ฅผ ํ•œ๋ฒˆ๋” ๊ฐœ์„ ํ•ด์„œ V3๋กœ ๋งŒ๋“ค์—ˆ๋‹ค. 

(1)์„ ๋ณด๋ฉด, HelloUserDetails๊ฐ€ ์ƒ์†ํ•˜๊ณ ์žˆ๋Š” Member์— ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒํ•œ List<String> roles๋ฅผ ์ „๋‹ฌํ•œ๋‹ค. 

(2)๋ฅผ ๋ณด๋ฉด, Member์— ์ „๋‹ฌํ•œ Role์˜ ์ •๋ณด๋ฅผ authorityUtils.createAuthorities() ๋ฉ”์„œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌํ•ด์„œ ๊ถŒํ•œ๋ชฉ๋ก(List<GrantedAuthority>)๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.  ๊ธฐ์กด(V2)์—๋Š” DB์—์„œ Role์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ,    authorityUtils.createAuthorities(this.getEmail())๋กœ ์ž‘์„ฑ๋˜์—ˆ์—ˆ๋‹ค.

 

@Component
public class HelloAuthorityUtils {
    @Value("${mail.address.admin}")
    private String adminMailAddress;

    private final List<GrantedAuthority> ADMIN_ROLES = AuthorityUtils.createAuthorityList("ROLE_ADMIN", "ROLE_USER");
    private final List<GrantedAuthority> USER_ROLES = AuthorityUtils.createAuthorityList("ROLE_USER");
    private final List<String> ADMIN_ROLES_STRING = List.of("ADMIN", "USER");
    private final List<String> USER_ROLES_STRING = List.of("USER");

    // ๋ฉ”๋ชจ๋ฆฌ ์ƒ์˜ Role์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ถŒํ•œ ์ •๋ณด ์ƒ์„ฑ.
    public List<GrantedAuthority> createAuthorities(String email) {
        if (email.equals(adminMailAddress)) {
            return ADMIN_ROLES;
        }
        return USER_ROLES;
    }

    // (1) DB์— ์ €์žฅ๋œ Role์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ถŒํ•œ ์ •๋ณด ์ƒ์„ฑ
    public List<GrantedAuthority> createAuthorities(List<String> roles) {
       List<GrantedAuthority> authorities = roles.stream()
               .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) // (2)
               .collect(Collectors.toList());
       return authorities;
    }

    ...
    ...
}

DB์—์„œ ์กฐํšŒํ•œ Role ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ User์˜ ๊ถŒํ•œ ๋ชฉ๋ก์„ ์ƒ์„ฑํ•˜๋Š” createAuthorities(List<String> roles) ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์˜€๋‹ค. 

(1)์„ ๋ณด๋ฉด ๊ธฐ์กด์—๋Š” application.yml์˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜์— ์ •์˜๋œ ๊ด€๋ฆฌ์ž์šฉ ์ด๋ฉ”์ผ ์ฃผ์†Œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๊ด€๋ฆฌ์ž์˜ Role์„ ์ถ”๊ฐ€ ํ•˜์˜€์ง€๋งŒ, ์ด์ œ๋Š” ๋‹จ์ˆœํžˆ DB์—์„œ ๊ฐ€์ ธ์˜จ Role ๋ชฉ๋ก์„ ๊ทธ๋Œ€๋กœ ์ด์šฉํ•ด์„œ ๊ถŒํ•œ ๋ชฉ๋ก์„ ๋งŒ๋“ค์–ด์ค€๋‹ค. ์ด๋•Œ, ์ƒ์„ฑ์ž์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ROLE_USER , ROLE_ADMIN ๋“ฑ์˜ ํ˜•ํƒœ๋กœ ๋„˜๊ฒจ์ฃผ์–ด์•ผํ•œ๋‹ค.

 

์—ฌ๊ธฐ๊นŒ์ง€ ์ž‘์„ฑ์ด ๋˜์—ˆ๋‹ค๋ฉด, ์ •์ƒ์ ์œผ๋กœ ํšŒ์›๊ฐ€์ž…๊ณผ ๋กœ๊ทธ์ธ์ด ์ž‘๋™ํ• ๊ฒƒ์ด๋‹ค.

 

 

๋ฐ˜์‘ํ˜•