Spring Security๋ž€?

ํ•œ์ง€์—ฐยท2023๋…„ 8์›” 4์ผ
0

๐Ÿ“˜ Spring Security๋ž€

  • ์Šคํ”„๋ง ๊ธฐ๋ฐ˜์˜ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ณด์•ˆ(์ธ์ฆ๊ณผ ์ธ๊ฐ€)์„ ๋‹ด๋‹นํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ
  • ๋ณด์•ˆ๊ณผ ๊ด€๋ จํ•ด ์ฒด๊ณ„์ ์ธ ์˜ต์…˜๋“ค์„ ์ง€์›
  • ํ•„ํ„ฐ ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•˜์—ฌ ์Šคํ”„๋งMVC์™€ ๋ถ„๋ฆฌ๋˜์–ด ๊ด€๋ฆฌ ๋ฐ ๋™์ž‘

ํ๋ฆ„

1๏ธโƒฃ ํด๋ผ์ด์–ธํŠธ(์œ ์ €)๊ฐ€ ๋กœ๊ทธ์ธ์„ ์‹œ๋„

2๏ธโƒฃ AuthenticationFilter๋Š” AuthenticationManager, AuthenticationProvider(s), UserDetailsService๋ฅผ ํ†ตํ•ด DB์—์„œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์ฝ์–ด์˜ด

  • UserDetailsService๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋กœ ํ•ด๋‹น ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ๋นˆ(Bean)์„ ์ƒ์„ฑํ•˜๋ฉด ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋Š” ํ•ด๋‹น ๋นˆ์„ ์‚ฌ์šฉํ•จ. ์ฆ‰, ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ ๋ถ€ํ„ฐ ์ฝ์–ด๋“ค์ผ์ง€ ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์Œ

3๏ธโƒฃ UserDetailsService๋Š” ๋กœ๊ทธ์ธํ•œ ID์— ํ•ด๋‹นํ•˜๋Š” ์ •๋ณด๋ฅผ DB์—์„œ ์ฝ์–ด๋“ค์—ฌ UserDetails๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฐ์ฒด๋กœ ๋ฐ˜ํ™˜

  • UserDetails๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•จ, ์ดํ›„ UserDetails์ •๋ณด๋ฅผ ์„ธ์…˜์— ์ €์žฅ

4๏ธโƒฃ ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋Š” ์ธ๋ฉ”๋ชจ๋ฆฌ ์„ธ์…˜์ €์žฅ์†Œ์ธ SecurityContextHolder ์— UserDetails์ •๋ณด๋ฅผ ์ €์žฅ

5๏ธโƒฃ ํด๋ผ์ด์–ธํŠธ(์œ ์ €)์—๊ฒŒ session ID(JSESSION ID)์™€ ํ•จ๊ป˜ ์‘๋‹ต

6๏ธโƒฃ ์ดํ›„ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ์š”์ฒญ ์ฟ ํ‚ค์—์„œ JSESSION ID์ •๋ณด๋ฅผ ํ†ตํ•ด ์ด๋ฏธ ๋กœ๊ทธ์ธ ์ •๋ณด๊ฐ€ ์ €์žฅ๋˜์–ด ์žˆ๋Š” ์ง€ ํ™•์ธ


๐Ÿ“š ์ธ์ฆ(Authentication)๊ณผ ์ธ๊ฐ€(Authorization)

Authentication

  • ์‚ฌ์šฉ์ž๊ฐ€ ๋ณธ์ธ์ด ๋งž๋Š”์ง€ ์•„์ด๋””์™€ ์•”ํ˜ธ๋ฅผ ์ด์šฉํ•˜์—ฌ ํ™•์ธ

Authorization

  • ์ธ์ฆ ๋ฐ›์€ ํšŒ์›์ด ์ž์›์„ ์š”์ฒญํ–ˆ์„ ๋•Œ ๊ทธ์— ๋Œ€ํ•ด ์ ‘๊ทผ ๊ถŒํ•œ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜์—ฌ ํ—ˆ์šฉ

๐Ÿ“š Spring Security Filter

์—ญํ• 

  • ํด๋ผ์ด์–ธํŠธ์™€ ์ž์› ์‚ฌ์ด์—์„œ ์š”์ฒญ๊ณผ ์‘๋‹ต ์ •๋ณด๋ฅผ ์ด์šฉํ•ด ๋‹ค์–‘ํ•œ ์ฒ˜๋ฆฌ

ํ๋ฆ„

