JiwonDev

Spring Security #1 ๊ธฐ๋ณธ๋™์ž‘

by JiwonDev
 

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ - Spring Boot ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐœ๋ฐœํ•˜๋Š” Spring Security - ์ธํ”„๋Ÿฐ | ๊ฐ•์˜

์ดˆ๊ธ‰์—์„œ ์ค‘.๊ณ ๊ธ‰์— ์ด๋ฅด๊ธฐ๊นŒ์ง€ ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์˜ ๊ธฐ๋ณธ ๊ฐœ๋…๋ถ€ํ„ฐ API ์‚ฌ์šฉ๋ฒ•๊ณผ ๋‚ด๋ถ€ ์•„ํ‚คํ…์ฒ˜๋ฅผ ํ•™์Šตํ•˜๊ฒŒ ๋˜๊ณ  ์ด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์‹ค์ „ ํ”„๋กœ์ ํŠธ๋ฅผ ์™„์„ฑํ•ด ๋‚˜๊ฐ์œผ๋กœ์จ ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์˜ ์ธ์ฆ๊ณผ

www.inflearn.com

 

 

๐Ÿ’ญ Spring Security

์Šคํ”„๋ง ๊ธฐ๋ฐ˜์˜ ์•ฑ์—์„œ ๋ณด์•ˆ(์ธ์ฆ, ๊ถŒํ•œ, ์ธ๊ฐ€)๋“ฑ์„ ๋‹ด๋‹นํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“ค์–ด์ง„ ํ•˜์œ„ ํ”„๋ ˆ์ž„์›Œํฌ์ด๋‹ค.

 

Spring Security๋Š” ๋ณด์•ˆ์„ ๋‹ด๋‹นํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์ด๋‹ค. ๊ทธ๋ž˜์„œ ์Šคํ”„๋ง ์•ฑ๊ณผ์˜ ์˜์กด์„ฑ์„ ๋ถ„๋ฆฌ์‹œํ‚ค๊ธฐ ์œ„ํ•ด ๊ธฐ๋ณธ์ ์œผ๋กœ ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•˜๊ฒŒ๋” ๋งŒ๋“ค์–ด์ ธ์žˆ๋‹ค. ์ฆ‰ ์Šคํ”„๋ง์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋”๋ผ๋„ ์„œ๋ธ”๋ฆฟ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ๋”ฐ๋กœ ์ ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

Spring Security๊ฐ€ ์—†๋‹ค๊ณ  ์Šคํ”„๋ง ์›น์•ฑ์˜ ๋ณด์•ˆ์„ ๋งŒ๋“ค ์ˆ˜ ์—†๋Š”๊ฑด ์•„๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ณด์•ˆ๊ณผ ๊ด€๋ จํ•ด์„œ ์ฒด๊ณ„์ ์œผ๋กœ ๋งŽ์€ ์˜ต์…˜์„ ์ œ๊ณตํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐœ๋ฐœ์ž ์ž…์žฅ์—์„œ๋Š” ์ผ์ผ์ด ๋ณด์•ˆ ๊ด€๋ จ ๋กœ์ง์„ ๊ณ ์ƒํ•ด์„œ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„๋„ ์‰ฝ๊ฒŒ ๋ณด์•ˆ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

Spring Security์˜ ์ธ์ฆ ์•„ํ‚คํ…์ฒ˜

 

 

 

๐Ÿ’ญ ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

maven์ด๋‚˜ gradle์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋ณต์žกํ•œ ์„ค์น˜๊ณผ์ • ์—†์ด ์•„๋ž˜ ํ•œ์ค„์ด๋ฉด ์ ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.

ํ•ด๋‹น ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜๊ฒŒ๋˜๋ฉด ์„œ๋ฒ„ (ex ์Šคํ”„๋ง๋ถ€ํŠธ์˜ ๋‚ด์žฅํ†ฐ์บฃ)์— ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์™€ ๊ด€๋ จ๋œ ์ดˆ๊ธฐํ™” ์ž‘์—… ๋ฐ ๋ณด์•ˆ ์„ค์ •์ด ์ด๋ฃจ์–ด์ง„๋‹ค. ์ฆ‰ ๋ณ„๋„์˜ ์„ค์ •์„ ํ•˜์ง€ ์•Š์•„๋„ ๊ธฐ๋ณธ์ ์ธ ์›น ๋ณด์•ˆ ๊ธฐ๋Šฅ์ด ์ ์šฉ๋œ๋‹ค๊ณ  ์ดํ•ดํ•˜๋ฉด ๋œ๋‹ค.

Spring-Security๋ฅผ ์ ์šฉํ•˜๋ฉด ๊ธฐ๋ณธ ๊ณ„์ •๊ณผ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€์„ ์ œ๊ณตํ•ด์ค€๋‹ค.

๋”๋ณด๊ธฐ

 ๊ธฐ๋ณธ์ ์ธ ์›น ๋ณด์•ˆ ๊ธฐ๋Šฅ์ด๋ž€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ์ธ์ฆ ๋ฐฉ์‹์œผ๋กœ ํผ ๋กœ๊ทธ์ธ ๋ฐฉ์‹ / httpBasic ๋กœ๊ทธ์ธ ๋ฐฉ์‹์„ ์ œ๊ณตํ•œ๋‹ค.
  • ๋ชจ๋“  ์š”์ฒญ(Request)์€ ์ธ์ฆ์ด ๋˜์–ด์•ผ ์ž์›์— ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ๊ฐœ๋ฐœ์šฉ์œผ๋กœ ๊ธฐ๋ณธ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€์™€ ๊ธฐ๋ณธ ๊ณ„์ •์„ ํ•œ๊ฐœ๋ฅผ ์ œ๊ณตํ•ด์ค€๋‹ค.
    ์ฐธ๊ณ ๋กœ ๊ฐœ๋ฐœ์šฉ ๊ธฐ๋ณธ ๊ณ„์ •์€ application.properties์—์„œ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•˜๋‹ค. 
