
- Spring Security๋ Spring ์น ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ณด์ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ํ๋ ์์ํฌ
- ์ ํ๋ฆฌ์ผ์ด์ ์ ์ธ์ฆ(Authentication), ์ธ๊ฐ(Authorization) ์ ๊ฐ๋ ์ ๊ธฐ๋ฐ์ผ๋ก ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ณด์์ ๊ด๋ฆฌํ ์ ์๋๋ก ๊ตฌ์ฑ
๐ค ์ธ์ฆ(Authentication)
- ์ฌ์ฉ์์ ์ ์์ ํ์ธํ๋ ๊ณผ์
- ์์) ๋ก๊ทธ์ธ ์ ์์ด๋์ ๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅํ๊ฑฐ๋, ์์ฒด์ธ์(์ง๋ฌธ, ์ผ๊ตด ์ธ์)์ ์ฌ์ฉํ๋ ๊ฒ
- "๋๊ตฌ์ธ๊ฐ?"๋ฅผ ํ์ธ
๐ค ์ธ๊ฐ(Authorization)
- ์ธ์ฆ๋ ์ฌ์ฉ์๊ฐ ์ด๋ค ๋ฆฌ์์ค์ ์ ๊ทผํ ์ ์๋ ๊ถํ์ด ์๋์ง ํ์ธํ๋ ๊ณผ์
- ์ฆ, ์ฌ์ฉ์๊ฐ ํน์ ์์ ์ด๋ ๋ฐ์ดํฐ์ ์ ๊ทผํ ์ ์๋์ง๋ฅผ ๊ฒฐ์
- ์์) ์ํ ์ฑ์์ ๊ณ์ข ์กฐํ๋ ๊ฐ๋ฅํ์ง๋ง, ๋ค๋ฅธ ์ฌ๋์ ๊ณ์ข๋ ๋ณผ ์ ์๋ ๊ฒฝ์ฐ
- "๋ฌด์์ ํ ์ ์๋๊ฐ?"๋ฅผ ๊ฒฐ์

Filters: ์ฌ์ฉ์์ ์์ฒญ์ด ์๋ฒ์ ๋๋ฌํ๋ฉด ๊ฐ์ฅ ๋จผ์ ํํฐ(์: ์คํ๋ง ์ํ๋ฆฌํฐ ํํฐ ์ฒด์ธ)๋ฅผ ๊ฑฐ์น๋ค. ์ด ๋จ๊ณ์์ ์ธ์ฆ, ์ธ๊ฐ, ๋ก๊น
๋ฑ ๋ค์ํ ์ฌ์ ์ฒ๋ฆฌ๊ฐ ์ด๋ฃจ์ด์ง๋ค..DispatcherServlet: ํํฐ๋ฅผ ํต๊ณผํ ์์ฒญ์ DispatcherServlet์ผ๋ก ์ ๋ฌ๋๋ค.Handler Interceptors: DispatcherServlet์ ์์ฒญ์ ์ปจํธ๋กค๋ฌ์ ์ ๋ฌํ๊ธฐ ์ ์ ์ธํฐ์
ํฐ๋ฅผ ๊ฑฐ์น๋ค.Controller: ๋ง์ง๋ง์ผ๋ก ์ปจํธ๋กค๋ฌ์์ ๋น์ฆ๋์ค ๋ก์ง์ด ์คํ๋๊ณ , ๊ฒฐ๊ณผ๊ฐ ๋ค์ ์ญ์์ผ๋ก ๋ฐํ๋๋ค.์์ฒญ(Request), ์๋ต(Response) ๊ฐ์ฒด๋ฅผ ๋ณํํ๊ฑฐ๋ ์ฐธ์กฐํ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.public class FilterChainProxy extends GenericFilterBean {
...
private SecurityContextHolderStrategy securityContextHolderStrategy;
private List<SecurityFilterChain> filterChains;
private FilterChainValidator filterChainValidator;
private HttpFirewall firewall;
private RequestRejectedHandler requestRejectedHandler;
private ThrowableAnalyzer throwableAnalyzer;
private FilterChainDecorator filterChainDecorator;
...
}
public interface SecurityFilterChain {
boolean matches(HttpServletRequest request);
List<Filter> getFilters();
}
SecurityFilterChain๋ ์์ฒญ์ ๋ํ ํํฐ๋ง์ ์ํด, Filter๋ฅผ ํฌํจํ๋ฉฐ, ํด๋น Filter๋ ๋ค์๊ณผ ๊ฐ์ด ์๋ธ๋ฆฟ์์ ์ ์ํ Filter๋ค
package jakarta.servlet;
import java.io.IOException;
public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {
}
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
default void destroy() {
}
}

