
์ค์ ์ด์ ํ๊ฒฝ์์ REST API๋ ์๋ฌด๋ ์ ๊ทผ ๊ฐ๋ฅํ ์์ ํ ์คํ API ์ํ๋ก ์๋น์ค๋ฅผ ๋ฐฐํฌํ ์๋ ์์ต๋๋ค.
์ด๋ฒ ๊ธ์์๋ Spring Security๊ฐ ์ด๋ป๊ฒ ๊ธฐ๋ณธ ๋ณด์์ ์ ๊ณตํ๋์ง, ๊ทธ๋ฆฌ๊ณ Security ๊ตฌ์กฐ์ ๋ด๋ถ ๋์์ ๋จ๊ณ๋ณ๋ก ๋ถ์ํฉ๋๋ค.
Spring Boot ์ฑ์ ๋ค์ ์์กด์ฑ๋ง ์ถ๊ฐํฉ๋๋ค.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
๋น๋ ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฌ์คํํ๋ฉด ์ฝ์์ ์๋์ผ๋ก ์์ฑ๋ ๋น๋ฐ๋ฒํธ(UUID)๊ฐ ํ์๋ฉ๋๋ค.
Using generated security password: a12b3c4d-...
์ด์ /welcome ๋ฑ ์ด๋ค API๋ฅผ ํธ์ถํ๋๋ผ๋ Spring Security๊ฐ ์๋์ผ๋ก ๋ก๊ทธ์ธ ํ์ด์ง๋ฅผ ์์ฑํฉ๋๋ค.
๐ก ์ง์ ๋ก๊ทธ์ธ ํ์ด์ง๋ฅผ ๋ง๋ ์ ์ด ์์ด๋ ์๋์ผ๋ก ๋ํ๋๋ ์ด์ ๋,
DefaultLoginPageGeneratingFilter๊ฐ HTML ๋ก๊ทธ์ธ ํ๋ฉด์ ์๋ ๋ ๋๋งํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
| ํญ๋ชฉ | ์ค๋ช |
|---|---|
| ๊ธฐ๋ณธ ์ฌ์ฉ์ ์ด๋ฆ | user |
| ๊ธฐ๋ณธ ๋น๋ฐ๋ฒํธ | ์ฝ์์ ํ์๋ UUID |
| ๋ก๊ทธ์ธ ํ์ด์ง | Spring Security ์๋ ์์ฑ |
| ๋ณดํธ ๋ฒ์ | ๋ชจ๋ API (/**) |
โ ๋ก๊ทธ์ธ ์ฑ๊ณต โ API ์ ์ ์๋ต
โ
์คํจ โ 401 Unauthorized ๋๋ 403 Forbidden
๋งค๋ฒ ๋น๋ฐ๋ฒํธ๊ฐ ๋ฐ๋๋ ๊ฑด ๋ถํธํ๋ฏ๋ก, ๋ค์์ฒ๋ผ ์ ์ํฉ๋๋ค.
spring.security.user.name=${SECURITY_USERNAME:eazybytes}
spring.security.user.password=${SECURITY_PASSWORD:12345}
:${} ๊ตฌ๋ฌธ โ ํ๊ฒฝ๋ณ์ ์์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ๊ฐ ์ฌ์ฉ์ฌ์์ ํ์ ์ฝ์์ ๋ ์ด์ ์์ ๋น๋ฐ๋ฒํธ๊ฐ ํ์๋์ง ์์ต๋๋ค.
Spring Security๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ธ์ ๊ธฐ๋ฐ ์ธ์ฆ(Session-based Authentication)์ ์ฌ์ฉํฉ๋๋ค.
์ฆ, ๋ก๊ทธ์ธ ์ฑ๊ณต ์ JSESSIONID ์ฟ ํค๋ฅผ ๋ฐ๊ธํ๊ณ ์ดํ ์์ฒญ ์ ์ฌ์ฌ์ฉํฉ๋๋ค.
๐ ๋ธ๋ผ์ฐ์ ์์ ํ์ธ
F12 โ Application โ Cookies โ localhost:8080
| ์ฟ ํค๋ช | ์ญํ |
|---|---|
JSESSIONID | ์ธ์ฆ ์ธ์ ์๋ณ ํค |
| ๊ฐ | ์๋ฒ์ SecurityContext์ ๋งคํ๋จ |
๐ก ์ฟ ํค๋ฅผ ์ญ์ ํ๊ฑฐ๋ ์กฐ์ํ๋ฉด ๋ก๊ทธ์ธ ์ ๋ณด๊ฐ ์ฌ๋ผ์ง๊ณ ๋ค์ ๋ก๊ทธ์ธ ํ์ด์ง๊ฐ ๋ํ๋ฉ๋๋ค.
"์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ณดํธํด์ผ ํ๋์?"
๋ฉด์ ๋จ๊ณจ ์ง๋ฌธ์ ๋๋ค.
๋ณด์์ โ๋น๊ธฐ๋ฅ ์๊ตฌ์ฌํญ(NFR)โ์ด์ง๋ง,
๊ฐ๋ฐ ์ด๊ธฐ๋ถํฐ ๋ฐ์ํด์ผ ํฉ๋๋ค.
๐ SDLC์ โ์ผ์ชฝ์ผ๋ก ๋ณด์ ์ด๋(Shift Left Security)โ
โ DevSecOps (Dev + Sec + Ops ํ์ ) ๊ฐ๋ ๋ฑ์ฅ
Spring Security๋ ์ ํ๋ฆฌ์ผ์ด์ ๋ ๋ฒจ ๋ณด์์ ๋ด๋นํฉ๋๋ค.
์ ์ฒด ์ธํ๋ผ์ ๋ณด์์ ๋ค์๊ณผ ๊ฐ์ ๊ณ์ธต ๊ตฌ์กฐ๋ก ์ด๋ฃจ์ด์ง๋๋ค.
[ํด๋ผ์ฐ๋] โ [VM/OS] โ [์ปจํ
์ด๋] โ [์ฑ ์๋ฒ] โ [Spring Security]
| ๊ณ์ธต | ๋ด๋น | ์์ |
|---|---|---|
| ํด๋ผ์ฐ๋, VM, OS | ์ด์ํ | IAM, ๋ฐฉํ๋ฒฝ, SSL |
| ์ปจํ ์ด๋, ์๋ฒ | DevOps | HTTPS, ๋คํธ์ํฌ ์ ์ด |
| ์ ํ๋ฆฌ์ผ์ด์ | ๊ฐ๋ฐ์ | ์ธ์ฆ, ์ธ๊ฐ, CSRF, CORS |
Spring Security๋ Filter ๊ธฐ๋ฐ ์ํคํ ์ฒ๋ก ์๋ํฉ๋๋ค.
Client โ Security Filters โ DispatcherServlet โ Controller
๋ชจ๋ ์์ฒญ์ ์๋ธ๋ฆฟ ๋๋ฌ ์ ๋ค์์ Security Filter (์ฝ 20์ฌ ๊ฐ)๋ฅผ ๊ฑฐ์นฉ๋๋ค.
| ํํฐ | ์ญํ |
|---|---|
AuthorizationFilter | ์ธ์ฆ๋์ง ์์ ์์ฒญ ๊ฐ์ |
DefaultLoginPageGeneratingFilter | ๋ก๊ทธ์ธ ํ์ด์ง ์์ฑ |
UsernamePasswordAuthenticationFilter | ๋ก๊ทธ์ธ ์๊ฒฉ ์ฆ๋ช ์ถ์ถ |
BasicAuthenticationFilter | HTTP Basic ์ฒ๋ฆฌ |
CsrfFilter | CSRF ๊ณต๊ฒฉ ๋ฐฉ์ด |
SecurityContextPersistenceFilter | ์ธ์ ์ ์ง |

AuthenticationManager โ AuthenticationProvider๊ฐ ์ธ์ฆ ์ํSecurityContext์ ์ ์ฅ, ์ธ์
์ ์งJSESSIONID ๊ธฐ๋ฐ)Spring Boot๋ ๊ธฐ๋ณธ์ ์ผ๋ก SpringBootWebSecurityConfiguration์ ํตํด ์๋ ๋ํดํธ ๊ตฌ์ฑ์ ์ ๊ณตํฉ๋๋ค.
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(req -> req.anyRequest().authenticated());
http.formLogin(Customizer.withDefaults());
http.httpBasic(Customizer.withDefaults());
return http.build();
}
๐ ์๋ฏธ
anyRequest()) ์ธ์ฆ ํ์formLogin() โ ๋ธ๋ผ์ฐ์ ๋ก๊ทธ์ธ UI ์ ๊ณตhttpBasic() โ REST ํด๋ผ์ด์ธํธ์์ ํค๋ ์ธ์ฆ@Configuration
public class ProjectSecurityConfig {
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(req -> req
.requestMatchers("/myAccount", "/myBalance", "/myLoans", "/myCards").authenticated()
.requestMatchers("/notices", "/contact", "/error").permitAll()
);
http.formLogin(Customizer.withDefaults());
http.httpBasic(Customizer.withDefaults());
return http.build();
}
}
โ requestMatchers() ๋ก ๊ฒฝ๋ก๋ณ ์ ๊ทผ ์ ์ด
โ
permitAll()โ ๋๊ตฌ๋ ์ ๊ทผ ๊ฐ๋ฅโ
authenticated()โ ๋ก๊ทธ์ธ ํ์
| ๊ตฌ๋ถ | ํน์ง | ์ฌ์ฉ ์ํฉ |
|---|---|---|
formLogin() | HTML ๋ก๊ทธ์ธ UI ์๋ ์์ฑ | ๋ธ๋ผ์ฐ์ ํ๊ฒฝ |
httpBasic() | ํค๋์ Base64 ์ธ์ฝ๋ฉ ์ธ์ฆ ์ ๋ณด ํฌํจ | REST ํด๋ผ์ด์ธํธ, Postman |
http.formLogin(flc -> flc.disable());
http.httpBasic(hbc -> hbc.disable());
โ ๋ ๋ค ๋นํ์ฑํ ์ ์ธ์ฆ ๋ถ๊ฐ (403 Forbidden)
Postman ์คํ โ ์ ์์ฒญ ์์ฑ
URL ์ ๋ ฅ
GET http://localhost:8080/myAccount
Authorization ํญ โ Basic Auth ์ ํ
eazybytes12345์๋ ํค๋ ์ถ๊ฐ:
Authorization: Basic ZWF6eWJ5dGVzOjEyMzQ1
์ด๋ username:password๋ฅผ Base64 ์ธ์ฝ๋ฉํ ๊ฐ์
๋๋ค.
| ์ง๋ฌธ | ํต์ฌ ์์ฝ |
|---|---|
| Security ๊ธฐ๋ณธ ์ธ์ฆ ํ๋ฆ? | Filter โ AuthManager โ Provider โ UserDetailsService โ PasswordEncoder โ SecurityContext |
| formLogin vs httpBasic | UI ์ ๋ฌด / ์์ฒญ๋ฐฉ์ ์ฐจ์ด |
| permitAll / authenticated / denyAll | ์ ๊ทผ์ ์ด ๊ตฌ๋ฌธ ๋น๊ต |
| JSESSIONID ์ญํ | ์ธ์ ์ธ์ฆ ์๋ณ์ฉ ์ฟ ํค |
| ์ JWT ์ฌ์ฉ? | ์ธ์ ์ ํ์ฅ์ฑ ๋ฌธ์ ํด๊ฒฐ (Stateless ์ธ์ฆ) |