spring.security.user.name=user
spring.security.user.password=1234โ€‹
๊ณ„์ •์ด๋ฆ„์€ user

  • pom.xml (maven)
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
  • build.gradle (gradle)
implementation 'org.springframework.boot:spring-boot-starter-security'

 

 

๐Ÿ’ญ ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์˜ ๊ธฐ๋ณธ ๊ตฌ์กฐ

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์˜ ๊ฐ์ข… ์„ค์ •์€ HttpSecurity๋กœ ๋Œ€๋ถ€๋ถ„ ํ•˜๊ฒŒ ๋œ๋‹ค.

  • ๋ฆฌ์†Œ์Šค(URL) ์ ‘๊ทผ ๊ถŒํ•œ
  • ์„ค์ •์ธ์ฆ ์ „์ฒด ํ๋ฆ„์— ํ•„์š”ํ•œ Login, Logout ํŽ˜์ด์ง€ ์ธ์ฆ์™„๋ฃŒ ํ›„ ํŽ˜์ด์ง€ ์ธ์ฆ ์‹คํŒจ ์‹œ ์ด๋™ํŽ˜์ด์ง€ ๋“ฑ
  • ์„ค์ •์ธ์ฆ ๋กœ์ง์„ ์ปค์Šคํ…€ํ•˜๊ธฐ์œ„ํ•œ ์ปค์Šคํ…€ ํ•„ํ„ฐ
  • ์„ค์ •๊ธฐํƒ€ csrf, ๊ฐ•์ œ https ํ˜ธ์ถœ ๋“ฑ๋“ฑ ๊ฑฐ์˜ ๋ชจ๋“  ์Šคํ”„๋ง์‹œํ๋ฆฌํ‹ฐ์˜ ์„ค์ •

 

HttpSecurity ๋Š” WebSecurityConfigurer ๋ฅผ ํ†ตํ•ด ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

 

์ฆ‰, ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” WebSecurityConfigurer ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด์„œ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•ด์•ผํ•œ๋‹ค.

์ง์ ‘ ๋งŒ๋“ค๊ธฐ๋Š” ๋ฒˆ๊ฑฐ๋กœ์šฐ๋ฏ€๋กœ, ๋ณดํ†ต์€ ์ด๋ฅผ ๊ตฌํ˜„ํ•œ ์ถ”์ƒํด๋ž˜์Šค์ธ WebSecurityConfigurerAdapter ๋ฅผ ์ด์šฉํ•œ๋‹ค.

 

์Šคํ”„๋ง์€ ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋ฅผ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก, @EnableWebSecurity๋ผ๋Š” ์• ๋…ธํ…Œ์ด์…˜์„ ์ œ๊ณตํ•œ๋‹ค.

๊ทธ๋ƒฅ ์• ๋…ธํ…Œ์ด์…˜๋งŒ ๋ถ™์ธ๋‹ค๊ณ  ๋™์ž‘ํ•˜๋Š” ๊ฑด ์•„๋‹ˆ๊ณ , Spring ์‹œํ๋ฆฌํ‹ฐ ๋ชจ๋“ˆ ์˜์กด์„ฑ์— ์กด์žฌํ•˜๋Š” ์ƒํƒœ์—์„œ WebSecurityConfigurerAdapter ํด๋ž˜์Šค ์ƒ์†๋ฐ›์œผ๋ฉด ์•ฑ ์‹คํ–‰ ์‹œ์ ์—์„œ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋œ๋‹ค.

@EnableWebSecurity // @Configuration ์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋‹ค. ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋จ.
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .anyRequest().authenticated(); // ์–ด๋–ค ์š”์ฒญ์—๋„ ๋ณด์•ˆ์„ ๋ฐ›๋„๋ก ์ธ๊ฐ€์ •์ฑ… ์„ค์ •.

        http.formLogin(); // form-login ์ธ์ฆ ์ •์ฑ…
    }
}

 

SecurityConfig ์˜ˆ์ œ

๋”๋ณด๊ธฐ
@Configuration // ์ƒ๋žตํ•ด๋„ ์ƒ๊ด€์—†์ง€๋งŒ, ๋ช…์‹œ์ ์œผ๋กœ ํ‘œ์‹œํ•ด์ฃผ์ž
@EnableWebSecurity
public class SecuriyConfig extends WebSecurityConfigurerAdapter{
    