1๏ธโƒฃ. ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ
2๏ธโƒฃ. ์š”์ฒญ URI ๊ฒฝ๋กœ ๊ธฐ๋ฐ˜ ํ•„ํ„ฐ ์ธ์Šคํ„ด์Šค์™€ ์„œ๋ธ”๋ฆฟ์ด ํฌํ•จ๋œ FilterChain์ƒ์„ฑ
3๏ธโƒฃ. ์ž‘์—…์ˆ˜ํ–‰

  • ๋‹ค์šด์ŠคํŠธ๋ฆผ ํ•„ํ„ฐ ๋˜๋Š” ์„œ๋ธ”๋ฆฟ์ด ํ˜ธ์ถœ๋˜์ง€ ์•Š๋„๋ก ํ•จ
    Http ์„œ๋ธ”๋ฆฟ ์‘๋‹ต์„ ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ์ž์›์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์‹œํ‚ฌ ์ˆ˜๋„ ์žˆ์Œ
  • ๋‹ค์šด์ŠคํŠธ๋ฆผ ํ•„ํ„ฐ์™€ ์„œ๋ธ”๋ฆฟ์— ์‚ฌ์šฉ๋˜๋Š” HttpServletRequest ๋˜๋Š” HttpServletResponse์ˆ˜์ •

ํŠน์ง•

  • DispatcherServlet๋ณด๋‹ค ๋จผ์ € ๋™์ž‘ํ•จ
  • ๋‹ค์–‘ํ•œ ํ•„ํ„ฐ๋ฅผ ์ ์šฉํ•˜์—ฌ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Œ

๐Ÿ“š Security Filter Chain

  • ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ํ•„ํ„ฐ๋“ค์„ 10๊ฐœ ์ด์ƒ ๊ธฐ๋ณธ ์ œ๊ณต, ์ด ๋ฌถ์Œ์„ Security filter Chain์ด๋ผ๊ณ  ํ•จ

  • DelegationFilterProxy์— ๋ฐ”๋กœ ๋“ฑ๋กํ•˜์ง€ ์•Š๊ณ  FilterChainProxy์•ˆ์— Security Filter Chain์„ ๋“ฑ๋กํ•จ

๋“ฑ๋ก๋œ ํ•„ํ„ฐ๋“ค์ด ํ•˜๋Š” ์—ญํ• 

  • SecurityContextPersistenceFilter
    SecurityContextRepository์—์„œ SecurityContext๋ฅผ ๊ฐ€์ ธ์˜ค๊ฑฐ๋‚˜ ์ €์žฅํ•˜๋Š” ์—ญํ• 

  • LogoutFilter
    ์„ค์ •๋œ ๋กœ๊ทธ์•„์›ƒ URL๋กœ ์˜ค๋Š” ์š”์ฒญ์„ ๊ฐ์‹œํ•˜๋ฉฐ, ํ•ด๋‹น ์œ ์ €๋ฅผ ๋กœ๊ทธ์•„์›ƒ ์ฒ˜๋ฆฌ

  • UsernamePasswordAuthenticationFilter
    (์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” form ๊ธฐ๋ฐ˜ ์ธ์ฆ) ์„ค์ •๋œ ๋กœ๊ทธ์ธ URL๋กœ ์˜ค๋Š” ์š”์ฒญ ๊ฐ์‹œ, ์œ ์ € ์ธ์ฆ ์ฒ˜๋ฆฌ

  • BasicAuthenticationFilter
    HTTP ๊ธฐ๋ณธ ์ธ์ฆ ํ—ค๋”๋ฅผ ๊ฐ์‹œํ•˜์—ฌ ์ฒ˜๋ฆฌ

  • RequestCacheAwareFilter
    ๋กœ๊ทธ์ธ ์„ฑ๊ณต ํ›„ ์›๋ž˜ ์š”์ฒญ ์ •๋ณด๋ฅผ ์žฌ๊ตฌ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ

  • SecurityContextHolderAwareRequestFilter
    HttpServletRequestWrapper๋ฅผ ์ƒ์†ํ•œ SecurityContextHolderAwareRequestWapper ํด๋ž˜์Šค๋กœ HttpServletRequest ์ •๋ณด๋ฅผ ๊ฐ์‹ธ์ฃผ๊ณ  Security
    ContextHolderAwareRequestWrapper ํด๋ž˜์Šค๋Š” ํ•„ํ„ฐ ์ฒด์ธ์ƒ์˜ ๋‹ค์Œ ํ•„ํ„ฐ๋“ค์—๊ฒŒ ๋ถ€๊ฐ€์ •๋ณด ์ œ๊ณต

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

  • SessionManagementFilter
    ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž์™€ ๊ด€๋ จ๋œ ๋ชจ๋“  ์„ธ์…˜์„ ์ถ”์ 

  • ExceptionTranslationFilter
    ๋ณดํ˜ธ๋œ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์ค‘์— ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ˆ์™ธ๋ฅผ ์œ„์ž„ํ•˜๊ฑฐ๋‚˜ ์ „๋‹ฌํ•˜๋Š” ์—ญํ• 

  • FilterSecurityInterceptor
    AccessDecisionManager๋กœ ๊ถŒํ•œ๋ถ€์—ฌ ์ฒ˜๋ฆฌ๋ฅผ ์œ„์ž„ํ•จ์œผ๋กœ์จ ์ ‘๊ทผ ์ œ์–ด ๊ฒฐ์ •์„ ์‰ฝ๊ฒŒ ํ•ด์คŒ


