서론
백엔드 프로젝트에 참여할 때마다 Spring Security를 적용하였지만 한 번도 동작 과정에 대해 공부해 본 적이 없어 이번 기회에 구경해봤다.
Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.
Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements
출처: https://spring.io/projects/spring-security#overview
우선, Spring Security가 무엇인지 쉽게 얘기하면 Java 어플리케이션에 대해 인증/인가를 제공해주는 강력한 framework라고 자신들을 소개하고 있다. 보안을 위해 최소한 나무 울타리 정도는 치는 느낌이라고 생각하면 될 것 같다.
Spring 3.4.0 버전에 Spring Security는 6.4.2 버전이 가장 최신 버전인 시점에 구경하면서 글을 작성했다.
Security를 dependency에 추가해준 뒤 아무것도 하지 않고 Run을 하면 localhost:8080 치면 아래와 같은 화면을 볼 수가 있다.
이 글에서 중점으로 다룰 내용은 '왜' 이런 화면이 나오게 되는 것인가 + ID, Password가 '어떻게' 제공되는가이다.
본론
ID, Password 생성은?
첫 째로, User 생성은 SecurityProperties 클래스에 들어가면 확인할 수 있다.
아래와 같이 SecurityProperties는 Default User를 만들어내는 것을 볼 수 있는데 User 클래스를 들여다보면 name은 user, password는 랜덤한 UUID를 제공하는 것을 알 수 있다.
여기까지 보면 어떻게 제공하는지에 대한 궁금증은 해결된다.
private final Filter filter = new Filter();
private final User user = new User();
public User getUser() {
return this.user;
}
public Filter getFilter() {
return this.filter;
}
public static class Filter {
/**
* Security filter chain order for Servlet-based web applications.
*/
private int order = DEFAULT_FILTER_ORDER;
/**
* Security filter chain dispatcher types for Servlet-based web applications.
*/
private Set<DispatcherType> dispatcherTypes = EnumSet.allOf(DispatcherType.class);
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
public Set<DispatcherType> getDispatcherTypes() {
return this.dispatcherTypes;
}
public void setDispatcherTypes(Set<DispatcherType> dispatcherTypes) {
this.dispatcherTypes = dispatcherTypes;
}
}
public static class User {
/**
* Default user name.
*/
private String name = "user";
/**
* Password for the default user name.
*/
private String password = UUID.randomUUID().toString();
/**
* Granted roles for the default user name.
*/
private List<String> roles = new ArrayList<>();
private boolean passwordGenerated = true;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
if (!StringUtils.hasLength(password)) {
return;
}
this.passwordGenerated = false;
this.password = password;
}
SecurityConfig
다음은 화면이 어떻게 나오는가에 대한 이야기로 넘어가보자.
보통 Spring Security를 프로젝트에 적용하는 사람들이라면 별도의 SecurityConfig 파일을 만든 후 아래와 같이 작성해서 프로젝트에 적용할 것이다.
물론 커스터마이징을 위해서 필요한 작업이고 블로그들은 비슷하게 작성해서 적용하지만(참고로 위 코드처럼 한다면 해당 화면은 나오지 않는다) 이걸 추가하지 않아도 default로 어디서 동작하는지를 알아보고싶었다.
결론적으로 SpringBootWebSecurityConfiguration 클래스 파일을 가보면 Default 클래스로 Method를 확인할 수 있다.
여담으로, 주석을 보면 User가 따로 SecurityFilterChain Bean을 생성하면 해당 Method는 실행되지 않는다라고 적혀있다.
SecurityFilterChain은 HttpSecurity 클래스에 의해 만들어지며 HttpSecurity 클래스 내 주석을 확인해보면 http 요청에 대해 이것저것 확인하는 역할을 수행하는 이름 그대로의 역할을 맡고 있는 것을 확인할 수 있다.
궁금증은 해결되었다. authorizeHttpRequests는 들어오는 Request에 대해 인증 작업을 수행하겠다는 것을 의미하고 formLogin의 경우는 login.jsp 파일을 제공해서 브라우저 내에서 login form을 보여주어 사용자가 로그인을 수행할 수 있도록 만들어준다고 적혀있다.
근데 httpBasic의 경우 없어도 그만 있어도 그만인 느낌이다.
Configures HTTP Basic authentication.
Class 내 주석도 이게 전부이다.
GPT-4의 말에 따르면 없어도 된다고 한다.
그래도 궁금하니 FormLogin을 없애고 실험해보았다.
결과는 아래와 같이 굉장히 밋밋한 페이지를 확인할 수 있었다.
WebSecurity
마지막으로 들어온 김에 조금만 더 구경해보자.
HttpSecurity는 Http 요청에 대해 도맡아서 처리를 하는 것이고 SecurityFilterChain을 만들어준다.
SecurityFilterChain을 활용하는 것은 WebSecurity 클래스로 이동하면 알 수가 있다고 한다.
들어가면 친절한 개발자들이 DelegatingFilterProxy에 SpringSecurityFilterChain을 만들어준다는 것을 명시해주어 더 이상 파지않아도 된다고 얘기해준다.
DelegatingFilterProxy에 대해 모르는 사람들을 위해 짧게 설명하자면,
Filter에서 Spring Container 내 Bean에 접근할 수 있도록 만들어주는 녀석이라는 것으로 알고있다.
'백엔드' 카테고리의 다른 글
[Spring] Maven Project 터미널 빌드 (0) | 2024.12.23 |
---|