- SpringBoot๊ธฐ์ด
- Spring Security Architecture
- Thymeleaf ๊ธฐ์ด
- Thymeleaf Security
- Thymeleaf layout dialet
- ํ์ผ์ ๋ก๋
- ์ปดํฌ๋ํธ์ค์บ
- ๋น ์ถฉ๋ ์ํฉ
- ๋ผ์ด๋ธํ ํ๋ฆฟ ์ธํ
@SpringBootApplication
์ ์๋ ์ด๋
ธํ
์ด์
์ ํฌํจํ๊ณ ๋ ํธ๋ฆฌํ ์ด๋
ธํ
์ด์
์ด๋ค.
@Configuration
: application context์๋ํ ๋น ์ ์์ ์์ค๋ก, ํด๋์ค์ ํ๊ทธ๋ฅผ ํ๋ค.@EnableAutoConfiguration
: ํด๋์ค ํจ์ค ์ธํ , ๋ค๋ฅธ ๋น๋ค๊ณผ ๋ค์ํ ํ๋กํผํฐ ์ธํ ์ ๊ธฐ๋ฐ์ผ๋ก ๋น์ ๋ฑ๋กํ๊ฒ ํ๋ค.
์๋ฅผ๋ค์ด spring-webmvc ๊ฐ ํด๋์คํจ์ค์ ์์ผ๋ฉด, ์ด ์ด๋ ธํ ์ด์ ์ ์น ์ดํ๋ฆฌ์ผ์ด์ ์ผ๋ก ํ๋๊ทธํ๊ณ , DispatcherServlet์ธํ ์๊ณผ ๊ฐ์ ํต์ฌ ๋์์ ํ์ฑํํ๋ค.
@ComponentScan
: ์คํ๋ง์ด ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ค๊ณผ, ์ค์ ๋ค๊ณผ ์๋น์ค๋ค์ ์ปจํธ๋กค๋ฌ์๊ฒ ์ฐพ๋๋ก ํ๋ค.
gradle ๊ธฐ๋ฐ
./gradlew bootRun
maven
./mvnw spring-boot:run
MockMvc
๋ Spring Test์์ ์๋ค. ์ด๊ฒ์ ํธ๋ฆฌํ ๋น๋ ํด๋์ค๋ค๊ณผ HTTP request๋ค์ DispatcherServlet์ ๋ณด๋ผ ์ ์๊ฒํ๋ค.
๊ทธ๋ฆฌ๊ณ ๊ฒฐ๊ณผ์ ๋ํ assertions์ ๋ง๋ค์ด์ค๋ค.
MockMvc๋ฅผ ์ฃผ์
ํ๊ธฐ์ํด ์ฌ์ฉ๋ @AutoConfigureMockMvc
์ @SpringBootTest
์ฌ์ฉ์ ๊ธฐ๋กํ์.
@SpringBootTest
๋ฅผ ์ฌ์ฉํ๋ฉด, ์ ์ฒด ์ดํ๋ฆฌ์ผ์ด์
์ปจํ
์คํธ๊ฐ ์์ฑ๋๋๋ก ์์ฒญํ๋ค.
๋์์ผ๋ก @WebMvcTest
๋ฅผ ์ฌ์ฉํ๋ฉด ์ปจํ
์คํธ์ ์น ๊ณ์ธต๋ง ์์ฑํ๋๋ก ์คํ๋ง๋ถํธ์ ์์ฒญํ๋ค.
์ด ๋๊ฐ์ง ๊ฒฝ์ฐ ๋ชจ๋, ์คํ๋ง ๋ถํธ๋ ์๋์ผ๋ก ์ดํ๋ฆฌ์ผ์ด์ ์ ๋ฉ์ธ ์ดํ๋ฆฌ์ผ์ด์ ํด๋์ค๋ก ์์นํ๋ ค ์๋ํ๋ค.
ํ์ง๋ง ๊ฐ๋ฐ์๋ ๋ฎ์ด์ฐ๊ฑฐ๋, ์ฝ๊ฐ์ ๋ค๋ฅธ ์ค์ ์ ํ ์ ์๊ฒ ํ๋ค.
๋ฟ๋ง์๋๋ผ, ์คํ๋ง๋ถํฌ๋ฅผ ์ฌ์ฉํด ํ์คํ ํตํฉํ ์คํธ๋ฅผ ์์ฑํ ์ ์๊ฒ, HTTP Request cycle์ ๋ชจํนํ๋ค.
๋ด์ฅ์๋ฒ๋ webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
๋๋ฌธ์ ๋๋ค ํฌํธ๋ก ์์๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ค์ ํฌํธ๋ ์๋์ผ๋ก TestRestTemplate๋ฅผ ์ํ ๊ธฐ๋ณธURL์ ์ค์ ๋๋ค.
์ค๋ถ๋ health, audits, beans,..๋ฑ์ ์๋น์ค๋ actuator module๊ณผ ํจ๊ป ์ ๊ณตํ๋ค.
implementation 'org.springframework.boot:spring-boot-starter-actuator'
์ด ์์กด์ฑ์ ์ถ๊ฐํ๊ณ bootRun์ ์คํํ๋ฉด, ์๋ก์ด RESTful ์๋ํฌ์ธํธ๋ค์ด ์ดํ๋ฆฌ์ผ์ด์ ์ ์ถ๊ฐ๋๊ฑธ ๋ณผ ์ ์๋ค.
management.endpoint.configprops-org.springframework.boot.actuate.autoconfigure.context.properties.ConfigurationPropertiesReportEndpointProperties
management.endpoint.env-org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointProperties
management.endpoint.health-org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties
management.endpoint.logfile-org.springframework.boot.actuate.autoconfigure.logging.LogFileWebEndpointProperties
management.endpoints.jmx-org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointProperties
management.endpoints.web-org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties
management.endpoints.web.cors-org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties
management.health.diskspace-org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthIndicatorProperties
management.info-org.springframework.boot.actuate.autoconfigure.info.InfoContributorProperties
management.metrics-org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties
management.metrics.export.simple-org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleProperties
management.server-org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties
actuator๋ ๋ค์์ ๋ ธ์ถํ๋ค.
- actuator/health
- actuator
/actuator/shutdown
์๋ํฌ์ธํธ๋ ์๋ค, ํ์ง๋ง ์ด๋ JMX1๋ฅผ ํตํด์๋ง ๋ณผ ์ ์๋ค.
์ด๋ฅผ HTTP ์ํธํฌ์ธํธ๋ก ๋ง๋ค๊ธฐ์ํด management.endpoint.shutdown.enable=true
๋ฅผ ์ดํ๋ฆฌ์ผ์ด์
ํ๋กํผํฐ์ ์ถ๊ฐํ์.
๊ทธ๋ฆฌ๊ณ management.endpoints.web.exposure.include=health,info,shutdown
์๊ฒ๋ ๋
ธ์ถ์ํค์.
๊ทธ๋ฌ๋, ๋๋ ๊ณต๊ฐ ์ดํ๋ฆฌ์ผ์ด์
์์ shutdown ์๋ํฌ์ธํธ๋ฅผ ํ์ฑํํ๋ฉด ์๋ ์๋ ์๋ค.
์๋๋๋ฐ?
SpringBoot Loader Module2 ๋๋ถ์ ๊ธฐ์กด์ WAR ํ์ผ ๋ฐฐํฌ๋ฅผ ์ง์ํ ๋ฟ๋ง ์๋๋ผ ์คํ ๊ฐ๋ฅํ JAR๋ฅผ ํจ๊ป ๋ฃ์ ์ ์๋ค.
spring-boot-gradle-plugin
, spring-boot-maeven-plugin
์ ํตํด ์ด๋์ ๋ฐ๋ชจ๋ฅผ ์ง์ํ๋ ๋ค์ํ ๊ฐ์ด๋๊ฐ์๋ค.
ํ๋์ ํ์ผ๋งํผ ์์ Spring MVC web ์ดํ๋ฆฌ์ผ์ด์ ์ ๋น๋ํ ์ ์๊ฒ, ์ค๋ถ๋ Groovy๋ ์ง์ํ๋ค.
(์์จ ๋๋ฐ!!)
root ๊ฒฝ๋ก์ app.groovy๋ฅผ ์์ฑํ๊ณ ๋ค์์ฝ๋๋ฅผ ๋ฃ๋๋ค.
@RestController
class ThisWillActuallyRun {
@GetMapping("/")
String home() {
return "Hello, World!"
}
}
๋จผ์ Springboot CLI๋ฅผ ์ค์น(mac์ฉ)
์๋ ์ปค๋ฉ๋ ์คํ
$ spring run app.groovy
์ค๋ถ๋ ๋์ ์ผ๋ก ํต์ฌ ์ ๋ ธํ ์ด์ ๋ค๊ณผ ์ฝ๋๋ฅผ ๋ฃ์ด์ ์คํํ๋ค.
GroovyGrape3๋ฅผ ์ฌ์ฉํด์ ์ฑ์ ์คํํ๊ธฐ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ฐ๋๋ค.
์ ํ๋ฆฌ์ผ์ด์ ๋ณด์ธ์ ์ธ์ฆ(Authentication : ๋น์ ์ ๋๊ตฌ?)๊ณผ ๊ถํ ๋ถ์ฌ(์ธ๊ฐ)(Authorization : ๋น์ ์ ๋ญ ํ ์ ์์?)๋ผ๋ ๋ ๊ฐ์ง ๋ ๋ฆฝ์ ์ธ ๋ฌธ์ ๋ก ์์ฝ๋๋ค.
๋๋๋ก ์ฌ๋๋ค์ "๊ถํ ๋ถ์ฌ(์ธ๊ฐ)"๋์ "์ ๊ทผ ๊ด๋ฆฌ"๋ผ๊ณ ๋ ํ๋ค.
Spring Security๋ ์ธ์ฆ๊ณผ ์ธ๊ฐ๋ฅผ ๋ถ๋ฆฌํ๋๋ก ์ค๊ณ๋จ ์ํคํ ์ฒ๊ฐ ์์ผ๋ฉฐ, ์ด๋ ์ด๋์ ์ ๋ต๊ณผ ํ์ฅ ํฌ์ธํธ๋ฅผ ๊ฐ๊ฒํ๋ค.
์ธ์ฆ์ ์ํ ์ฃผ ์ ๋ต ์ธํฐํ์ด์ค๋ AuthenticationManager
์ด๋ค. AuthenticationManager
๋ ํ๋์ ๋ฉ์๋๋ง ๊ฐ์ง๋ค.
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
AuthenticationManager
์ authenticate()
๋ฉ์๋๋ก 3๊ฐ์ง ์ผ์ ํ ์ ์๋ค.
- ์
๋ ฅํ ๊ฐ์ด ์ ํจํ ์์น(principal)์ธ์ง ๊ฒ์ฆํ ์ ์๋ค๋ฉด,
Authentication
(์ผ๋ฐ์ ์ผ๋ก authenticated=true์ ํจ๊ป)์ ๋ฐํ - ์
๋ ฅํ ๊ฐ์ด ์ ํจํ์ง ์์ ์์น์์ ํ์ธํ๋ค๋ฉด,
AuthenticationException
์ ๋์ง๋ค. - ๊ฒฐ์ ํ ์ ์์ ๊ฒฝ์ฐ
null
์ ๋ฐ
AuthenticationException
์ Runtime Exception์ด๋ค.
AuthenticationException
์ ๋ณดํต ์ผ๋ฐ์ ์ธ ๋ฐฉ๋ฒ์ผ๋ก ์ดํ๋ฆฌ์ผ์ด์
์์ ์คํ์ผ์ด๋, ์ดํ๋ฆฌ์ผ์ด์
์ ๋ชฉ์ ์ ๋ฐ๋ผ ์ฒ๋ฆฌ๋๋ค.
์ฆ, ์ฌ์ฉ์์ ์ฝ๋๋ ์ผ๋ฐ์ ์ผ๋ก ์ด ์ต์ ์ ์ ์ก๊ฑฐ๋ ํธ๋ค๋งํ๊ธฐ๋ฅผ ๋ฐ๋ผ์ง ์๋๋ค.
์๋ฅผ๋ค์ด, ์น UI๋ ์ธ์ฆ์ด ์คํจํ๋ค๋ ํ์ด์ง๋ฅผ ๋ ๋๋งํ ์ ์๊ณ , WWW-Authenticate
header๊ฐ ๊ฐ์ด(๊ฐ์ด ์์ฌ ์๋ ์์), backend HTTP๋ 401 ์๋ต์ ์ค๊ฒ์ด๋ค.
๋ ์ผ๋ฐ์ ์ธ AuthenticationManager
์ ๊ตฌํ์ ProviderManager
์ด๋ค.
ProviderManager
์ AuthenticationProvider
ํ ์ธ์คํด์ค ์ฒด์ธ์๊ฒ ์์์ ํ๋ค.
AuthenticationProvider
๋ AuthenticationManager
๊ณผ ์ฝ๊ฐ ๋น์ทํ์ง๋ง, ๋ค๋ฅธ ๋ฉ์๋๋ฅผ ๊ฐ๋๋ค.
์ด ๋ฉ์๋๋ AuthenticationProvider
์ด ์ฃผ์ด์ง ์ธ์ฆํ์
์ ์ง์ํ๋์ง ํธ์ถ์์๊ฒ ๋ฌผ์ ์ ์๊ฒ ํ๋ฝํ๋ค.
public interface AuthenticationProvider {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
boolean supports(Class<?> authentication);
}
์ด supports()
๋ฉ์๋์ Class<?>
์ธ์๋ ์ค์ ๋กClass<? extends Authentication>
์ด๋ค.
(์ด ์ธ์๋ authenticate() ๋ฉ์๋๋ฅผ ์ง์ํ๋์ง๋ง ๋ฌผ์ด๋ณธ๋ค.)
ProviderManager
๋ AuthenticationProviders
์ ์ฒด์ธ์๊ฒ ๊ฐ์ ์ ํ๋ฆฌ์ผ์ด์
์ด ์์์ ํตํด ์ฌ๋ฌ๊ฐ์ ๋ค๋ฅธ ์ธ์ฆ ๋ฉ์ปค๋์ฆ์ ์ ๊ณตํ ์ ์๋ค.
๋ง์ฝ ProviderManager
๊ฐ ์ธ์ฆ ์ธ์คํด์ค์ ํ์
์ผ๋ถ๋ฅผ ์ธ์ํ์ง ๋ชปํ๋ฉด, ์ด๋ ๋์ด๊ฐ๋ค.(skip)
ProviderManager
์ ํ์ ์ผ๋ก ๋ถ๋ชจ๋ฅผ ๊ฐ์ ์ ์์ผ๋ฉฐ. ์ด๋ ๋ง์ฝ ๋ชจ๋ ์ ๊ณต์๊ฐ null์ ๋ฐํํ๋ค๋ฉด ๊ณ ๋ คํด๋ณผ ์ ์๋ค.
(๋ญ์๋ฆฌ์ผ ์ ๊ณต์๊ฐ ์ ๋ถ null์ ๋ฐํํ๋ฉด ๋ถ๋ชจ๋ฅผ ๊ฐ๋๊ฒ ์ข๋ค๋ ๋ป์ธ๋ฏ)
๋ง์ฝ ๋ถ๋ชจ๊ฐ ์ ํจํ์ง ์๋ค๋ฉด, null ์ธ์ฆ ๊ฒฐ๊ณผ๋ AuthenticationException
์ด๋ค.
๋๋๋ก, ์ดํ๋ฆฌ์ผ์ด์ ์ ๋ณดํธ๋ ๋ฆฌ์์ค์ ๋ ผ๋ฆฌ์ ๊ทธ๋ฃน์ ๊ฐ๋๋ค.(ex: path pattern ๋งค์นญ๊ณผ ๊ด๋ จ๋ ๋ชจ๋ ์น ์์)
๊ทธ๋ฆฌ๊ณ ๊ฐ ๊ทธ๋ฃน์ ๊ทธ๋ค์ ๊ณ ์ ํ AuthenticationManager
๋ฅผ ๊ฐ๋๋ค.
์์ฃผ, ๊ฐ๊ฐ์ AuthenticationManager
๋ ProviderManager
์ด๊ณ , ์ด๋ค์ ๋ถ๋ชจ๋ฅผ ๊ณต์ ํ๋ค.
์ด ๋ถ๋ชจ๋ "global"์์์ ํ ์ข ๋ฅ๋ก, ๋ชจ๋ ์ ๊ณต์์๊ฒ ์๋นํ(fallback)์ฒ๋ผ ๋์ํ๋ค.
์คํ๋ง ์ํ๋ฌํฐ๋ ์ฌ์ฉ์ ์ดํ๋ฆฌ์ผ์ด์ ์ ๋น ๋ฅด๊ฒ ์ผ๋ฐ์ ์ธ ์ธ์ฆ ๋งค๋์ ์ ํน์ง์ ์ ์ ํ ์ ์๋๋ก ๋ช๋ช ์ค์ ์ ์ ๊ณตํ๋ค.
๊ฐ์ฅ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ๋๋ ํฌํผ๋ AuthenticationManagerBuilder
์ด๋ค.
AuthenticationManagerBuilder
๋ ์ธ๋ฉ๋ชจ๋ฆฌ, JDBC, LDAP ์ฌ์ฉ์ ์ธ๋ถ์ฌํญ ๋๋ ์ปค์คํ
ํUserDetailService
์ถ๊ฐ ์
์
์ ํ๋ฅญํ๋ค.
๋ค์ ์์ ๋ global(parent) AuthenticationManager
์ค์ ์ํ ์ดํ๋ฆฌ์ผ์ด์
์ ๋ณด์ฌ์ค๋ค.
import javax.sql.DataSource;
@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
... // web stuff here
@Autowired
public void initialize(AuthenticationManagerBuilder builder, DataSource dataSource) {
builder.jdbcAuthentication().dataSource(dataSource).withUser("dave").password("secret").roles("USER");
}
}
์ด ์์ ๋ ์น ์ดํ๋ฆฌ์ผ์ด์
๊ณผ ๊ด๋ จ์๋ค. ํ์ง๋ง, AuthenticationManagerBuilder
๋ ๋ ๋๊ฒ ์ฐ์ผ ์ ์๋ค.(web security4)
์ดAuthenticationManagerBuilder
๋ @Autowired
์ด๋ค. ์ด ๋ฉ์๋๋@Bean
์ผ๋ก ๋ฑ๋ก๋ฌ๋ค.
์ด๋ AuthenticationManager
๋ฅผ global(parent)๋ฅผ ์ ๊ณตํ๊ฒํ๋ค.
๋์กฐ์ ์ผ๋ก ์๋ ์์๋ฅผ ๋ณด์
import javax.sql.DataSource;
@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
@Autowired
DataSource dataSource;
... // web stuff here
@Override
public void configure(AuthenticationManagerBuilder builder) {
builder.jdbcAuthentication().dataSource(dataSource).withUser("dave").password("secret").roles("USER");
}
}
โ ๏ธ In Spring-security 5.7 M2 deprecatedWebSecurityConfigurerAdapter
์ฌ์ฉ์๊ฐ ์ปดํฌ๋ํธ ๊ธฐ๋ฐ์ security ์ค์ ์ ํ๋๋ก ๊ถ์ฅํ๊ณ ์๋ค. docs : https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter
๋ง์ฝ ์ค์ ์์ ๋ฉ์๋์ @Override
๋ฅผ ์ฌ์ฉํ๋ค๋ฉด, AuthenticationManagerBuilder
๋ global์ child๊ฐ ๋ "local"AuthenticationManager
๋ฅผ ๊ตฌ์ฑํ ๋ฟ์ด๋ค.
์ค๋ถ์์๋ global AuthenticationManagerBuilder
๋ฅผ ๋ค๋ฅธ ๋น์ ์ฃผ์
@Autowired
ํ ์ ์๋ค.
๊ทธ๋ฌ๋, local์ธ ๊ฒ์ ๋ช
์์ ์ผ๋ก ๋
ธ์ถํ์ง ์๋ ํ, @Autowired
๋ก ์ฃผ์
ํ ์ ์๋ค.
์ค๋ถ๋ default global AuthenticationManager
๋ฅผ ์ ๊ณตํ๋ค.
(์ฌ์ฉ์๊ฐ AuthenticationManager
ํ์
์ ๋น์ ์ ์ ํด์ ์ ๊ณตํ์ง ์๋ ํ)
์ด ๊ธฐ๋ณธ๊ฐ์ ์ถฉ๋ถํ ์์ ํ๋ฏ๋ก ์ฌ์ฉ์ ์ ์ global์ด ์ ๊ทน์ ์ผ๋ก ํ์ํ์ง ์๋ ํ ํฌ๊ฒ ๊ฑฑ์ ํ ํ์๊ฐ ์๋ค.
๋ง์ฝ AuthenticationManager
๋น๋๋ฅผ ์ํด ์ด๋ค ์ค์ ์ ํ๋ คํ๋ค๋ฉด, global ๊ธฐ๋ณธ๊ฐ์๋ํด ๊ณ ๋ฏผํ์ง์๊ณ , ์ฃผ๋ก ์ฌ์ฉ์๋ ๋ณดํธํ๋ ค๋ ๋ฆฌ์์ค๋ฅผ ์ํด locallyํ๊ฒ ๋ง๋ค๋ฉด๋๋ค.
authorization์ ํต์ฌ ์ ๋ต์ "AccessDecisionManager"์ด๋ค.
ํ๋ ์์ํฌ๋ 3๊ฐ์ง ๊ตฌํ์ฒด๋ฅผ ์ ๊ณตํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ด ์ธ๊ฐ์ง๋ "AccessDecisionVoter"์ ์ฒด์ธ ์ธ์คํด์ค์ ์์ํ๋ค.
"ProviderManager"๊ฐ "AuthenticationProviders"์ ์์ํ๋ ๊ฒ๊ณผ์ฝ๊ฐ ๋น์ทํ๋ค.
"AccessDecisionVoter"๋ "Authentication"์ ๊ณ ๋ คํ๊ณ (์์น(principal ์ ํํ)), "ConfigAttributes"๋ก ๋ฐ์ฝ๋ ํธ๋ "Object"๋ฅผ ๋ณดํธํ๋ค.
boolean supports(ConfigAttribute attribute);
boolean supports(Class<?> clazz);
int vote(Authentication authentication, S object, Collection<ConfigAttribute> attributes);
"AccessDecisionManager", "AccessDecisionVoter"์ ์๊ทธ๋์ฒ์์ "Object"๋ ์์ ํ ์ ๋ค๋ฆญ์ด๋ค.
์ฌ์ฉ์๊ฐ ์ ๊ทผ(์น ๋ฆฌ์์ค๋, ์๋ฐ ๋ฉ์๋ ๋๊ฐ๋ ๊ฐ์ฅ ์ผ๋ฐ์ ์ธ ์ผ์ด์ค)์ ์ํ๋ ์ด๋ ํ ๊ฒ๋ ํํํ ์ ์๋ค.
"ConfigAttributes"์ญ์ ์ ๋ค๋ฆญ์ด๋ค. ๋, ๊ทธ๊ฒ์ ์ ๊ทผํ๊ธฐ ์ํด ํ์ํ ๊ถํ๋ ๋ฒจ์ ๊ฒฐ์ ํ๋ ๋ฉํ๋ฐ์ดํฐ์ ํจ๊ป "Object"์ ๋ณด์ ๋ฐ์ฝ๋ ์ด์ ์ ๋ณด์ฌ์ค๋ค.
"ConfigAttribute" ๋ ์ธํฐํ์ด์ค์ด๋ค. ์๋ ํ๋์ ๋ฉ์๋๋ง ๊ฐ๋๋ค(์ด ๋ฉ์๋๋ ์ ๋ค๋ฆญ์ด๊ณ "String"์ ๋ฐํํ๋ค.) ๊ทธ๋์ ์ด ๋ฌธ์์ด์ ์์์ ์์ ์์ ์๋๋๋ก ์ํธํ(encode)๋๋ค.
์ด๋ ๋๊ฐ ๊ทธ๊ฒ์ ์ ๊ทผํ ์ ์๋์ง ๊ท์น์ ํํํ๋ค.
์ ํ๋ณ "ConfigAttribute"๋ ์ฌ์ฉ์ ์ญํ ์ ์ด๋ฆ์ด๋ฉฐ("ROLE_ADMIN", "ROLE_AUDOT"๊ฐ์) ๊ทธ๋ฆฌ๊ณ , ์ผ๋ค์ ํน๋ณํ ํฌ๋งท("ROLE_"๊ฐ์)์ด ์๊ฑฐ๋ ํ๊ฐ๊ฐ ํ์ํ ํํ์ ๋ณด์ฌ์ค๋ค.
์ผ๋ฐ์ ์ผ๋ก "ConfigAttributes"sms Spring Expression Language(SpEL)ํํ์ผ๋ก ์ฌ์ฉํ๋ค.
์๋ฅผ ๋ค์ด, "isFullyAuthenticated() && hasRole('user')" ์ด ํํ์ "AccessDecisionVoter"๊ฐ ์ง์ํด์ค๋ค.
"AccessDecisionVoter"๋ ํํ์ ๋ค๋ฃฐ ์ ์๊ณ , ๊ทธ๋ค์ ์ํด ์ปจํ ์คํธ๋ฅผ ์์ฑํ๋ค.
ํํ๋ฒ์ ๋ฒ์๋ฅผ ํธ๋ค๋งํ ์ ์๊ฒ ํ์ฅํ๊ธฐ ์ํด "SecurityExpressionRoot"๊ณผ ๊ฐ๋ "SecurityExpressionHandler"์ ์ปค์คํ ๊ตฌํ์ฒด๋ฅผ ํ์๋กํ๋ค.
์น ๊ณ์ธต์์ Spring Security๋ Servlet "Filters"์ ๊ธฐ๋ณธ์ผ๋ก์๋ค. ๊ทธ๋์, "Filters"์ ์ญํ ์ ๋จผ์ ๋ณด๋ ๊ฒ์ด ๋์์ด๋๋ค.
์๋ ๊ทธ๋ฆผ์ ํ๋์ HTTP ์์ฒญ์๋ํ ํธ๋ค๋ฌ์ ์ ํ๋ณ๋ก ๊ณ์ฆ์ธต์ ๋ณด์ฌ์ค๋ค.
ํด๋ผ์ด์ธํธ๋ ์ดํ๋ฆฌ์ผ์ด์ ์ ํ๋์ ์์ฒญ์ ๋ณด๋ด๊ณ , ์ปจํ ์ด๋๋ ์ด๋ค ํํฐ์ ์ด๋ค ์๋ธ๋ ์ ์ ์ฉํ ์ง๋ฅผ ์์ฒญ URI์ ๊ฒฝ๋ก๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ฒฐ์ ํ๋ค.
๋๊ฒ ํ๋์ ์๋ธ๋ ์ ํ๋์ ์์ฒญ์ ๋ค๋ฃฐ ์ ์์ง๋ง, ํํฐ๋ ์ฒด์ธ์ผ๋ก ํ์ฑ๋๊ณ , ์ ๋ ฌ๋๋ค.
์ฌ์ค ํํฐ๋ ๋ง์ฝ์ ์์ฒญ์ ์์ฒด์ ์ผ๋ก ์ฒ๋ฆฌํ๊ธธ ์ํ๋ฉด, ๋๋จธ์ง ์ฒด์ธ์ ๊ฑฐ๋ถํ ์ ์๋ค.
ํํฐ๋ ์์ฒญ๊ณผ ์๋ต(๋ค์ด์คํธ๋ฆผ ํํฐ์ ์๋ธ๋ฆฟ์์ ์ฌ์ฉ๋๋)์ ๋ณ๊ฒฝํ ์ ์๋ค.(?)
ํํฐ์ฒด์ธ์ ์ ๋ ฌ์ ๊ต์ฅํ ์ค์ํ๋ค. ์คํ๋ง ๋ถํธ๋ ์ด๊ฒ์ ๋๊ฐ์ง ๋งค์ปค๋์ฆ์ผ๋ก ๊ด๋ฆฌํ๋ค.
@Bean
ํ์
์ ํํฐ๋ @Order
๋ฅผ ๊ฐ๊ฑฐ๋ Ordered
๋ฅผ ๊ตฌํํ๋ค.
๊ทธ๋ฆฌ๊ณ , ์๋ค์ FilterRegisterationBean
์ ํ ๋ถ๋ถ์ด ๋ ์ ์๋ค. FilterRegisterationBean
์ ์ค์ค๋ก ์์ ์ API์ ํ ๋ถ๋ถ์ผ๋ก ์์ฒญํ๋ค.
์ด๋ค ๋ง๋ค์ด์ง ํํฐ๋ ๋์์์ฒญ(help signal)์ ์ถฉ์คํ๊ฒ ์ ์๋์ด์๋ค.
์๋ฅผ๋ค์ด, Spring Session์SessionRepositoryFilter
๋ "Integer.MIN_VALUE + 50"์ "DEFAULT_ORDER"๋ฅผ ๊ฐ๋๋ค.
์ด๋ ์ฒด์ธ์์ ์ข๋ ์์๊ณ ์ถ์ง๋ง, ๋ค๋ฅธ ํํฐ๋ค์ด ๋์ค๊ธฐ ์ ์ ๊ท์น์ ์ด๊ธฐ์ง ์๋๋ค.
์คํ๋ง ์ํ๋ฌํฐ๋ ์ฒด์ธ์์ ํ๋์ ํํฐ๋ก ์ค์น๋์ด์๋ค. ๊ทธ๋ฆฌ๊ณ ์ด๋ FilterChainProxy
ํ์
์ผ๋ก ๊ณ ์ (concrete)์ด๋ค.
์คํ๋ง ๋ถํธ ์ดํ๋ฆฌ์ผ์ด์
์์, security ํํฐ๋ ์ดํ๋ฆฌ์ผ์ด์
์ปจํ
์คํธ์์ @Bean
์ด๋ค.
๊ทธ๋ฆฌ๊ณ ์ผ๋ ๊ธฐ๋ณธ์ผ๋ก ์ค์น๋์ด์๊ณ , ๋ชจ๋ ์์ฒญ์ ์ ์ฉํ ์ ์๋ค.
security ํํฐ๋ "SecurityProperties.DEFAULT_FILTER_ORDER" ์ ์ํด ์ ์๋ ์์น์ ์ค์น๋๋ค.
"SecurityProperties.DEFAULT_FILTER_ORDER"๋ "FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER"์์ํด ๊ณ ์ ๋๋ค.
(๋ญ๋ผ๋๊ฑฐ์ผ)
Spring Security๋ ํ๋์ ๋ฌผ๋ฆฌ์ ์ธ ํํฐ์ด๋ค. ๊ทธ๋ฌ๋ ๋ด๋ถ ํํฐ๋ค์ ์ฒด์ธ์ ๊ณผ์ ์ ์์์ํจ๋ค.
์ฌ์ค, security ํํฐ์์ ๊ฐ์ ์ ์ธ ๊ณ์ธต์ด ํ๋ ๋ ์๋ค.
์๋ "DelegatingFilterProxy"๋ผ๋ ์ปจํ
์ด๋์์ ์ค์น๋๋ค. ์๋ Spring @Bean
์ผ ํ์๊ฐ ์๋ค.
์ด ํ๋ก์๋ "FilterChainProxy"์๊ฒ ์์์ํ๋ค. "FilterChainProxy"๋ ํญ์ @Bean
์ด๊ณ , "springSecurityFilterChain"์ด๋ผ๋ ๊ณ ์ ๋ ์ด๋ฆ์ ๊ฐ๋๋ค.
์ด "FilterChainProxy"๋ ๋ด๋ถ์ ์ผ๋ก ํํฐ์ ์ฒด์ธ์ผ๋ก ๊ตฌ์ค๋ ๋ณด์ธ ๋ก์ง์ ์ ๋ถ ํฌํจํ๋ค.
๋ชจ๋ ํํฐ๋ ๊ฐ์ API๋ฅผ ๊ฐ๋๋ค.(ํํฐ๋ค์ ์ ๋ถ ์๋ธ๋ฆฟ ์ค๋ช ์์ "Filter" ์ธํฐํ์ด์ค๋ก ๊ตฌํ๋๋ค.), ๋ชจ๋ ํํฐ๋ ๋๋จธ์ง ์ฒด์ธ๋ค์ ๊ฑฐ๋ถํ ์ ์๋ ๊ธฐํ๊ฐ์๋ค.
"FilterChainProxy"์ ๊ฐ์ ํ๋ ๋ฒจ์์ Spring Security๋ก ๊ด๋ฆฌ๋๋ ๋ชจ๋ ํํฐ์ฒด์ธ์ ๋ง์ ์ ์๋ค. ๊ทธ๋ฆฌ๊ณ , ๋ชจ๋ ์ปจ์ฒด์ด๋์๊ฒ ์๋ ค์ง์ง ์๋๋ค.
Spring Security ํํฐ๋ ํํฐ์ฒด์ธ์ ๋ฆฌ์คํธ๋ฅผ ๊ฐ๊ณ ์๊ณ , ํํฐ์ ๋งค์นญ๋๋ ์ฒซ๋ฒ์งธ ์ฒด์ธ๊ณผ ์์ฒญ์ ๋ถ๋ฆฌํ๋ค.
์๋ ์ด๋ฏธ์ง๋ ์์ฒญ ๊ฒฝ๋ก ๋งค์นญ์ ๊ธฐ๋ฐํ ๋ถ๋ฆฌ(dispatch) ์ํฉ์ ๋ณด์ฌ์ค๋ค. ์ด๋ ์์ฃผ ์ผ๋ฐ์ ์ด์ง๋ง ์์ฒญ์ ๋งค์นญํ๋๊ฒ์ด ์ ์ผํ ๋ฐฉ๋ฒ์ด ์๋๋ค.
์ด ๋ถ๋ฆฌ(dispatch) ๊ณผ์ ์ ๊ฐ์ฅ ์ค์ํ ํน์ง์ ํ๋์ ์ฒด์ธ๋ง์ด ์์ฒญ์ ๋ค๋ฅธ๋ค๋ ๊ฒ์ด๋ค.
Spring Security "FilterChainProxy"๋ ๋จผ์ ๋งค์นญ๋๋ ์ฒซ๋ฒ์งธ ์ฒด์ธ์ ์์ฒญ์ ๋ถ๋ฆฌํ๋ค.
์ปค์คํ security ์ค์ ์ด ์๋ vanilla Spring Boot ์ดํ๋ฆฌ์ผ์ด์ ์ n๊ฐ์ ํํฐ ์ฒด์ธ์ด์๋ค. ๋ณดํต n = 6.
์ฒซ๋ฒ์งธ ์ฒด์ธ(n-1)์ static ์์์ ํจํด๋ค์ ๋ฌด์ํ๋ค. (/css/, /images/, /error ๋ทฐ ๊ฐ์) (๊ทธ ๊ฒฝ๋ก๋ SecurityProperties์์ "security.ignored"๋ก ์ฌ์ฉ์๊ฐ ์์ ํ ์ ์๋ค.)
๋ง์ง๋ง ์ฒด์ธ์ catch-all ๊ฒฝ๋ก(/**) ๋งค์นญํ๊ณ , ์ธ์ฆ์ ์ ๋ณด๋ฅผ ํฌํจํ๊ณ , ์ธ๊ฐ, ์์ธ์ฒ๋ฆฌ, ์ธ์ ์ฒ๋ฆฌ, ํค๋ ์์ฑ ๋ฑ๋ฑ ๋ ๋ง์ ์์ ์ํ๋ค.
๊ธฐ๋ณธ(default) ์ฒด์ธ์์ ์ ๋ถ 11๊ฐ์ ํํฐ๊ฐ ์๋ค. ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ์๋ ์ด๋ค ํํฐ๊ฐ ์ธ์ ์ฌ์ฉ๋๋์ง์ ๋ํ ๊ฑฑ์ ์ ํ ํ์๋ ์๋ค.
Spring Security ๋ด์ ๋ชจ๋ ํํฐ๋ ์ฌ์ค ์ปจํ ์ด๋์๊ฒ ์๋ ค์ง์ง ์์๋ค. ์ด๋ ์ค์ํ๋ค. ํนํ Spring Boot ์ดํ๋ฆฌ์ผ์ด์ ์์, ๊ธฐ๋ณธ์ ์ผ๋ก ๋ชจ๋ Filter ํ์ ์
@Bean
์ ์ปจํ ์ด๋์ ์๋์ผ๋ก ๋ฑ๋ก๋๋ค. ๊ทธ๋์ ์ฌ์ฉ์๊ฐ ์ปค์คํ ํํฐ๋ฅผ security ์ฒด์ธ์ ์ถ๊ฐํ๊ณ ์ถ๋ค๋ฉด,@Bean
์ ๋ง๋ค์ง ๋ง๊ฑฐ๋, ์ปจํ ์ด๋๊ฐ ๋ฑ๋ก์ ๋ชฉํ๋๋ก ๋ช ์์ ์ผ๋ก "FilterRegistrationBean"์ผ๋ก ๊ฐ์ธ์ผํ๋ค.
Spring Boot ์ดํ๋ฆฌ์ผ์ด์ ์์ ๊ธฐ๋ณธ ํํฐ์ฒด์ธ์ "SecurityProperties.BASIC_AUTH_ORDER"์ ์ฃผ๋ฌธ์์ํด ๋จผ์ ์ ์๋์๋ค.
"security.basic.enabled=false" ์ธํ ์ ํตํด ์์ ํ ์ค์ ์ ๋ ์ ์๊ณ , ์๋๋ฉด ๋๋น์ฑ ์ผ๋ก ๊ธฐ๋ณธ์ค์ ์ ์ฌ์ฉํ ์ ์๋ค. ๊ทธ๋ฆฌ๊ณ , ๋ ๋ฎ์ ์ฐ์ ์์๋ฅผ ๊ฐ๋(lower order) ๋ค๋ฅธ ๊ท์น์ ์ ์ํ ์ ์๋ค.
๋์ค์, "WebSecurityConfigurerAdapter"๋๋ "WebSecurityConfigurer" ํ์
์ @Bean
์ ์ถ๊ฐํ ์ ์๋ค.
๊ทธ๋ฆฌ๊ณ @Order
๋ฅผ ์ด์ฉํด ํด๋์ค๋ฅผ ๋ฐ์ฝ๋ ์ดํธํ ์ ์๋ค.
@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/match1/**")
...;
}
}
์ด ๋น์ Spring Security๊ฐ ์๋ก์ด ํํฐ์ฒด์ธ์ ์ถ๊ฐํ๊ณ , ๊ธฐ๋ณธ ์ค์ ์ดํ์ ์ฐ์ฐ์์๋ฅผ ๊ฐ๊ฒ์ผ๊ธฐํ๋ค.
๋ง์ ์ดํ๋ฆฌ์ผ์ด์ ์ ๋ฆฌ์์ค ์งํฉ์ ๋ํด ์์ ํ ๋ค๋ฅธ ์ ๊ทผ ๊ท์น์ ๊ฐ์ง๊ณ ์๋ค.
๊ฐ ๋ฆฌ์์ค์ ์ธํธ๋ ์์ ๋ค๋ง์ ์ ์ผํ ์์(order)์ ์์ฒญ ๋งค์นญ(request matcher)๊ณผ ํจ๊ป "WebSecurityConfigurerAdapter"๋ฅผ ๊ฐ๋๋ค.
๋ง์ฝ ๋งค์นญ๋ฃฐ๋ฆฌ ์ค๋ฒ๋ฉ๋๋ฉด, ๊ฐ์ฅ ์ฐ์ ์์๊ฐ ๋์ ํํฐ์ฒด์ธ์ด ์ด๊ธด๋ค.
security ํํฐ ์ฒด์ธ(="WebSecurityConfigurerAdapter)์ ์์ฒญ ๋งค์นญ(request matcher)์ ๊ฐ๋๋ค. ์๋ HTTP ์์ฒญ์ ์ด ํํฐ๋ฅผ ์ ์ฉํ ์ง ๋ง์ง ๊ฒฐ์ ํ๋ค.
ํน์ ํํฐ ์ฒด์ธ์ ์ ์ฉํ๊ธฐ๋ก ๊ฒฐ์ ๋๋ฉด, ๋ค๋ฅธ ํํฐ ์ฒด์ธ์ ์ ์ฉ๋์ง ์๋๋ค.
ํํฐ์ฒด์ธ์ ์ ์ฉํ๋ฉด, "HttpSecurity" ์ค์ ์์ ์ถ๊ฐ์ ์ธ ๋งค์นญ์ ์ธํ ํ๋ฉด ๋ ์ฌ์ธํ๊ฒ ์ธ๊ฐ๋ฅผ ์ค์ ํ ์ ์๋ค.
@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/match1/**")
.authorizeRequests()
.antMatchers("/match1/user").hasRole("USER")
.antMatchers("/match1/spam").hasRole("SPAM")
.anyRequest().isAuthenticated();
}
}
์คํ๋ง ๋ถํธ MVC ์ดํ๋ฆฌ์ผ์ด์ ์(thymeleaf, spring-boot-starter-web ์ด ์ถ๊ฐ๋) ํ์ผ ์ ๋ก๋๋ฅผ ํ๊ธฐ ์ํด์๋ "MultipartConfigElement"๋ฅผ ๋ฑ๋กํด์ผํ๋ค. ์ค๋ถ๋ ๋ชจ๋ auto-config!
- [์ฐธ๊ณ ์๋ฃ] (https://howtodoinjava.com/java/io/reading-large-files/)
์ฐธ๊ณ ์๋ฃ๋ค
controller์ ๋ฑ๋ก๋ html์ templates/~
์์ ์ฐพ๋๋ค.
formํ๊ทธ์ ํจ๊ป ์ฌ์ฉํ๋ th:object="${greeting}"
ํํ์ ํผ๋ฐ์ดํฐ๋ฅผ ์์งํ๊ธฐ ์ํ ๋ชจ๋ธ ๊ฐ์ฒด๋ ์ ์ธํ๋ค.
@, * ๊ฐ๋ถ์ EL(?) ํํ์์ ์ ํ์ ํด ๋ณด์
์ฐธ๊ณ : HelloController - greeting GET/POST
form ์์์์ th:action="@{/greeting}"
ํํ์ POST "/greeting"์๋ํฌ์ธํธ๋ก ๋ฐ๋ก๊ฐ๋ค๋๋ฐ.. ์๊ฐ.
method="post"
๋ฃ์ด์ค์ผ POST๋๋๋? ๊ธฐ๋ณธ์ GET์ผ๋ก ๊ฐ.
gradle
implementation 'org.springframework.boot:spring-boot-starter-validation'
์ํผ ๊ฐ๋จํ ์์ ๋๋งค ๋ฅ๋ป์งํ๋ค ๋ฐ๊ฒฌํ ๋ฌธ์ ๋ด์ฉ
Errors, BindingResult: ๋๋, @RequestBody, @RequestPart ์ธ์์ ์ ํจ์ฑ ๊ฒ์ฌ์ ๋ช ๋ น ๊ฐ์ฒด(@ModelAttribute ์ธ์)์ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์์ ์๋ฌ์ ์ ๊ทผํ๊ธฐ์ํด ์ฌ์ฉ. "Errors", "BindingResult" ์ธ์๋ ์ ํจ์ฑ ๊ฒ์ฌ์ฉ ๋ฉ์๋ ์ธ์์ ์งํ์ ์์นํด์ผ๋๋๊ฑธ ๋ฐ๋์ ํ์คํํด๋ผ
thymeleaf extras spring security extras springsecurity ์ ์ ๋ฆฌ๋ ๋ฌธ์
- ์ ํธ๋ฆฌํฐ ๊ฐ์ฒด ํํ๋ฒ ์ฌ์ฉ
<div th:text="${#authentication.name}">
The value of the "name" property of the authentication object should appear here.
</div>
<div th:if="${#authorization.expression('hasRole(''ROLE_ADMIN'')')}">
This will only be displayed if authenticated user has role ROLE_ADMIN.
</div>
#authorization
๋ org.thymeleaf.extras.springsecurity[5|6].auth.Authorization
์ ๊ฐ์ฒด์ด๋ค.
- ์ดํธ๋ฆฌ๋ทฐํธ ์ฌ์ฉํ๊ธฐ
sec:authentication
์ดํธ๋ฆฌ๋ทฐํธ๋#authorization
๋ ๊ฐ๋ค. ์ฌ์ฉ๋ฒ๋ง ๋ค๋ฆ
<div sec:authentication="name">
The value of the "name" property of the authentication object should appear here.
</div>
sec:authorize
์ sec:authorize-expr
์ดํธ๋ฆฌ๋ทฐํธ๋ ๊ฐ๋ค.
์คํ๋ง ์ํ๋ฌํฐ expression์์ th:if
๋ ์ ํธ๋ฆฌํฐ ๊ฐ์ฒด ํํ๋ฒ์์ #authorization.expression(...)
์ ๊ฐ์๊ธฐ๋ฅ ์ ๊ณต.
- ๋ค์์คํ์ด์ค
dialect์ ๋ชจ๋ ๋ฒ์ ์ ๋ค์์คํ์ด์ค๋
http://www.thymeleaf.org/extras/spring-security
์ด๋ค.
์๋ชป๋ ๋ค์์คํ์ด์ค๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒ์ ํ ํ๋ฆฟ ์ฒ๋ฆฌ์ ์ํฅ์ ๋ฏธ์น์ง ์์ง๋ง, ํ ํ๋ฆฟ์ ์ ์/์๋์์ฑ ๊ฐ์ IDE์ ์ํฅ์ ์ค ์ ์๋ค.
thymeleaf layout dialet ์ ๋ฆฌ ์ ๋ ๋ฌธ์
layout:decorate="~{layouts/default}"
layout:decorate
์ ์ฌ์ฉํ์, ํ์ด์ง๋ฅผ ์ฐ๊ฒฐํ๋ค. ์๋ ํ์ด์ง์ ๋ฃจํธ ์์๋ฅผ ๊ธฐ์ค์ผ๋ก ๋์ํ๋ค.
controller์์ ์๋ตํ์ด์ง๋ content ๊ฒฝ๋ก๋ฅผ ์ก์์ฃผ๋ฉด๋๋ค. ํํ
@ComponentScan
์ด๋ ธํ ์ด์ ์ด ์๋ ํ์ผ์ ํจํค์ง ์๋๋ฅผ ์ฐพ๋๋ค.basePackages
/basePackageClasses
๋ก ์ง์ ๋ ๊ฐ๋ฅ- ๊ถ์ฅ ๋ฐฉ๋ฒ: ๊ตฌ์ฑํ์ผ์ ๋ฑ๋ก์ ํ๋ก์ ํธ ์ต์๋จ์ ๋๊ธฐ.
- SpringBoot๋
@SpringBootApplication
์ ํฌํจ๋์ด์์.
- SpringBoot๋
์ปดํฌ๋ํธ ์ค์บ์ ์ํด ์๋์ผ๋ก ์คํ๋ง๋น์ด ๋ฑ๋ก๋๋๋ฐ, ์ด๋ ํด๋์ค ์ด๋ฆ์ ์๊ธ์๋ฅผ ์๋ฌธ์๋ก ๋ฐ๊ฟ ์คํ๋ง๋น ๋ฑ๋ก์ด ๋๋ค. ๊ทธ ์ด๋ฆ์ด ๊ฐ์ ๊ฒฝ์ฐ ์คํ๋ง์ ์ค๋ฅ๋ฅผ ๋ฐ์์ํจ๋ค.
โ url mapping์ด ์์ผ๋ฉด ๋์ํจ..;;;5
์๋ ๋น๊ณผ ์๋ ๋น์ ์ด๋ฆ์ด ์ถฉ๋๋๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
โ ๏ธ Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
๋น ์ฃผ์
์ ์ฃผ์
๋์์ด ์ฌ๋ฌ๊ฐ์ผ ๊ฒฝ์ฐ ์ถฉ๋์ด ๋๋ค. @Qualifier
, @Named
, @Primary
๋ฅผ ์ฌ์ฉํด ์ถฉ๋์ ๋ฐฉ์งํ์
ํน์ ์ด๋ ธํ ์ด์ ์ ํฌํจ/์ ์ธ ์ํฌ ์ ์์
- includeFilters : ์ปดํฌ๋ํธ ์ค์บ ๋์์ผ๋ก ์ถ๊ฐ
- excludeFilters : ~" ์ ์ธ
@ComponentScan(
includeFilters = @ComponentScan.Filter(type = FilterType.Annotation, classes = IncludeComponent.class),
excludeFilters = @ComponentScan.Filter(type = FilterType.Annotation, classes = OutcludeComponent.class)
)
FilterType ์ต์
ANNOTATION
: ๊ธฐ๋ณธ๊ฐ, ์ด๋ ธํ ์ด์ ์ ์ธ์ํด ๋์ASSIGNABLE_TYPE
: ์ง์ ํ ํ์ ๊ณผ ์์ ํ์ ์ ์ธ์ํด ๋์ASPECTJ
: AspectJ ํจํด ์ฌ์ฉREGEX
: ์ ๊ท ํํ์CUSTOM
: TypeFilter์ด๋ผ๋ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด์ ์ฒ๋ฆฌ
@org.junit.jupiter.api.Test
public void $EXPR$() {
org.assertj.core.api.Assertions.assertThat($END$)
}