Spring Security란?
Spring Security는 Java 기반의 웹 응용 프로그램 및 서비스에서 보안을 제공하는 프레임워크이다. 주로 Spring 프레임워크 기반의 애플리케이션에서 사용되며, 주로 웹 사이트의 회원을 관리할 때, 인증(Authentication)과 권한 부여(Authorization)를 처리하는 데 사용된다. Spring Security는 다양한 보안 기능을 제공하여 웹 애플리케이션을 안전하게 개발하고 운영할 수 있도록 도와준다.
인증
- 인증은 사용자가 자신을 식별하고, 그사용자가 주장하는 주체가 맞는지 확인하는 프로세스이다. 인증은 주로 사용자의 신원을 확인하고 검증하기 위해 사용된다.
- 주로 아이디와 비밀번호, 토큰 등을 제공하여 자신을 인증하며 신원을 확인하고 성공적인 인증 후, 시스템은 사용자에게 일부 권한을 부여한다.
인가
- 인가는 사용자가 특정자원에 접근하거나 특정 동작(읽기, 쓰기, 삭제 등)을 수행할 권한이 있는지 결정하는 프로세스 이다. 즉 인가는 인증된 사용자에 대한 권한을 관리하는 것이다. 특정 자원이나 서비스에 접근하려는 사용자에 대해 시스템이 결정을 내리고, 이에 따라 해당 사용자에 대한 권한을 부여하거나 거부한다.
- 인증은 "누구인가?" 를 확인하고, 인가는 "무엇을 할 수 있는가?"를 결정한다.
스프링 시큐리티 구조
스프링 시큐리티 구조의 처리 과정은
- 사용자가 로그인 정보와 함께 인증 요청을 한다.
- AuthenticationFilter가 요청을 대신 받아서 그 정보를 통해 UsernamePasswordAuthenticationToken의 인증용 객체를 생성한다.
- AuthenticationManager의 구현체인 ProviderManager에게 생성한 UsernamePasswordToken 객체를 전달한다.
- AuthenticationManager는 등록된 AuthenticationProvider를 조회하여 인증을 요구한다.
- 실제 DB에서 사용자 인증정보를 가져오는 UserDetailsService에 사용자 정보를 넘겨준다.
- 넘겨받은 사용자 정보를 통해 DB에서 찾은 사용자 정보인 UserDetails 객체를 만든다.
- AuthenticationProvider는 UserDetails를 넘겨받고 사용자 정보를 비교한다.
- 인증이 완료되면 권한 등의 사용자 정보를 담은 Authentication 객체를 반환한다.
- 다시 최초의 AuthenticationFilter에 Authentication 객체가 반환된다.
- Authentication 객체를 SecurityContext에 저장한다.
스프링 시큐리티 사용해보기
UserEntity
@Entity
@Table(name = "users")
@Getter
@Setter
public class UserEntity {
@Id
private String username;
private String password;
private String role;
}
UserRepository
public interface UserRepository extends JpaRepository<UserEntity, String> {
}
CustomUserDeailsServiceImpl
//UserDetailsService는 스프링 시큐리티에서 제공하는 인터페이스
@Service
public class CustomUserDetailsServiceImpl implements UserDetailsService {
private final UserRepository userRepository;
@Autowired
public CustomUserDetailsServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserEntity userEntity = userRepository.findById(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found with username: " + username));
return new CustomUserDetails(userEntity);
}
}
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class) // 로그인시 UsernamePasswordAuthenticationFilter를 통해 사용자를 인증하는 동작을 정의
.authorizeRequests() // 요청에 대한 접근 권한을 설정하는 것을 시작.
.antMatchers("/main").permitAll() // "/main" 경로에 대한 접근을 모두 허용
.antMatchers("/admin/**").hasAnyRole("ADMIN") // "/admin/"으로 시작하는 경로에 대해서 ADMIN 역할을 가진 사용자에게만 허용
.anyRequest().authenticatied() // 나머지 모든 요청은 인증된 사용자에게만 허용
.and() // 다른 설정 옵션 추가
.formLogin() // 폼 기반 로그인을 활성화
.loginPage("/login") // 로그인 페이지의 경로를 "/login"으로 설정
.loginProcessingUrl("/member/login") // 로그인을 처리하는 URL을 "/member/login"으로 설정
.defaultSuccessUrl("/main", true) // 로그인 성공시 "/main"으로 항상 이동
.permitAll() // 로그인 페이지에 대한 접근 권한을 모든 사용자에게 허용
.and()
.logout() // 로그아웃 설정을 시작
.logoutSuccessUrl("login") // 로그아웃 성공 시 이동할 페이지는 "/login"
.permitAll(); // 로그아웃에 대한 권한을 모든 사용자에게 허용
}
/*
* 로그인할때 사용되며 AuthenticationFilter를 확장하여 로그인 프로세스를 정의한다.
* "/login" URL설정, 로그인 폼에서 username, passwor를 받는다.
* 핸들러를 통해 성공과 실패 시의 정의한 핸들러 객체를 설정한다.
@Bean
public UsernamePasswordAuthenticationFilter authenticationFilter() throws Exception {
UsernamePasswordAuthenticationFilter authenticationFilter = new UsernamePasswordAuthenticationFilter();
authenticationFilter.setAuthenticationManager(authenticationManagerBean());
authenticationFilter.setFilterProcessesUrl("/login"); // 로그인 URL 설정
authenticationFilter.setUsernameParameter("username");
authenticationFilter.setPasswordParameter("password");
authenticationFilter.setAuthenticationSuccessHandler(successHandler());
authenticationFilter.setAuthenticationFailureHandler(failureHandler());
return authenticationFilter;
}
@Bean
public AuthenticationSuccessHandler successHandler() {
return new CustomAuthenticationSuccessHandler();
}
@Bean
public AuthenticationFailureHandler failureHandler() {
return new CustomAuthenticationFailureHandler();
}
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
// BCrypt형식의 암호화
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
UsernamePasswordAuthenticationFilter는 구현이 필수는 아니다. UsernamePasswordAuthenticationFilter가 제공하는 기능 이외의 기능이 필요할 경우 구현할 수 있다. CustomAuthenticationSuccessHandler와 CustomAuthenticationSuccessHandler 를 구현하는 것은 필수는 아니다. 스프링 시큐리티에서는 기본적으로 성공과 실패시 동작을 처리하기 위한 기본 핸들러를 제공한다. 따라서 구현하지 않아도 기본적인 동작은 정상적으로 수행된다.
대체로는 사용자의 요청이 실패와 성공했을때 페이지 이동등의 설정을 하는데.defaultSuccessUrl이나 failureUrl으로 대체가 가능하다.
'Spring' 카테고리의 다른 글
Spring Boot - Lombok (0) | 2024.04.03 |
---|---|
Spirng Boot - REST API로 CRUD 만들기 (0) | 2024.04.03 |
스프링 핵심 원리 - 스프링 컨테이너 생성, 빈 조회 (0) | 2024.01.22 |
스프링 핵심 원리 - 예제 만들기 (0) | 2024.01.14 |
객체지향 설계와, 스프링 (1) | 2024.01.12 |