HttpSecurity
HttpSecurityConfiguration에서 HttpSecurity를 생성하고 초기화를 진행한다
HttpSecurity는 보안에 필요한 각 설정 클래스와 필터들을 생성하고 최종적으로 SecurityFilterChain 빈 생성
HttpSecurityConfiguration를 통해 HttpSecurity를 수동빈 등록하는데 빈을 생성할때 설정을 진행하며
Configurer가 만들어지고 최종적으로 HttpSecurity가 생성되면 Configurer안의 init, configure 메소드를 통해 필터들이 생성되고 등록된다.
SecurityFilterChain
- SecurityFilterChain는 하나가 아니라 여러개가 생성될 수 있다.
public final class DefaultSecurityFilterChain implements SecurityFilterChain {
private static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class);
private final RequestMatcher requestMatcher;
private final List<Filter> filters;
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) {
this(requestMatcher, Arrays.asList(filters));
}
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) {
if (filters.isEmpty()) {
logger.debug(LogMessage.format("Will not secure %s", requestMatcher));
} else {
List<String> filterNames = new ArrayList();
Iterator var4 = filters.iterator();
while(var4.hasNext()) {
Filter filter = (Filter)var4.next();
filterNames.add(filter.getClass().getSimpleName());
}
String names = StringUtils.collectionToDelimitedString(filterNames, ", ");
logger.debug(LogMessage.format("Will secure %s with filters: %s", requestMatcher, names));
}
this.requestMatcher = requestMatcher;
this.filters = new ArrayList(filters);
}
public RequestMatcher getRequestMatcher() {
return this.requestMatcher;
}
public List<Filter> getFilters() {
return this.filters;
}
public boolean matches(HttpServletRequest request) {
return this.requestMatcher.matches(request);
}
public String toString() {
String var10000 = this.getClass().getSimpleName();
return var10000 + " [RequestMatcher=" + this.requestMatcher + ", Filters=" + this.filters + "]";
}
}
RequestMatcher를 통해 해당 요청에 맞는 필터인지 체크 한다.
개별 필터에 RequestMatcher를 생성해서 쓰는경우도 있지만
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.requestMatchers(new AntPathRequestMatcher("/public/**")).permitAll()
.requestMatchers(new AntPathRequestMatcher("/admin/**")).hasRole("ADMIN")
.requestMatchers(new AntPathRequestMatcher("/user/**")).hasRole("USER")
.requestMatchers(HttpMethod.POST, "/api/**").authenticated()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/")
.permitAll()
.and()
.csrf().disable(); // Note: Disabling CSRF is not recommended for production
}
다음과 같이 HttpSecurity 설정을 통해 공통으로 지켜야할 규칙에 대한 RequestMatcher 설정을 진행한다.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() // 여기서부터는 공유되는 RequestMatcher 설정
.requestMatchers(new AntPathRequestMatcher("/public/**")).permitAll()
.requestMatchers(new AntPathRequestMatcher("/admin/**")).hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout() // 로그아웃에 대한 특정 RequestMatcher 설정
.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET")) // 특정 필터에 대한 개별 설정
.logoutSuccessUrl("/login?logout")
.permitAll();
}
}
이렇게 특정 filter에 대해 적용되는 RequestMatcher도 설정가능하다.
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class AuthenticatedExcelDownloadFilter extends OncePerRequestFilter {
private final AntPathRequestMatcher requestMatcher;
public AuthenticatedExcelDownloadFilter(String urlPattern) {
this.requestMatcher = new AntPathRequestMatcher(urlPattern);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (requestMatcher.matches(request)) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.isAuthenticated() && auth.getAuthorities().stream()
.anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"))) {
// 엑셀 다운로드 로직 구현
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment; filename=download.xlsx");
// 여기에 실제 엑셀 생성 및 다운로드 로직 추가
// ...
return; // 필터 체인 진행을 여기서 중단
}
}
filterChain.doFilter(request, response);
}
}
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/excel/download/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.addFilterAfter(new AuthenticatedExcelDownloadFilter("/excel/download/**"), UsernamePasswordAuthenticationFilter.class);
}
}
이런식으로 특정 필터에 적용할 requestmatcher 만들고 HttpSecurity 설정에서 addFilterAfter() 또는 addFilterBefore()을 이용해서 필터를 추가해줄 수 있다.
WebSecurity
WebSecurityConfiguration에서 WebSecurity를 생성하고 초기화를 진행한다
WebSecurity는 HttpSecurity에서 생성한 SecurityFilterChain빈을 SecurityBuilder에 저장한다
WebSecurity 가 build() 를 실행하면 SecurityBuilder 에서 SecurityFilterChain 을 꺼내어 FilterChainProxy 생성자에게 전달한다
[WebSecurity 가 build()를 실행하면 FilterChainProxy가 생성된다. ]
최종적으로 FilterChainProxy가 생성되고, 그 Proxy는 설정한 모든 보안 필터목록을 다 가지고 있는다.
댓글