์•ˆ๋…•ํ•˜์„ธ์š”, ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์Šคํ”„๋ง๋ถ€ํŠธ 3.1 ์ด์ƒ ๋ฒ„์ „ ๋ฐ Spring Security 6.1.5๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๊ฒฝํ—˜ํ•œ ๋„์ „๊ณผ ํ•ด๊ฒฐ ๊ณผ์ •์„ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ธ€์€ ํŠนํžˆ SecurityConfig ์„ค์ •์— ์ดˆ์ ์„ ๋งž์ถ”๊ณ  ์žˆ์œผ๋ฉฐ, ๊ณต์‹ ๋ฌธ์„œ์—์„œ ๋‹ค๋ฃจ์ง€ ์•Š๋Š” ์„ธ๋ถ€์‚ฌํ•ญ๊ณผ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์˜ค๋ฅ˜๋“ค์— ๋Œ€ํ•ด ๋‹ค๋ฃน๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ ๊ตฌ์„ฑ ๋ฐ ์˜์กด์„ฑ ์ถ”๊ฐ€

dependency ์ถ”๊ฐ€

  • Maven project - pom.xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
  • Gradle project - build.gradle
implementation 'org.springframework.boot:spring-boot-starter-security'

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ์˜์กด์„ฑ ์ถ”๊ฐ€ ์‹œ ๋ฐœ์ƒํ•˜๋Š” ์ผ๋“ค

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๊ฐ€ ์ดˆ๊ธฐํ™”๋˜๊ณ , ๊ธฐ๋ณธ ์›น ๋ณด์•ˆ ๊ธฐ๋Šฅ์ด ์‹œ์Šคํ…œ์— ํ†ตํ•ฉ๋ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ, ๋ชจ๋“  ์š”์ฒญ์€ ์ธ์ฆ์ด ํ•„์š”ํ•˜๋ฉฐ, ํผ ๋กœ๊ทธ์ธ๊ณผ httpBasic ๋กœ๊ทธ์ธ ๋ฐฉ์‹์ด ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€์™€ ๊ณ„์ •(user/๋žœ๋ค๋ฌธ์ž์—ด)๋„ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

  1. ๋ชจ๋“  ์š”์ฒญ์€ ์ธ์ฆ์ด ๋˜์–ด์•ผ ์ž์›์— ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
  2. ์ธ์ฆ ๋ฐฉ์‹์€ ํผ ๋กœ๊ทธ์ธ ๋ฐฉ์‹๊ณผ httpBasic๋กœ๊ทธ์ธ ๋ฐฉ์‹์„ ์ œ๊ณตํ•œ๋‹ค.
  3. ๊ธฐ๋ณธ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ์ œ๊ณต (๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ์„ค์ • ๋”ฐ๋กœ ์•ˆํ•˜๋ฉด ์—ฌ๊ธฐ๋กœ๋งŒ ์˜ด)
  4. ๊ธฐ๋ณธ ๊ณ„์ • ํ•œ ๊ฐœ ์ œ๊ณต user/๋žœ๋ค๋ฌธ์ž์—ด๋žœ๋ค๋ฌธ์ž์—ด


๊ธฐ๋ณธ ๋กœ๊ทธ์ธ ํ™”๋ฉด

 

  • spring security๋กœ ๋งŒ๋“ค์–ด์ง€๋Š” ๊ณ„์ • ๋น„๋ฐ€๋ฒˆํ˜ธ

์ฐธ๊ณ : ๊ฐœ๋ฐœ๋„์ค‘ ๋งค๋ฒˆ ์ƒ์„ฑ๋˜๋Š” ๋žœ๋ค๋ฌธ์ž์—ด/๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธํ•˜๋Š”๊ฒƒ์€ ๊นŒ๋‹ค๋กญ๋‹ค. application.properties์— ๊ธฐ๋ณธ name/password์„ค์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

  • (PasswordEncoder Bean ์ถ”๊ฐ€ํ•˜๋ฉด ๊ทธ๊ฑฐ์—๋งž์ถฐ์„œ ๋น„๋ฐ€๋ฒˆํ˜ธ ํ˜•ํƒœ๋„ ๋ฐ”๊ฟ”์ค˜์•ผํ•จ)
spring.security.user.name=user
spring.security.user.password=1234

๋ฌธ์ œ์ 

  • ๊ณ„์ • ์ถ”๊ฐ€, ๊ถŒํ•œ ์ถ”๊ฐ€, DB ์—ฐ๋™ ๋“ฑ
  • ๊ธฐ๋ณธ์ ์ธ ๋ณด์•ˆ ๊ธฐ๋Šฅ ์™ธ์— ์‹œ์Šคํ…œ์—์„œ ํ•„์š”๋กœ ํ•˜๋Š” ๋” ์„ธ๋ถ€์ ์ด๊ณ  ์ถ”๊ฐ€์ ์ธ ๋ณด์•ˆ๊ธฐ๋Šฅ ํ•„์š”.

