인증Authentication
인가Authorization
에 대한 처리를 위임하는 별도의 프레임 워크이다.
Spring Security
의 인증
인가
는 대부분을 Filter
의 흐름에 따라 처리한다. Filter
는 Dispatcher Servlet
이전에 적용 되므로 가장 먼저 URL 요청을 받는다.
스프링 시큐리티 프레임워크가 인증, 인가를 어덯게 처리하는지?
클라이언트가 DispatcherServlet
에 도달하기 전에 Filter
에서 요청을 가로채 인증
과 인가
에 대한 처리를 해준다.
id
password
를 로그인 인증 Authentication
요청AuthenticationFilter
가 정보를 가로채 UsernamePasswordAuthentication Token
을 생성하여 (Authentication 객체
) AuthenticationManager
에게 Authentication
전달AuthenticationManager
인터페이스를 거쳐 Autehntication Provider
에게 정보 전달, 등록된 AuthenticationProvider
를 조회하여 인증 요구 한다. AutenticationProvider
는 UserDetailService
를 통해 입력받은 (3)의 사용자 정보를 DB에서 조회한다. .support()
메소드가 실행 가능한지 체크authenticate()
메소드를 통해 DB에 저장된 이용자 정보와 입력한 로그인 정보 비교UserDetailService
의 loadUserByUsername()
을 통해 불러옴Authentication
객체 UsernameAuthentication Token
Authentication
반환 AuthenticationManager
는 Authentication
객체를 AutenticationFilter
로 전달 AuthenticationFilter
는 전달 받은 Authentication
객체를 LoginSuccessHandler
로 전송 하고 SecurityContextHolder
에 담는다.AuthenticationSucesshandle
실패시 AuthenticationFailureHandle
실행 Spring Security
의존성을 추가함으로 /login 페이지에 해당 화면을 볼수 있다.서버 빌드시 password 확인이 가능하며 기본 id는
user
이다.
첫 번째 화면에서만 자격증명을 요구하며 세션ID나 토큰 세부 정보를 이용한다.application.properties 정적 인증 정보 설정
위와 같이 변경이 가능하며 서버를 빌드할 때 마다 비밀번호를 복사-붙여넣기 하지 않아도 된다.
스프링 필터 속에는 각기 다른 기능을 가진 필터들을 대거 포함하고 있으며 그 갯수는 자그마치 20개가 족히 넘는다. 주 기능은 유저네임과 비밀번호를 2 단계 안의 인증 객체로 변환하며, 개게가 생성되면 Spring Security Filter 들은 이 요청을 인증 관리자 (authentication manager)
에게 넘긴다.
이름에서 알수 있듯 이 인증관리자는 실질적인 인증 로직을 관리하는 인터페이스 클래스이다.
인증 관리자가 하는 일은 웹 애플리케이션 안에 어떤 인증 제공자 (authentication provider)
가 존재하는지 확인한다.
특정 요청에 대해 유요한 인증 제공자가 어떤 것인지 확인하는 거이 인증 관리자의 책임이다.
단순히 로그인에 실패해서 응답을 엔드 유저에게 반환하는 것이 아닌 가능한 모든 인증 제공자들을 시도하고 모든 시도에서 실패했을 때 비로소 인증이 실패했다고 응답한다.
실질적인 인증 로직을 정의하는 부분이다. 가령 어떤 데이터베이스나, LDAP 서버에서 혹은 권한 부여 서버나 캐시에서 유저 자격 증명을 할 것인지 서술한다.
웹 애플리케이션 내에 다수의 인증 제공자를 작성할 수 있다. 예를 들어 유저네임과 비밀번호 인증 담당하는 인증 제공자와 별도로 로그인을 하기위핸 OR 매핑을 프레임워크에 활용하게 할수도 있다.
대부분의 프로젝트에서 요구하는 공통 로직을 Sprng Security에서 이미 구현해 놓은 인터페이스와 클래스.아이디, 비밀번호 대조에 사용된다.
Password Encoder
와 협동하여 작동한다.
Spring Security Filter
는 생성된 인증 객체를 보안 컨텍스트 안에 저장하데 이를 공간이다. 인증이 성공적이었는지 유무와, 세션 ID 등을 저장한다. 이미 인증정보가 저장 되어 있다면 재 접속 시에 다시 인증요구를 하지 않는다.
default Security Configurations
안에는defaultSecurityFilterChain
클래스가 있는데HttpSecurity
를 매개변수로 받는다. 위 클래스를 해석 해보면 모든 URL을 보호하게 만들고 있음을 아수 있다.
http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated());
이 줄 하나로 모든 경로의 요청이 증명이 되어야 한다.
@Configuration
public class ProjectSecurityConfig {
@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated());
http.formLogin(withDefaults());
http.httpBasic(withDefaults());
return http.build();
}
}
@Configuration
public class ProjectSecurityConfig {
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((requests) -> requests
.requestMatchers("/myAccount","/myBalance","/myLoans","/myCards").authenticated()
.requestMatchers("/notices","/contact").permitAll())
.formLogin(Customizer.withDefaults())
.httpBasic(Customizer.withDefaults());
// httpBasic() 메소드는 HTTP 기본 인증을 활성화한다. 이것은 사용자 이름과 비밀번호를 사용하여 인증을 수행한다.
return http.build();
}
}
formLogin
httpBasic
(Customizaer.withDefaults())
formLogin()
과 httpBasic()
메소드의 매개 변수로 사용된다.Customizar
인터페이스르 구현하는 객체를 전달하여 기본 설정을 변경할 수 있다. @Configuration
public class ProjectSecurityConfig {
@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated());
http.formLogin(withDefaults());
http.httpBasic(withDefaults());
return http.build();
}
}
@Configuration
public class ProjectSecurityConfig {
@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((requests) -> requests.anyRequest().permitAll())
.formLogin(withDefaults()).httpBasic(withDefaults()
);
}
}
UserDetails
를 이용하여 User
를 .build()
하게 되면 InMemoryUserDetailManager
가 build()
된 User
를 전부 생성하게 된다.
@Bean
public InMemoryUserDetailsManager userDetailService() {
UserDetails admin = User.withDefaultPasswordEncoder()
.username("admin")
.password("12345")
.authorities("admin")
.build();
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("12345")
.authorities("user")
.build();
return new InMemoryUserDetailsManager(admin, user);
}
방법2에는 사소한 차이가 있는데 바로 PasswordEncoder 관련 메서드를 호출하지 않는 다는 것이다.
비밀번호의 암호화 및 해싱을 수행하지 않는 방법 이다
@Bean
public InMemoryUserDetailsManager userDetailService() {
UserDetails admin = User
.withUsername("admin")
.password("12345")
.authorities("admin")
.build();
UserDetails user = User
.withUsername("user")
.password("12345")
.authorities("user")
.build();
return new InMemoryUserDetailsManager(admin, user);
}
username
을 바로 사용하지 못하는 이유는 첫 번째 파라메터에서는withDefaultPasswordEncoder
나withUsername
을 써야하기 때문이다.
가장 처음에 있는 인터페이스는
UserDetailsService
이다. 이 서비스에는loadUserByUsename
이라는 이름의 추상 메서드가 있을 것이다. 가령 수행하려는 모든 인증 작업은 브라우저내의 입력된 유저정보, DB내에 있는 저장 정보를load
하는 것이다. 따라서loadUsersByUsername
이라는 이름의 단일 추상 메소드를 가진 별도의 인터페이스가 별도로 있는 것이다.
여기서 왜 유저의 이름으로만 로드하는 걸까? 비밀번호 둘 다 쓰면 안되나?
UserDetailSevice
를 확장하는 또 다른 인터페이스가 있는데 UserDetailsManager
이며 유저 세부정보를 관리한다. (생성, 수정,삭제,변경, 등 )위에 보면 다 나와있음 데이터베이스를 다룰 때엔
JdbcUserDetailsManager
를 사용하면 된다. 이와 비슷하게 저장하기 위해LdapServer
를 이용하는 경우LdapUserDetailsManager
를 사용하면 된다. 총 이 3가지가SpringSecurity FrameWork
에서 제공하는 세가지 구현 클래스이다.
자체 인증 로직이 있거나 직접 작성하려는 경우 자체 AuthenticationProvider
를 정의하면 된다.
위의 모든 인터페이스와 클래스는 유저 세부 정보를 저장하고 로드하고, 업데이트, 수정을 하는데 정작 유저 세부 정보는 어떻게 다루지?
UserDetails
의 구현체 중 하나인User
라는 클래스를 제공한다.
SecurityFilter
->AutenticationManager
구현체 :ProviderManager
->AuthneticationProvider
구현체 :DaoAuthenticationProvider
->UserDetailsManager
구현체 :InMemoryUserDetailsManager
진행한 예제에서는 InMemoryUserDetailsManager
유형의 Bean을 생성했기 때문