: Spring 기반의 애플리케이션의 보안(인증과 권한, 인가 등)을 담당하는 스프링 하위 프레임워크.
- 스프링 서버에 필요한 인증 및 인가를 위해 많은 기능을 제공해 줌으로써 개발의 수고를 덜어줌!
Spring Security는 ' 인증'과 '권한'에 대한 부분을 Filter 흐름에 따라 처리한다.
더보기
※ 인증 ? 해당 사용자가 본인이 맞는지를 확인하는 절차
인가 ? 인증된 사용자가 요청한 자원에 접근 가능한지를 결정하는 절차
※ Principal (접근 주체) : 보호받는 Resource에 접근하는 대상
Credential (비밀번호) : Resource에 접근하는 대상의 비밀번호
<Filter / Interceptor>
- Filter와 Interceptor는 실행되는 시점이 다름!
- Filter : Web Application에 등록 // Servlet 에서 처리하기 전후로 다룸
- Interceptor : Spring의 Context에 등록 // 실행시점을 다르게 가져감
<스프링 시큐리티 프레임워크 추가>
build.gradle
// 스프링 시큐리티
implementation 'org.springframework.boot:spring-boot-starter-security'
<스프링 시큐리티 활성화>
security > WebSecurityConfig
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity // 스프링 Security 지원을 가능하게 함
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
// 어떤 요청이든 '인증'
.anyRequest().authenticated()
)
// 로그인 기능 허용
.formLogin()
.defaultSuccessUrl("/")
.permitAll()
.and()
//로그아웃 기능 허용
.logout()
.permitAll();
return http.build();
}
}
<로그인/로그아웃 처리과정 이해>
- 스프링 시큐리티 사용 전
- 스프링 시큐리티 사용 후
- Client 의 요청은 모두 Spring Security 를 거침
- Spring Security 역할
- 인증/인가
- 성공 시: Controller 로 Client 요청 전달
- Client 요청 + 사용자 정보 (UserDetails)
- 실패 시: Controller 로 Client 요청 전달되지 않음
- Client 에게 Error Response 보냄
- 성공 시: Controller 로 Client 요청 전달
- 인증/인가
- 로그인 처리과정
더보기
- Client
- 로그인 시도
- 로그인 시도할 username, password 정보를 HTTP body 로 전달 (POST 요청)
- 로그인 시도 URL 은 WebSecurityConfig 클래스에서 변경 가능
- 인증 관리자 (Authentication Manager)
- UserDetailsService 에게 username 을 전달하고 회원상세 정보를 요청
- UserDetailsService
- 회원 DB 에서 회원 조회
- 회원 정보가 존재하지 않을 시 → Error 발생
- 조회된 회원 정보(user) 를 UserDetails 로 변환
- UserDetails 를 "인증 관리자"에게 전달
- 회원 DB 에서 회원 조회
- "인증 관리자" 가 인증 처리
- 아래 2 개의 username, password 일치 여부 확인
- Client 가 로그인 시도한 username, password
- UserDetailsService 가 전달해준 UserDetails 의 username, password
- password 비교 시
- Client 가 보낸 password 는 평문이고, UserDetails 의 password 는 암호문
- Client 가 보낸 password 를 암호화해서 비교
- 인증 성공 시 → 세션에 로그인 정보 저장
- 인증 실패 시 → Error 발생
- 아래 2 개의 username, password 일치 여부 확인
- 로그아웃 처리
- "GET /user/logout" 요청 시 로그아웃
- 서버 세션에 저장되어 있는 로그인 사용자 정보 삭제
<로그인/로그아웃 구현>
1. 로그인, 로그아웃 처리 URL 설정
security > WebSecurityConfig
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity // 스프링 Security 지원을 가능하게 함
public class WebSecurityConfig {
@Bean
public BCryptPasswordEncoder encodePassword() {
return new BCryptPasswordEncoder();
}
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
// h2-console 사용에 대한 허용 (CSRF, FrameOptions 무시)
return (web) -> web.ignoring()
.antMatchers("/h2-console/**");
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// 회원 관리 처리 API (POST /user/**) 에 대해 CSRF 무시
http.csrf()
.ignoringAntMatchers("/user/**");
http
.authorizeHttpRequests((authz) -> authz
// image 폴더를 login 없이 허용
.antMatchers("/images/**").permitAll()
// css 폴더를 login 없이 허용
.antMatchers("/css/**").permitAll()
// 회원 관리 처리 API 전부를 login 없이 허용
.antMatchers("/user/**").permitAll()
// 어떤 요청이든 '인증'
.anyRequest().authenticated()
)
// [로그인 기능]
.formLogin()
// 로그인 View 제공 (GET /user/login)
.loginPage("/user/login")
// 로그인 처리 (POST /user/login)
.loginProcessingUrl("/user/login")
// 로그인 처리 후 성공 시 URL
.defaultSuccessUrl("/")
// 로그인 처리 후 실패 시 URL
.failureUrl("/user/login?error")
.permitAll()
.and()
// [로그아웃 기능]
.logout()
// 로그아웃 처리 URL
.logoutUrl("/user/logout")
.permitAll();
return http.build();
}
}
2. DB 의 회원 정보 조회 → 스프링 시큐리티의 "인증 관리자" 에게 전달
- UserDetailsService 구현
- UserDetailsService 인터페이스(기본 스프링시큐리티) → UserDetailsServiceImpl 클래스(직접 만들어야함)
security > UserDetailsServiceImpl
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserRepository userRepository;
@Autowired
public UserDetailsServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("Can't find " + username));
return new UserDetailsImpl(user);
}
}
- UserDetails 구현
- UserDetails 인터페이스 → UserDetailsImpl 클래스(직접구현)
security > UserDetailsImpl
public class UserDetailsImpl implements UserDetails {
private final User user;
public UserDetailsImpl(User user) {
this.user = user;
}
public User getUser() {
return user;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.emptyList();
}
}
설정하는 것도 좋고 default 로그인 기능도 다 좋으나!
이슈가 발생하기 쉬움!
EX)
- 로그인 페이지에 CSS 가 적용되지 않음
- 웹 페이지 이슈가 생겼을 때 원인을 파악하고 해결할 수 있는 능력 필요
1) '개발자 도구' 로 에러 내용 확인
2) 해결 방법 : 스프링 시큐리티의 URL 허용 정책 변경 필요 확인
// image 폴더를 login 없이 허용
.antMatchers("/images/**").permitAll()
// css 폴더를 login 없이 허용
.antMatchers("/css/**").permitAll()
'🌿SPRING > 🍀공부 [SPRING]' 카테고리의 다른 글
[SPRING] Spring Security - API 접근 권한 제어 (0) | 2022.08.23 |
---|---|
[SPRING]Spring Security - 패스워드 암호화 (0) | 2022.08.23 |
[SPRING] Spring Security - 쿠키 vs 세션 vs JWT + Token (0) | 2022.08.23 |
[SPRING] spring framwork의 이해 (0) | 2022.08.22 |
[SPRING] 의존성 주입 DI , IoC 컨테이너 (0) | 2022.08.22 |