์‚ฌ์šฉ์ž ์ •์˜ ๋ณด์•ˆ ๊ธฐ๋Šฅ ๊ตฌํ˜„

WebSecurityConfigurerAdapter๋ฅผ ์‚ฌ์šฉํ•ด HttpSecurity๋ฅผ ํ†ตํ•œ ์„ธ๋ถ€ ๋ณด์•ˆ ๊ธฐ๋Šฅ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด, http.authorizeRequests(), http.formLogin() ๋“ฑ์˜ API๋ฅผ ํ†ตํ•ด ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

HttpSecurity ๋ผ๋Š” ์„ธ๋ถ€์ ์ธ ๋ณด์•ˆ๊ธฐ๋Šฅ์„ ์„ค์ •ํ• ์ˆ˜ ์žˆ๋Š” API๋ฅผ ์ œ๊ณตํ•˜๋Š” ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

์ธ์ฆ API ์ธ๊ฐ€ API
http.formLogin() http.authorizeRequests()
http.logout() http.antMatchers("/admin")
http.csrf() http.hasRole("USER")
http.httpBasic() http.permitAll()
http.sessionManagement() http.authenticated()
http.rememberMe() http.fullyAuthenticated()
http.exceptionHandling() http.access("hasRole('USER')")
http.addFilter() http.denyAll()

SecurityConfig ์„ค์ • ์˜ˆ์‹œ

@Configuration
@EnableWebSecurity
public class SecurityConfig {
      @Bean  
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {  
        http  
                .authorizeHttpRequests(request -> request  
                        .requestMatchers(new AntPathRequestMatcher("/login")).permitAll()  
                        .requestMatchers(new AntPathRequestMatcher("/user/register")).permitAll()  
                        .requestMatchers(new AntPathRequestMatcher("/user/password")).permitAll()  
                        .requestMatchers(new AntPathRequestMatcher("/status")).permitAll()  
                        .requestMatchers(new AntPathRequestMatcher("/view/join")).permitAll()  
                        .requestMatchers(new AntPathRequestMatcher("/auth/join")).permitAll()  
                        .requestMatchers(new AntPathRequestMatcher("/home")).permitAll()  
                        .requestMatchers(new AntPathRequestMatcher("/dist/css/**")).permitAll()  
                        .requestMatchers(new AntPathRequestMatcher("/dist/js/**")).permitAll()  
                        .requestMatchers(new AntPathRequestMatcher("/dist/img/**")).permitAll()  
                        .requestMatchers(new AntPathRequestMatcher("/plugins/**")).permitAll()  
                        .anyRequest().authenticated() // ๊ทธ ์™ธ ๋ชจ๋“  ์š”์ฒญ์€ ์ธ์ฆ ํ•„์š”  
                )  
                .formLogin(formLogin -> formLogin  
                        .loginPage("/login")  
                        .loginProcessingUrl("/login-process")  
                        .usernameParameter("loginId")  
                        .passwordParameter("password")  
                        .defaultSuccessUrl("/", true)  
                        .failureHandler(new CustomAuthenticationFailureHandler())  
                        .permitAll()  
                )  
                .logout(Customizer.withDefaults());  


        return http.build();  
    }  


    @Bean      // ์ด๊ฑธ ์ฃผ์„ํ•˜๋ฉด passwordEncoder๋กœ ์•”ํ˜ธํ™”๊ฐ€ ๋˜์งˆ ์•Š์Šต๋‹ˆ๋‹ค.
    public PasswordEncoder passwordEncoder() {  
        return new BCryptPasswordEncoder();  
    }  

}
  • @EnableWebSecurity ์• ๋…ธํ…Œ์ด์…˜์„ WebSecurityconfigurerAdapter ๋ฅผ ์ƒ์†ํ•˜๋Š” ์„ค์ • ๊ฐ์ฒด์— ๋ถ™ํ˜€์ฃผ๋ฉด SpringSecurityFilterChain์— ๋“ฑ๋ก๋œ๋‹ค.

๐Ÿš€์ฐธ๊ณ : @EnableWebMvcSecurity

์Šคํ”„๋ง MVC์—์„œ ์›น ๋ณด์•ˆ์„ ํ™œ์„ฑํ™”ํ•˜๊ธฐ ์œ„ํ•œ ์• ๋„ˆํ…Œ์ด์…˜์œผ๋กœ ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์†Œ๋“œ์—์„œ @AuthenticationPrincipal ์• ๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ด์šฉํ•ด ์ธ์ฆ์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ž๋™์œผ๋กœ CSRF ํ† ํฐ์„ ์Šคํ”„๋ง์˜ form binding tag library๋ฅผ ์‚ฌ์šฉํ•ด ์ถ”๊ฐ€ํ•˜๋Š” ๋นˆ์„ ์„ค์ •ํ•œ๋‹ค.