Authentication ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ์ ์ธ์ฆ ์ํ๋ฅผ ํ์ธํ๊ฑฐ๋, ์ธ๊ฐ์ ๋ํ ๋ด์ฉ ํ์ธ ๊ฐ๋ฅThreadLocal์ ์ฌ์ฉAuthentication ๊ฐ์ฒด๋ฅผ ํฌํจ๋ก๊ทธ์ธ -> Authentication ๊ฐ์ฒด ์์ฑ -> Authentication ๊ฐ์ฒด SecurityContext์ ์ ์ฅ -> ์ด๋์๋ SecurityContext ๋ฅผ ํตํด ํ์ฌ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด ์ ๊ทผ ๊ฐ๋ฅPrincipal : getPrincipal() ๋ฉ์๋๋ฅผ ํตํด ์ ๊ทผ ๊ฐ๋ฅ, ๋ณดํต ์ฌ์ฉ์์ ๊ณ ์ ์๋ณ์๋ฅผ ๋ํ๋
๋๋ค. ์ผ๋ฐ์ ์ผ๋ก UserDetails ๊ฐ์ฒด์ด๊ฑฐ๋, ์ฌ์ฉ์๋ช
(username)์ ๋ํ๋ด๋ String์ผ ์ ์๋ค.
Credentials : getCredentials() ๋ฉ์๋๋ฅผ ํตํด ์ ๊ทผํ ์ ์์ผ๋ฉฐ, ์ฌ์ฉ์์ ์ธ์ฆ ์ ๋ณด(๋น๋ฐ๋ฒํธ)๋ฅผ ๋ํ๋ธ๋ค.
Authorites : getAuthorities() ๋ฉ์๋๋ฅผ ํตํด ์ ๊ทผํ ์ ์์ผ๋ฉฐ, ์ฌ์ฉ์๊ฐ ๊ฐ์ง๊ณ ์๋ ๊ถํ(๋๋ ์ญํ )์ ๋ชฉ๋ก์ ์ ๊ณต. ์ด ๊ถํ๋ค์ GrantedAuthority ์ธํฐํ์ด์ค์ ๊ตฌํ์ฒด๋ก ํํ
Authenticated:isAuthenticated()๋ฉ์๋๋ฅผ ํตํด ํ์ฌ ์ฌ์ฉ์๊ฐ ์ธ์ฆ๋์๋์ง ์ฌ๋ถ๋ฅผ ๋ฐํ. ์ธ์ฆ์ด ์ฑ๊ณต์ ์ผ๋ก ์ด๋ฃจ์ด์ง ๊ฒฝ์ฐ, ์ด ๊ฐ์true