    /*
    	- Spring Security๋ฅผ ๋ฌด์‹œํ•  url์„ ์„ ์–ธ
    	- ๋ณดํ†ต ์ •์  ์ž์›์ด ์ €์žฅ๋œ ๊ฒฝ๋กœ๋ฅผ ๋ช…์‹œ
    */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web,ignoring().antMatchers("/webjars/**"); 
    }
	
    // - Spring Security ์ ์šฉ ๊ทœ์•ฝ ๋ช…์‹œ
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
		    .antMatchers("/**").permitAll() //๋ช…์‹œ๋œ ๊ฒฝ๋กœ์— ๋Œ€ํ•ด์„œ๋Š” ๋ชจ๋‘ ์ ‘๊ทผ ๊ฐ€๋Šฅ
			.anyRequest().authenticated();

		http.formLogin()
		   	.loginPage("/loginPage") //๋ช…์‹œ๋œ ๊ฒฝ๋กœ๋ฅผ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ์‚ฌ์šฉ
		   	.loginProcessingUrl("/login") //๋ช…์‹œ๋œ ๊ฒฝ๋กœ๋ฅผ ๋กœ๊ทธ์ธ url๋กœ ์‚ฌ์šฉ
		   	.usernameParameter("usrId") //ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ๋™์ผํ•œ name์„ ๊ฐ€์ง€๋Š” input ํƒœ๊ทธ๋กœ๋ถ€ํ„ฐ ์•„์ด๋””๊ฐ’์„ ๋ฐ›์•„์˜ด
			.passwordParameter("usrPw") //ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ๋™์ผํ•œ name์„ ๊ฐ€์ง€๋Š” input ํƒœ๊ทธ๋กœ๋ถ€ํ„ฐ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ’์„ ๋ฐ›์•„์˜ด
			.successHandler(successHandler()).failureHandler(failureHandler());

		http.logout()
			.logoutUrl("/logout") //๋ช…์‹œ๋œ ๊ฒฝ๋กœ๋ฅผ ๋กœ๊ทธ์•„์›ƒ url๋กœ ์‚ฌ์šฉ
			.logoutSuccessUrl("/") //๋กœ๊ทธ์•„์›ƒ ์„ฑ๊ณต์‹œ ๋ฆฌ๋””๋ ‰์…˜๋  url์„ ์ง€์ •
			.invalidateHttpSession(true) //์ธ์ฆ ๊ด€๋ จ ์„ธ์…˜ ์ •๋ณด ์‚ญ์ œ
			.deleteCookies("JSESSIONID"); //์ฟ ํ‚ค ์‚ญ์ œ

		http.exceptionHandling()
			.accessDeniedPage("/");  
    }
	
    // - ์‚ฌ์šฉ์ž ์ธ์ฆ ๋ฐฉ๋ฒ• ๋ช…์‹œ(์—ฌ๊ธฐ์„œ๋Š” ์ธ๋ฉ”๋ชจ๋ฆฌ๋ฐฉ์‹์„ ์‚ฌ์šฉ)
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            	.passwordEncoder(encoder())
            	.withUser("admin")
            	,password("admin")
            	.authroties("USER","ADMIN");
    }

    @Bean
    public BCryptPasswordEncoder encoder(){
        return new BCryptPasswordEncoder();
    }
    
    // Sucess, Failure Handler ๊ตฌํ˜„ํ•˜์—ฌ Bean์œผ๋กœ ๋“ฑ๋ก
    @Bean
    public AuthenticationSuccessHandler successHandler(){
        return new AuthSuccessHandler("/");
    }
    
    @Bean
    public AuthenticationFailureHandler failureHandler(){
        return new AuthFailureHandler();
    }
}

์ด์ฒ˜๋Ÿผ ์–ด๋Œ‘ํ„ฐ๋ฅผ ์ด์šฉํ•˜๊ฒŒ๋˜๋ฉด ๋‚ด๋ถ€์˜ configure() ๋ฉ”์„œ๋“œ๋งŒ ์ ์ ˆํ•˜๊ฒŒ ๋ฐ”๊ฟ”์„œ ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๋‹ค.

  • configure(WebSecurity web) : WebSecurity๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ํŠน์ • ์š”์ฒญ์„ ๋ฌด์‹œํ•˜๊ฑฐ๋‚˜ ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • configure(HttpSecurity http) : HttpSecurity๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ ‘๊ทผ๊ถŒํ•œ, ๋กœ๊ทธ์ธ ๋“ฑ๋“ฑ ์š”์ฒญ ๊ฒฝ๋กœ ์ง€์ •๊ฐ€๋Šฅ
  • configure(AuthenticationManagerBuilder) : ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด๋ฅผ ๊ตฌ์„ฑํ•œ๋‹ค. in-memory ๊ธฐ๋ฐ˜ ์œ ์ €๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฌผ๋ก  ์Šคํ”„๋ง๋ถ€ํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด apllication.yml์—์„œ๋„ ์„ค์ •์ด ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ๋ชจ๋“  ์„ค์ •์„ yml์—์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†๊ธฐ์— ๋ณดํ†ต ์œ„์˜ ์ฝ”๋“œ์ฒ˜๋Ÿผ Configuration Class๋ฅผ ๋งŒ๋“ค์–ด์„œ ํ•จ๊ป˜ ์‚ฌ์šฉํ•œ๋‹ค.

 

WebSecurityConfigurerAdapter ๋ฅผ ํ†ตํ•ด HttpSecurity ๊ฐ์ฒด๊ฐ€ ๋งŒ๋“ค์–ด์ง„๋‹ค.
๊ทธ๋ฆฌ๊ณ  WebSecurityConfigurerAdapter ์•ˆ์˜ configure ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด HttpSecurity ๊ฐ์ฒด์˜ ๊ธฐ๋ณธ ๋ณด์•ˆ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

๐Ÿ“‘ ์–ด? ๊ทธ๋Ÿฐ ์„ค์ •์—†์ด๋„ ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋Š” ๋™์ž‘ํ•˜๋˜๋ฐ์š”?

๊ทธ๊ฑด ์šฐ๋ฆฌ๊ฐ€ ์Šคํ”„๋ง ๋ถ€ํŠธ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์—ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

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

์Šคํ”„๋ง๋ถ€ํŠธ๋Š” SecurityAutoConfiguration ํด๋ž˜์Šค๋ฅผ ์„ค์ •ํŒŒ์ผ๋กœ ์ฐธ๊ณ ํ•˜๋Š”๋ฐ,

ํ•ด๋‹น ๊ฐ์ฒด๋Š” DefaultAuthenticationEventPublisher๋ฅผ @Bean์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค.

@Configuration
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
@Import({SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
    SecurityDataConfiguration.class})