โš™๏ธ Spring Security ์„ค์ • ๋ฐฉ๋ฒ•

build.gradle

	implementation 'org.springframework.boot:spring-boot-starter-security'

Config

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableMethodSecurity
public class SecurityConfig {

    private final TokenProvider tokenProvider;
    private final CorsFilter corsFilter;
    private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    private final JwtAccessDeniedHandler jwtAccessDeniedHandler;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws  Exception {


        http
                .csrf((csrf) -> csrf.disable())

                .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)

                .exceptionHandling(exceptionHandling -> exceptionHandling
                .accessDeniedHandler(jwtAccessDeniedHandler)
                .authenticationEntryPoint(jwtAuthenticationEntryPoint)
                )

                .cors(cors -> cors.disable())

                .sessionManagement(httpSecuritySessionManagementConfigurer ->
                        httpSecuritySessionManagementConfigurer
                                .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .httpBasic(b -> b.disable())

                .authorizeHttpRequests(
        (authorizeHttpRequests) ->
                        authorizeHttpRequests
                               .requestMatchers(
                                       "/signup",
                                       "/photo/list",
                                       "/photo/like",
                                       "/meeting/list",
                                       "/meeting/info",
                                       "/people-count",
                                       "/api/authenticate",
                                       "/meeting/info/**",
                                       "/login").permitAll()
                                .anyRequest().authenticated()
                )
                .formLogin(
        formLogin -> formLogin.disable())
                .logout(logout -> logout.logoutUrl("/logout"))
                .apply(new JwtSecurityConfig(tokenProvider));


        return http.build();
    }

}
  • csrf.disable()
    ์‚ฌ์ดํŠธ๊ฐ„ ์š”์ฒญ ์œ„์กฐ ๋ฐฉ์ง€ ๋น„ํ™œ์„ฑํ™”

  • cors.disable()
    ๋‹ค๋ฅธ ์ถœ์ฒ˜๋ฅผ ๊ฐ€์ง„ ์ž์›์˜ ์ ‘๊ทผ ๋น„ํ™œ์„ฑํ™”

  • httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    ์Šคํ”„๋ง์ด ์„ธ์…˜์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์ƒ์„ฑ๋„ ํ•˜์ง€ ์•Š์Œ

  • httpBasic(b -> b.disable()
    ๊ธฐ๋ณธ ์ธ์ฆ ๋กœ๊ทธ์ธ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ

  • authorizeHttpRequests.requestMatchers("๊ฒฝ๋กœ").permitAll()
    ๊ฒฝ๋กœ๋ณ„๋กœ ๊ถŒํ•œ์„ ์ฒ˜๋ฆฌํ•˜์ง€๋งŒ permitAll()๋œ ๊ฒฝ๋กœ๋Š” ๊ถŒํ•œ์— ์ƒ๊ด€์—†์ด ๋ชจ๋‘์—๊ฒŒ ํ—ˆ์šฉ๋จ

  • anyRequest().authenticated()
    ํ—ˆ์šฉ๋œ ๊ฒฝ๋กœ๋ฅผ ์ œ์™ธํ•œ ๋ชจ๋“  ์š”์ฒญ์€ ์ธ์ฆ ๋˜์–ด์•ผ ํ•จ

  • formLogin.disable()
    formLogin์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ

UserDetailsService

@Component("userDetailsService")
@RequiredArgsConstructor
@Transactional(readOnly = true)
@Slf4j
public class CustomUserDetailsService implements UserDetailsService {

    private final UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
        log.info("loadUserByUsername ์‹คํ–‰๋จ");

        User user = userRepository.findByUserStrId(userId);

        if(user == null) {
            throw new UserNotFoundException(userId+"๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค");
        }

        return new PrincipalDetails(user);
    }
}

UserDetatils

@NoArgsConstructor
@Getter
@Setter
public class PrincipalDetails implements UserDetails, OAuth2User{

    private User user;

    private Map<String, Object> attributes;



    public PrincipalDetails(User user) {
        this.user = user;
    }

    public PrincipalDetails(User user, Map<String, Object> attributes) {
        this.user = user;
        this.attributes = attributes;
    }

    @Override
    public Map<String, Object> getAttributes() {
        return attributes;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }

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

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

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

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

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

์ถœ์ฒ˜

profile
๋ฐฐ์šฐ๊ณ  ํ™œ์šฉํ•˜๋Š” ๊ฒƒ์„ ์ฆ๊ธฐ๋Š” ๊ฐœ๋ฐœ์ž, ํ•œ์ง€์—ฐ์ž…๋‹ˆ๋‹ค!

0๊ฐœ์˜ ๋Œ“๊ธ€