| ๋ฉ์๋ | ๋ฐํ ํ์ | ์ค๋ช | ๊ธฐ๋ณธ๊ฐ |
|---|---|---|---|
| getAuthorities() | Collection<? extends GrantedAuthority> | ์ฌ์ฉ์ ๊ถํ ๋ชฉ๋ก ๋ฐํ | |
| getPassword() | String | ์ฌ์ฉ์ ๋น๋ฐ๋ฒํธ ๋ฐํ | |
| getUsername() | String | ์ฌ์ฉ์ ๊ณ ์ ๊ฐ ๋ฐํ | |
| isAccountNonExpired() | boolean | ๊ณ์ ๋ง๋ฃ ์ฌ๋ถ ๋ฐํ | true (๋ง๋ฃ ์๋จ) |
| isAccountNonLocked() | boolean | ๊ณ์ ์ ๊น ์ฌ๋ถ ๋ฐํ | true (์ ๊ธฐ์ง ์์) |
| isCredentialsNonExpired() | boolean | ๋น๋ฐ๋ฒํธ ๋ง๋ฃ ์ฌ๋ถ ๋ฐํ | true (๋ง๋ฃ ์๋จ) |
| isEnabled() | boolean | ๊ณ์ ํ์ฑํ ์ฌ๋ถ ๋ฐํ | true (ํ์ฑํ ๋จ) |
์ฌ์ฉ์๋ช
(username)์ ๊ธฐ๋ฐ์ผ๋ก UserDetails ๊ฐ์ฒด๋ฅผ ๋ก๋ํ๋ ์ญํ ์ ํ๋ฉฐ, ์ฌ์ฉ์ ์ธ์ฆ ๊ณผ์ ์์ ํ์ํ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ๋ค๋ฅธ ์ ์ฅ์์์ ๊ฐ์ ธ์ค๋ ๋ฐ ์ฌ์ฉpublic class CustomUserDetailService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return null;
}
}
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
}
}
SecurityFilterChain : Spring Security์์ ์น ์ ํ๋ฆฌ์ผ์ด์
์ ๋ณด์ ์ ์ฑ
(์ธ์ฆ, ์ธ๊ฐ ๋ฑ)์ ์ค์ ํ๊ณ , ์ด๋ฅผ ํํฐ ์ฒด์ธ ํํ๋ก ๋ฑ๋กํ๋ ์ญํ
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable()) // csrf ๊ธฐ๋ฅ ์ ๊ฑฐ
}
}
.csrf(csrf -> csrf.disable()) : Spring Security์์ CSRF(Cross-Site Request Forgery) ๋ณดํธ ๊ธฐ๋ฅ์ ๋นํ์ฑํํ๋ ์ค์ .cors(cors -> cors.disable()) : CORS ๋นํ์ฑํ.formLogin(
form -> {
form
.failureForwardUrl("/login?error=true")
// ๋ก๊ทธ์ธ ์คํจ ์ ์ง์ ํ URL๋ก forward(๋ด๋ถ ์ด๋). URL์ ๋ฐ๋์ง ์๊ณ , ์คํจ ํ์ด์ง๋ฅผ ๋ณด์ฌ์ค๋ค.
.successForwardUrl("/")
// ๋ก๊ทธ์ธ ์ฑ๊ณต ์ ์ง์ ํ URL๋ก forward(๋ด๋ถ ์ด๋). URL์ ๋ฐ๋์ง ์๊ณ , ์ถ๊ฐ ์ฒ๋ฆฌ๊ฐ ํ์ํ ๋ ์ฌ์ฉ.
.defaultSuccessUrl("/")
// ๋ก๊ทธ์ธ ์ฑ๊ณต ์ ์ง์ ํ URL๋ก redirect(์ธ๋ถ ์ด๋). URL์ด ์ค์ ๋ก ๋ณ๊ฒฝ๋๋ฉฐ, ๊ฐ์ฅ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ.
usernameParameter("loginId")
// ๋ก๊ทธ์ธ ํผ์์ ์์ด๋ ์
๋ ฅ๋์ name ์์ฑ์ "loginId"๋ก ์ฌ์ฉํ๋๋ก ์ง์ . (๊ธฐ๋ณธ๊ฐ: "username")
.passwordParameter("loginPwd")
// ๋ก๊ทธ์ธ ํผ์์ ๋น๋ฐ๋ฒํธ ์
๋ ฅ๋์ name ์์ฑ์ "loginPwd"๋ก ์ฌ์ฉํ๋๋ก ์ง์ . (๊ธฐ๋ณธ๊ฐ: "password")
.loginProcessingUrl("/signin")
// ๋ก๊ทธ์ธ ํผ์ด ์ ์ถ๋ ๋ ์ธ์ฆ ์ฒ๋ฆฌ๋ฅผ ๋ด๋นํ๋ URL์ ์ง์ . (POST ์์ฒญ ์ ์ด URL๋ก ์ธ์ฆ ์ฒ๋ฆฌ)
.loginPage("/signin")
// ์ธ์ฆ์ด ํ์ํ ์ฌ์ฉ์๊ฐ ์ ๊ทผ ์ ๋ณด์ฌ์ค ๋ก๊ทธ์ธ ํ์ด์ง์ URL์ ์ง์ . (GET ์์ฒญ ์ ๋ก๊ทธ์ธ ํผ ์ ๊ณต)
.permitAll()
// ๋ก๊ทธ์ธ ๊ด๋ จ ํ์ด์ง(๋ก๊ทธ์ธ ํผ, ๋ก๊ทธ์ธ ์ฒ๋ฆฌ ๋ฑ)๋ ์ธ์ฆ ์์ด ๋๊ตฌ๋ ์ ๊ทผํ ์ ์๋๋ก ํ์ฉ
Customizer.withDefaults()
// ์์ ๋ชจ๋ ์ปค์คํ
์ค์ ์์ด, Spring Security์ ๊ธฐ๋ณธ formLogin ์ค์ ์ ๊ทธ๋๋ก ์ฌ์ฉ
)
.logout(logout -> {
logout.logoutUrl("/signout")
// ๋ก๊ทธ์์์ ์ฒ๋ฆฌํ URL์ ์ง์ . (POST ์์ฒญ ์ ๋ก๊ทธ์์ ์ฒ๋ฆฌ)
.logoutSuccessUrl("/")
// ๋ก๊ทธ์์ ์ฑ๊ณต ํ ์ด๋ํ URL์ ์ง์ . (redirect ๋ฐฉ์)
.invalidateHttpSession(true)
// ๋ก๊ทธ์์ ์ ํ์ฌ HTTP ์ธ์
์ ๋ฌดํจํ(์ญ์ ). (์ธ์
์ ์ ์ฅ๋ ์ ๋ณด ๋ชจ๋ ์ญ์ )
.clearAuthentication(true)
// ๋ก๊ทธ์์ ์ `SecurityContext`์ ์ ์ฅ๋ ์ธ์ฆ(Authentication) ์ ๋ณด๋ฅผ ์ญ์ . (์ฐ๋ ๋ ๋ก์ปฌ์์ ์ ๊ฑฐ)
.deleteCookies("JSESSIONID")
// ๋ก๊ทธ์์ ์ ์ง์ ํ ์ฟ ํค(JSESSIONID)๋ฅผ ์ญ์ . (์๋ฒ์ ํด๋ผ์ด์ธํธ ๋ชจ๋์์ ์ธ์
์ฟ ํค ์ ๊ฑฐ)
}
)
.authorizeHttpRequests(
auth -> {
auth.requestMatchers("/signup", "/signin").anonymous()
// "/signup", "/signin" ๊ฒฝ๋ก๋ ๋ก๊ทธ์ธํ์ง ์์(anonymous) ์ฌ์ฉ์๋ง ์ ๊ทผํ ์ ์๋ค.
// ์ด๋ฏธ ๋ก๊ทธ์ธํ ์ฌ์ฉ์๋ ์ ๊ทผํ ์ ์๋ค..
.requestMatchers("/user/**").hasRole("MEMBER")
// "/user/**" ๊ฒฝ๋ก๋ "MEMBER" ๊ถํ(์ญํ )์ด ์๋ ์ฌ์ฉ์๋ง ์ ๊ทผ ๊ฐ๋ฅํ๋ค.
.anyRequest().authenticated()
// ์์์ ์ง์ ํ ๊ฒฝ๋ก ์ธ์ ๋ชจ๋ ์์ฒญ์ ์ธ์ฆ๋ ์ฌ์ฉ์๋ง ์ ๊ทผ ๊ฐ๋ฅํ๋ค.
// (๋ก๊ทธ์ธํ์ง ์์ ์ฌ์ฉ์๋ ์ ๊ทผ ๋ถ๊ฐ)
})
์ฐธ๊ณ