public class SecurityAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(AuthenticationEventPublisher.class)
    public DefaultAuthenticationEventPublisher authenticationEventPublisher(
        ApplicationEventPublisher publisher) {
        return new DefaultAuthenticationEventPublisher(publisher);
    }

}

์ด๋ ‡๊ฒŒ ๋“ฑ๋ก๋œ publisher๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๊ธฐ๋ณธ ์ด๋ฒคํŠธ๋ฅผ ๋งคํ•‘ํ•ด์„œ, ์ธ์ฆ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.

  • BadCredentialsException
  • UsernameNotFoundException
  • AccountExpiredException ๋“ฑ๋“ฑ..

ํ•˜์ง€๋งŒ ์Šคํ”„๋ง ๋ถ€ํŠธ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ํ•ด๋„, ๊ฒฐ๊ตญ ๋™์ž‘์›๋ฆฌ๋Š” ๊ฐ™๋‹ค. ์Šคํ”„๋ง ๋ถ€ํŠธ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์‹œํ๋ฆฌํ‹ฐ ์„ค์ •๊ฐ์ฒด๋ฅผ ๋ณด๋ฉด ์œ„์—์„œ ์„ค๋ช…ํ•œ ๊ฒƒ ์ฒ˜๋Ÿผ WebSecurityConfigurerAdapter๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

@Configuration
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class SpringBootWebSecurityConfiguration {

    @Configuration
    @Order(SecurityProperties.BASIC_AUTH_ORDER)
    static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {

    }

}

 

์ฐธ๊ณ ๋กœ ์•ฑ์ด ์‹คํ–‰๋  ๋•Œ ์ƒ๊ธฐ๋Š” ๊ธฐ๋ณธ user ๊ณ„์ •์€ UserDetailsService๋ฅผ ์ƒ์†๋ฐ›์•„์„œ ์žฌ์ •์˜ํ•˜๋ฉด ๋งŒ๋“ค์–ด์ง€์ง€ ์•Š๋Š”๋‹ค.

๊ถ๊ธˆํ•˜๋ฉด UserDetailsServiceAutoConfiguration ๊ฐ์ฒด๋ฅผ ๊ตฌ๊ธ€์— ๊ฒ€์ƒ‰ํ•ด์„œ ์ฐพ์•„๋ณด์ž.

 

 

๐Ÿ’ญ ์‹œํ๋ฆฌํ‹ฐ ์‚ฌ์šฉํ•ด๋ณด๊ธฐ - ์„ธ์…˜๊ธฐ๋ฐ˜ ์›น ๋กœ๊ทธ์ธ

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋ฅผ ์ฒ˜์Œ ์ ์šฉํ•˜๋ฉด, ์•„๋ž˜์ฒ˜๋Ÿผ ์ „ํ†ต์ ์ธ ์„ธ์…˜ ์›น ๋กœ๊ทธ์ธ ๋ฐฉ์‹์˜ form ์ธ์ฆ์„ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•ด์ค€๋‹ค.

์ด๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด configure ๋ฉ”์„œ๋“œ์•ˆ์—์„œ, http.formLogin() ์„ค์ •์„ ํ†ตํ•ด ์‰ฝ๊ฒŒ ์ปค์Šคํ…€์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

@EnableWebSecurity // @Configuration ์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋‹ค. ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋จ.
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest()
                .authenticated()
        .and() // ์ด๋ ‡๊ฒŒ and()๋ฅผ ์ด์šฉํ•ด์„œ ์ธ์ฆ, ์ธ๊ฐ€๋ฅผ ํ•œ๋ฒˆ์— ์„ค์ •ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
            .formLogin()
                .loginPage("/loginPage") // ๋‚ด๊ฐ€ ๋งŒ๋“  ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€
                .defaultSuccessUrl("/") // ๋กœ๊ทธ์ธ ์„ฑ๊ณต์‹œ ์ด๋™ ๊ฒฝ๋กœ
                .failureUrl("/login.html?error=true") // ๋กœ๊ทธ์ธ ์‹คํŒจ์‹œ ์ด๋™ ๊ฒฝ๋กœ
                .loginProcessingUrl("/login_proc") // form ํƒœ๊ทธ์˜ Action URL. ๊ธฐ๋ณธ๊ฐ’์€ "/login"
                .usernameParameter("userId")
                .passwordParameter("passwd")
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request,
                        HttpServletResponse response, Authentication authentication)
                        throws IOException, ServletException {

                        System.out.println("์ธ์ฆ์„ฑ๊ณต(authentication) : " + authentication.getName());
                        response.sendRedirect("/");
                    }
                })
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request,
                        HttpServletResponse response, AuthenticationException exception)
                        throws IOException, ServletException {

                        System.out.println("์ธ์ฆ์‹คํŒจ(exception) : "+exception.getMessage());
                        response.sendRedirect("/login");
                    }
                })
                .permitAll() // loginForm ์ ‘์† ํ—ˆ์šฉ. ์ด๊ฒŒ ์—†์œผ๋ฉด loginPage๋„ ์ธ์ฆ์—†์ด๋Š” ์š”์ฒญํ•˜์ง€ ๋ชปํ•œ๋‹ค.
        ;

    }
}

์ฐธ๊ณ ๋กœ Login HTML ํผ์„ ์„ค์ • ๊ฐ’๊ณผ ๋‹ค๋ฅด๊ฒŒ ์ž‘์„ฑํ•œ๋‹ค๋ฉด Bad crednetials ๋“ฑ์˜ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.

๋‹น์—ฐํžˆ HTML์„ ์ž‘์„ฑํ•  ๋•Œ, ๊ฐ’์„ ์Šคํ”„๋ง ์„ค์ •๊ณผ ๋™์ผํ•˜๊ฒŒ ๋งž์ถฐ์ฃผ์–ด์•ผํ•œ๋‹ค.

 