์˜ค๋ฅ˜ํ•ด๊ฒฐ

https://jakezo.tistory.com/26

[Spring Security 6.1.5 ๋ฒ„์ „ ์˜ค๋ฅ˜ ๋ฌธ์ œ ํ•ด๊ฒฐ

์•ˆ๋…•ํ•˜์„ธ์š”, Spring Security์— ๊ด€ํ•œ ์˜ค๋Š˜์˜ ๋ธ”๋กœ๊ทธ ๊ธ€์—์„œ๋Š” Spring Security ์„ค์ • ์ค‘ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ผ๋ฐ˜์ ์ธ ์˜ค๋ฅ˜์™€ ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•ด๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค. ํŠนํžˆ, AntPathRequestMatcher์™€ MvcReques

jakezo.tistory.com](https://jakezo.tistory.com/26)

Form Login ์ธ์ฆ

Login Flow

  1. Client์—์„œ Get๋ฐฉ์‹์œผ๋กœ Home Url์ž์›์ ‘๊ทผ ์š”์ฒญ
  2. Server์—์„œ๋Š” ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž๋งŒ ์ ‘๊ทผ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ํŒ๋‹จํ•ด ์ธ์ฆ์ด ์•ˆ๋˜๋ฉด ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ
  3. Client๋Š” ๋กœ๊ทธ์ธํŽ˜์ด์ง€์˜ username/password ์ž…๋ ฅํ•˜์—ฌ Post๋ฐฉ์‹์œผ๋กœ ์ธ์ฆ ์‹œ๋„
  4. Server์—์„œ๋Š” Session ID์ƒ์„ฑํ›„ ์ธ์ฆ๊ฒฐ๊ณผ๋ฅผ ๋‹ด์€ ์ธ์ฆ ํ† ํฐ(Authentication) ์ƒ์„ฑ ๋ฐ ์ €์žฅ
  5. Client์—์„œ /home ์ ‘๊ทผ์š”์ฒญ ์‹œ ์„ธ์…˜์— ์ €์žฅ๋œ ์ธ์ฆ ํ† ํฐ์œผ๋กœ ์ ‘๊ทผ๋ฐ ์ธ์ฆ ์œ ์ง€

UsernamePasswordAuthenticationFilter

๋กœ๊ทธ์ธ ์ธ์ฆ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•„ํ„ฐ๋กœ, ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ์‚ฌ์šฉ์ž ์ด๋ฆ„๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ธ์ฆ ๊ฐ์ฒด์— ์ €์žฅํ•˜๊ณ , ์ธ์ฆ ๊ด€๋ฆฌ์ž(AuthenticationManager)์—๊ฒŒ ์ธ์ฆ์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. ์ธ์ฆ ์„ฑ๊ณต ์‹œ, SecurityContext์— ์ธ์ฆ ๊ฐ์ฒด๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

Logout ์ฒ˜๋ฆฌ ๋ฐ LogoutFilter

  • Flow Diagram

  1. Client์—์„œ GET๋ฐฉ์‹์˜ /logout ๋ฆฌ์†Œ์Šค ํ˜ธ์ถœ
  2. Server์—์„œ ์„ธ์…˜๋ฌดํšจํ™”, ์ธ์ฆํ† ํฐ ์‚ญ์ œ, ์ฟ ํ‚ค์ •๋ณด ์‚ญ์ œ ํ›„ ๋กœ๊ทธ์ธํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ
  • Logout Flow Detail
  1. ์š”์ฒญ์ด Logout Url ์ธ์ง€ ํ™•์ธ
  2. ๋งž์„ ๊ฒฝ์šฐ SecurityContext์—์„œ ์ธ์ฆ๊ฐ์ฒด(Authentication)๊ฐ์ฒด๋ฅผ ๊บผ๋‚ด์˜ด
  3. SecurityContextLogoutHandler์—์„œ ์„ธ์…˜ ๋ฌดํšจํ™”, ์ฟ ํ‚ค์‚ญ์ œ, clearContext()๋ฅผํ†ตํ•ด SecurityContext๊ฐ์ฒด๋ฅผ ์‚ญ์ œํ•˜๊ณ  ์ธ์ฆ๊ฐ์ฒด๋„ null๋กœ ๋งŒ๋“ ๋‹ค.
  4. SimpleUrlLogoutSuccessHandler๋ฅผ ํ†ตํ•ด ๋กœ๊ทธ์ธํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์‹œํ‚จ๋‹ค.

๋กœ๊ทธ์ธ ์‹คํŒจ์‹œ ๊ตฌํ˜„ ์˜ˆ์‹œ

 

https://jakezo.tistory.com/25