๋‚˜์ค‘์— ์–ธ๊ธ‰ํ• ๊ฑฐ์ง€๋งŒ, ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ (@EnableWebSecurity)๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ CSRF ๊ณต๊ฒฉ๋ฐฉ์–ด๋“ฑ์˜ ๋‹ค์–‘ํ•œ ๋ณด์•ˆ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค. ์ด๋Ÿฐ๊ฒƒ๋“ค๋„ configure ์—์„œ http ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์‰ฝ๊ฒŒ ์„ค์ •์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค.

CSRF (Cross-Site Request Forgery)๊ฐ€ ๋ญ”๋ฐ์š”?

๋”๋ณด๊ธฐ
์ง์—ญํ•˜๋ฉด '์‚ฌ์ดํŠธ ๊ฐ„ ๊ณต๊ฒฉ ์œ„์กฐ'๋ผ๊ณ  ๋ถˆ๋ฆฌ๋ฉฐ, ์ธ์ฆ(๋กœ๊ทธ์ธ) ํ›„ ์ด์šฉ์ž์˜ ์˜๋„ํ•˜์ง€ ์•Š์€ ์š”์ฒญ์„ ํ†ตํ•œ ๊ณต๊ฒฉ์„ ๋งํ•œ๋‹ค.

์ฆ‰ ์‚ฌ์šฉ์ž์˜ ์ธ์ฆ ์ดํ›„, ์Šคํฌ๋ฆฝํŠธ๋‚˜ ํผ ํƒœ๊ทธ๋ฅผ ํ†ตํ•ด ํŠน์ • ์›น์‚ฌ์ดํŠธ์— ๋ณธ์ธ์ด ์˜๋„ํ•˜์ง€ ์•Š์€ ์š”์ฒญ์„ ํ•˜๋„๋ก ๋งŒ๋“œ๋Š” ๊ณต๊ฒฉ์ด๋‹ค. ์‹ค์ œ ํ•œ๊ตญ์—์„œ๋„ 2008๋…„ ์˜ฅ์…˜์˜ ๊ฐœ์ธ์ •๋ณด ์œ ์ถœ์‚ฌ๊ฑด์— CSRF ๊ณต๊ฒฉ์„ ์ด์šฉํ•ด ๊ด€๋ฆฌ์ž๊ณ„์ •์„ ํƒˆ์ทจํ•˜์˜€๋‹ค.

 

์‰ฝ๊ฒŒ๋งํ•˜๋ฉด ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž์—๊ฒŒ ์•„๋ž˜์™€ ๊ฐ™์€ HTML ์ฝ”๋“œ๋ฅผ ์ด์šฉํ•ด ํ•ด์ปค๊ฐ€ ์›ํ•˜๋Š” ์„œ๋ฒ„ ์š”์ฒญ์„ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ๊ฒƒ์ด๋‹ค.

<!-- ์‚ฌ์šฉ์ž๊ฐ€ ํ•ด๋‹น ๋งํฌ๋ฅผ ํด๋ฆญํ•˜๋ฉด ์š”์ฒญ์ด ๋ฐœ์ƒ๋จ -->
<a href="http://bank.com/transfer?accountNumber=5678&amount=10000">
Pictures@
</a>

<!-- ํŽ˜์ด์ง€ ๋กœ๋“œ์‹œ ์ด๋ฏธ์ง€๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ์œ„ํ•ด ํ•ด๋‹น ๋งํฌ๋ฅผ ์‚ฌ์šฉ์ž ๊ณ„์ •์œผ๋กœ ์š”์ฒญํ•˜๊ฒŒ๋จ -->
<img src="http://bank.com/transfer?accountNumber=5678&amount=10000"/>

๋ฌผ๋ก  1990๋…„๋Œ€ ์›น๋„ ์•„๋‹ˆ๊ณ , ์œ„์™€ ๊ฐ™์€ Get ์š”์ฒญ์œผ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ์ผ์€ ์—†๋‹ค.
ํ•˜์ง€๋งŒ ์•„๋ž˜์™€ ๊ฐ™์ด Post ์š”์ฒญ๋„ HTML form์„ ์ด์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์ธ๋ผ์ธ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ด์šฉํ•ด์„œ ์š”์ฒญ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

<!-- Post๋ฅผ ์ด์šฉํ•˜๋Š” Form ํƒœ๊ทธ๋„ ์•„๋ž˜์™€ ๊ฐ™์ด HTML ์ธ๋ผ์ธ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ํ†ตํ•ด ๊ณต๊ฒฉ๊ฐ€๋Šฅ. -->
<body onload="document.forms[0].submit()">
<form action="http://bank.com/transfer" method="POST">
    <input type="hidden" name="accountNumber" value="5678"/>
    <input type="hidden" name="amount" value="10000"/>
    <input type="submit" value="Pictures@"/>
</form>
...

 

์ด๋Ÿฌํ•œ CSRF๋ฅผ ๋ง‰์„๋ ค๊ณ  Http์˜ Referrer ํ—ค๋”๋ฅผ ๊ฒ€์ฆํ•ด์„œ ์š”์ฒญ์ด ๊ธฐ์กด domain url์ด๋ž‘ ๋™์ผํ•œ์ง€ ๊ฒ€์ฆํ•˜๊ธฐ๋„ ํ•œ๋‹ค. ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋Š” CSRF Token์„ ๋ฐœ๊ธ‰ํ•˜์—ฌ ํ† ํฐ๊ณผ ํ•จ๊ป˜ ์š”์ฒญ์„ ๋ฐ›์•„ ๊ณต๊ฒฉ์„ ๋ฐฉ์ง€ํ•œ๋‹ค.

(* X-XSRF-TOKEN ๊ฐ’์ด ์—†๋‹ค๋ฉด 403 Forbidden์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๊ธฐ๋ณธ์œผ๋กœ ์„ค์ •๋˜์–ด์žˆ๋‹ค.)

 

๊ทธ๋Ÿฌ๋‚˜ ์œ„์˜ ์˜ˆ์ œ์—์„œ๋„ ๋ณด๋‹ค์‹ถ์ด, CSRF๋Š” ์„œ๋ฒ„ ์ธก์—์„œ HTML์„ ์ƒ์„ฑํ•˜๋Š” ๊ตฌ์กฐ (Thymeleaf, JSP)์—์„œ๋งŒ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์ด๋‹ค.

 

REST API์ฒ˜๋Ÿผ ๋งค๋ฒˆ HTML ํŽ˜์ด์ง€๋ฅผ ์ฃผ๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ API๋งŒ ๋…ธ์ถœํ•˜๊ณ  JSON์œผ๋กœ ํ†ต์‹ ํ•˜๋Š” ๊ฒฝ์šฐ ์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— CSRF๋Š” ์ „ํ˜€ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š๋Š”๋‹ค. ์ฆ‰ ์ถ”๊ฐ€์ ์ธ CSRF ๋ณด์•ˆ์„ ํ•  ํ•„์š”๊ฐ€ ์ „ํ˜€์—†๊ธฐ์— http.csrf.disable()๋กœ ๋น„ํ™œ์„ฑํ™” ํ•ด์ฃผ๋Š”๊ฒŒ ์ข‹๋‹ค. โžก permitAll()์„ ํ–ˆ๋Š”๋ฐ 403 forbidden์ด ๋œฌ๋‹ค๋ฉด ์ด๋ฅผ ํ™•์ธํ•ด๋ณด์ž.

 

 

 

๐Ÿ’ญ ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์˜ ๋™์ž‘๊ณผ์ •

๊ธฐ๋ณธ์ ์œผ๋กœ ์ž๋ฐ” ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ์˜ ๊ฒฝ์šฐ, FilterChain์„ ๊ตฌ์„ฑํ•˜์—ฌ ์„œ๋ธ”๋ฆฟ์ด ์‹คํ–‰๋˜๊ธฐ ์ „ ํ•„ํ„ฐ๋ฅผ ๊ฑฐ์น˜๊ฒŒ ๋œ๋‹ค.

 

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์˜ ๊ฒฝ์šฐ ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ์ฒด์ธ์— DelegatingFilterProxy๋ฅผ ๋“ฑ๋กํ•œ๋‹ค. ์ด๋Š” ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ์˜ ๊ตฌํ˜„์ฒด์ด๋‹ค.

๊ทธ๋ฆฌ๊ณ  ํ•ด๋‹น ํ•„ํ„ฐ๊ฐ€ SecurityFilterChain์„ ํ˜ธ์ถœํ•˜๊ณ , ์Šคํ”„๋ง์— ๋“ฑ๋ก๋œ ๋นˆ์„ ๊ฐ€์ ธ์™€์„œ ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•˜๊ฒŒ ๋œ๋‹ค.  

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
    // Lazily get Filter that was registered as a Spring Bean
    // For the example in DelegatingFilterProxy delegate is an instance of Bean Filter0
    Filter delegate = getFilterBean(someBeanName);
    // delegate work to the Spring Bean
    delegate.doFilter(request, response);
}

DelegatingFilterProxy ๋Š” ์Šคํ”„๋ง ๋นˆ์ด ์•„๋‹Œ, ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ๋ผ์„œ ์‹ค์ œ ๋™์ž‘์€ FilterChainProxy์— ์œ„์ž„ํ•˜๊ฒŒ๋œ๋‹ค.

  • FilterChainProxy๋Š” ์š”์ฒญ๊ณผ ์‘๋‹ต์„ ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ํ•„ํ„ฐ ์ฒด์ธ์— ์œ„์ž„ํ•˜๋Š” ์—ญํ• ์„ ๋‹ด๋‹นํ•œ๋‹ค.
    [์„œ๋ธ”๋ฆฟ โžก ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์˜ ์ง„์ž…์ ]์ด๋ฉฐ, ์„œ๋ธ”๋ฆฟ์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด ํ•ด๋‹น ๊ฐ์ฒด์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

์ฐธ๊ณ ๋กœ SecurityFilterChain์€ ์—ฌ๋Ÿฌ ๊ฐœ๋กœ ๊ตฌ์„ฑํ•  ์ˆ˜๋„ ์žˆ๋‹ค.&amp;amp;nbsp;

  • ์ฐธ๊ณ ๋กœ SecurityFilterChain์€ ์—ฌ๋Ÿฌ ๊ฐœ๋กœ ๊ตฌ์„ฑํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
    ์ฆ‰ ์•„๋ž˜ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ DelegatingFilterProxy์—์„œ URL์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๊ฐ€ ์ž‘๋™ํ•˜๋„๋ก ๊ตฌ์„ฑํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
    ์ด๋ฏธ์ง€ ์ถœ์ฒ˜ :&amp;amp;nbsp;https://limdevbasic.tistory.com/19
    FilterChainProxy ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ์‹œํ๋ฆฌํ‹ฐ ํ•„ํ„ฐ์ฒด์ธ์„ ๋ฆฌ์ŠคํŠธ๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

์‹œํ๋ฆฌํ‹ฐ ํ•„ํ„ฐ๋Š” SecurityFilterChain API๋ฅผ ํ†ตํ•ด FilterChainProxy์— ์‚ฝ์ž…๋˜๊ณ , ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋œ๋‹ค.

์ฆ‰, ํ•„ํ„ฐ๋กœ ๋™์ž‘์€ ํ•˜์ง€๋งŒ '์‹œํ๋ฆฌํ‹ฐ ํ•„ํ„ฐ' ์ž์ฒด๊ฐ€ ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ๋กœ ๋ฐ”๋กœ ๋“ฑ๋ก๋˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ๋Š” ๊ฒƒ์„ ์ดํ•ดํ•˜๊ณ  ์žˆ์ž.

 

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋งŽ์€ ํ•„ํ„ฐ๋ฅผ ์ œ๊ณตํ•ด์ฃผ๋Š”๋ฐ, ์ ์šฉ๋˜๋Š” ์ˆœ์„œ์™€ ์ข…๋ฅ˜๋Š” ๊ณต์‹๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์ž.

์•Œ๊ณ ์žˆ์œผ๋ฉด ์ข‹๊ธดํ•œ๋ฐ, ์›Œ๋‚™ ์ข…๋ฅ˜๊ฐ€ ๋งŽ๊ธฐ๋„ํ•˜๊ณ  ๋ชจ๋ฅธ๋‹ค๊ณ ํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š”๋ฐ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ์ง€๋Š” ์•Š๋Š”๋‹ค.

๋””๋ฒ„๊ทธ๋ฅผ ์ฐ์–ด๋ณด๋ฉด, http.formLogin()์€ 13๊ฐœ์˜ ์‹œํ๋ฆฌํ‹ฐ ํ•„ํ„ฐ์ฒด์ธ์ด ์ˆœ์„œ๋Œ€๋กœ ๋™์ž‘ํ•จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

 

์กฐ๊ธˆ ๋” ์ž์„ธํ•œ ๋™์ž‘์›๋ฆฌ๋Š” ํ•ด๋‹น ๋ธ”๋กœ๊ทธ๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด ์ดํ•ดํ•˜๋Š”๋ฐ ๋„์›€์ด ๋  ๊ฒƒ ๊ฐ™๋‹ค.

 

 

๐Ÿ“‘ Login-Form์€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ–ˆ์„๊นŒ

Login-Form์—๋Š” ์ด๋ฏธ ๋งŒ๋“ค์–ด์ ธ์žˆ๋Š” UsernamePasswordAuthenticationFilter๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋™์ž‘ํ•˜๋Š”๋ฐ, ํ•ด๋‹น ์‹œํ๋ฆฌํ‹ฐ ํ•„ํ„ฐ์˜ ๋™์ž‘๊ตฌ์กฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

์ฐธ๊ณ ๋กœ Username...Filter๋Š” AbstractAuthenticationProcessingFilter๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฒƒ์ด๋ฉฐ, ์ด๋Š” ์‹œํ๋ฆฌํ‹ฐ ํ•„ํ„ฐ ์ฒด์ธ์— ๋“ฑ๋ก๋˜์–ด ๋™์ž‘ํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ ์ธ์ฆ์— ์„ฑ๊ณตํ•˜๋ฉด, SecurityContext์— ์ธ์ฆ ๊ฐ์ฒด๊ฐ€ ์ €์žฅ๋˜๋ฉฐ, ๋‚˜์ค‘์— ์ธ์ฆ์ด ํ•„์š”ํ•  ๋•Œ ์ด๋ฅผ ๊บผ๋‚ด ์“ธ ์ˆ˜ ์žˆ๊ฒŒ๋œ๋‹ค.

 

์กฐ๊ธˆ ๋” ์ž์„ธํ•œ ํด๋ž˜์Šค ๊ตฌ์กฐ(Spring Security Flow Communication Diagram)

 

 

๐Ÿ“‘ Logout์€ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•˜์ฃ ?

๋กœ๊ทธ์•„์›ƒ๋„ LogoutFilter๋ฅผ ํ†ตํ•ด ๋™์ž‘ํ•œ๋‹ค.

์‚ฌ์šฉ๋ฒ•์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

์ฐธ๊ณ ๋กœ ์ต๋ช…ํด๋ž˜์Šค ๋Œ€์‹  ๋žŒ๋‹ค์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ํ›จ์”ฌ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

@EnableWebSecurity // @Configuration ์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋‹ค. ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋จ.
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .anyRequest()
            .authenticated();

        http.formLogin()
        .and()// ๋ฌผ๋ก  http.logout()์œผ๋กœ ๋”ฐ๋กœ ์ž‘์„ฑํ•ด๋„ ๋จ.    
        .logout()
            .logoutUrl("/logout") // ๋กœ๊ทธ์•„์›ƒ ์š”์ฒญ URL
            .logoutSuccessUrl("/login") // ๋กœ๊ทธ์•„์›ƒ ์„ฑ๊ณต ํ›„ ์ด๋™
            .deleteCookies("JSESSIONID","remember-me") // ๋กœ๊ทธ์•„์›ƒ ํ›„ ์„ธ์…˜์ฟ ํ‚ค ์‚ญ์ œ
            .addLogoutHandler(new LogoutHandler() {
                // handler ๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•ด์„œ ์ถ”๊ฐ€์ ์ธ ๋กœ๊ทธ์•„์›ƒ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅ.
                @Override
                public void logout(HttpServletRequest request, HttpServletResponse response,
                    Authentication authentication) {
                    System.out.println("Logout ์ฒ˜๋ฆฌ ํ•ธ๋“ค๋Ÿฌ");
                    HttpSession session = request.getSession();
                    session.invalidate();
                }
            })
            .logoutSuccessHandler(new LogoutSuccessHandler() {
                @Override
                public void onLogoutSuccess(HttpServletRequest request,
                    HttpServletResponse response, Authentication authentication)
                    throws IOException, ServletException {
                    System.out.println("๋กœ๊ทธ์•„์›ƒ ์ดํ›„");
                    response.sendRedirect("/login");
                }
            });
    }
}

 

๋™์ž‘๊ณผ์ •์€ ๋กœ๊ทธ์ธ๊ณผ ๋น„์Šทํ•˜๋‹ค.

 

 

 

๐Ÿ“‘ ๋งˆ์ง€๋ง‰, Remember Me ๊ธฐ๋Šฅ ์‚ฌ์šฉํ•˜๊ธฐ

์ฐธ๊ณ ๋กœ ์ด๋Š” ์„ธ์…˜์ด ๋งŒ๋ฃŒ๋˜๊ณ , ์›น ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ข…๋ฃŒ๋œ ํ›„์—๋„ ์•ฑ์ด ์‚ฌ์šฉ์ž๋ฅผ ๊ธฐ๋กํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

์‚ฌ์šฉ์ž๊ฐ€ Remember-me ์ฟ ํ‚ค๋ฅผ ์š”์ฒญ์— ๋ณด๋‚ด๋ฉด, ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•˜๊ณ  ๋ณ„๋„์˜ ์ธ์ฆ๊ณผ์ • ์—†์ด ๋กœ๊ทธ์ธ์„ ์œ ์ง€ํ•ด์ค€๋‹ค.

์ฒดํฌ๋ฐ•์Šค๋ฅผ ํ™œ์„ฑํ™”๋ฉด, ์„œ๋ฒ„์—์„œ JSESSIONID์™€ ํ•จ๊ป˜ Remeber-ME ํ† ํฐ์„ ์ฟ ํ‚ค๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

Login๊ณผ Logout์„ ๋ด์„œ ์ด๋ฏธ ์•Œ๊ฒ ์ง€๋งŒ, ์ด๊ฒƒ๋„ configure ๋ฉ”์„œ๋“œ์˜ http๋ฅผ ์ด์šฉํ•ด์„œ ์‰ฝ๊ฒŒ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๋‹ค.

 

 

์„ธ์…˜ ์ฟ ํ‚ค๊ธฐ๋ฐ˜ ๋กœ๊ทธ์ธ์—์„œ, ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋กœ๊ทธ์ธ์— ์„ฑ๊ณตํ•˜๋ฉด JSessionID์™€ ํ•จ๊ป˜ Remember-me ์ฟ ํ‚ค๋ฅผ ํ•จ๊ป˜ ์ „๋‹ฌํ•œ๋‹ค.

ํ•ด๋‹น remeber-me ํ† ํฐ์—๋Š” [์•”ํ˜ธํ™”๋œ UserID, Password]์™€ ๋งŒ๋ฃŒ์ผ์ด ์ ํ˜€์žˆ๋‹ค. 

1. ์›น๋ธŒ๋ผ์šฐ์ €๋ฅผ ์ข…๋ฃŒํ•ด์„œ JSESSIONID๊ฐ€ ์—†๊ฑฐ๋‚˜ ์„œ๋ฒ„์˜ ์„ธ์…˜์ด ๋งŒ๋ฃŒ๋˜์—ˆ๋”๋ผ๋„

2. remember-me ํ† ํฐ์„ ์ฟ ํ‚ค๋กœ ๋ณด๋‚ด๋ฉด

3. ํ˜„์žฌ Security Session โžก Security Context์— ์ผ์น˜ํ•˜๋Š” ํ† ํฐ์ด ์žˆ๋Š”์ง€ ๋น„๊ตํ•˜๊ณ , ์žˆ๋‹ค๋ฉด ์ƒˆ๋กœ์šด ์ธ์ฆ๊ฐœ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ƒˆ๋กœ์šด JSESSIONID๋ฅผ ์‘๋‹ต์— ๋ณด๋‚ธ๋‹ค.

๋ฌผ๋ก  [์„œ๋ฒ„์˜ ์„ธ์…˜ ๋ฉ”๋ชจ๋ฆฌ or ์„œ๋ฒ„ ํ† ํฐ DB]์— ์œ ํšจํ•œ ํ† ํฐ์ด ์žˆ์–ด์•ผํ•œ๋‹ค. ๊ทธ๊ฒƒ๋„ ์—†๋‹ค๋ฉด ๋‹น์—ฐํžˆ ๋กœ๊ทธ์ธ๋˜์ง€ ์•Š๋Š”๋‹ค.

RemeberMe ํ•„ํ„ฐ๋Š” ํ˜„์žฌ ์ธ์ฆ๊ฐœ์ฒด๊ฐ€ null์ธ ๊ฒฝ์šฐ, [remeber-me ํ† ํฐ]์„ ์ด์šฉํ•ด์„œ ๋‹ค์‹œ ๋กœ๊ทธ์ธ์„ ์‹œ๋„ํ•œ๋‹ค.&amp;amp;nbsp;

 

 

 

 

 

๐Ÿ’ญ ์ •๋ฆฌ

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋Š” ์–ด๋–ค ์‹์œผ๋กœ ๋™์ž‘ํ•˜๋Š”์ง€, ๋Œ€๋žต์ ์ธ ๊ฐ์„ ์žก์•„๋ณด์•˜๋‹ค.

๋‹ค์Œ ๊ธ€์—์„œ๋Š” ์„ธ์…˜์ œ์–ดํ•„ํ„ฐ, ์˜ˆ์™ธ์ฒ˜๋ฆฌ ํ•„ํ„ฐ๋“ฑ์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๋„๋ก ํ•˜์ž.

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

JiwonDev

JiwonDev

ํ™œ๋™ํ•˜๊ธฐ