Spring Security - 기본 API 및 Filter

mul·2024년 4월 22일

Spring Security

목록 보기
2/2

기본 API 및 Filter

인프런 정수원 지식공유자님의 스프링 시큐리티 강의를 수강하고 공부한 내용을 정리한 글입니다.

📌 인증 API
Spring Security는 프로젝트에 의존성(dependency)를 추가하여 사용하는 프레임워크로 다양한 인증 API를 제공한다.

<!-- spring security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

의존성을 추가하면 아래와 같은 일이 이루어진다.

서버 기동
👉🏻 Spring Security 초기화
👉🏻 웹 보안 기능 활성화
👉🏻 보안 기능이 시스템에 반영되어 작동

📌 사용자 정의 보안 기능
WebSecurityConfigurerAdapter를 상속받은 사용자 정의 보안 설정 클래스(SecurityConfig)를 작성하여 HttpSecurity가 제공하는 API를 사용하여 세부적인 보안 기능을 설정할 수 있다.

WebSecurityConfigureAdapter
🫧 기본적인 보안 기능 활성화
🫧 시스템이 보안 기능 작동하도록 설정
🫧 초기화 작업
🫧 HttpSecurity 생성

📌 Form Login 인증
사용자가 인증이 필요한 자원에 접근하려고 할 때, 로그인 페이지로 유도하여 사용자가 인증을 요청(POST form data : username + password)하여
🙂 인증에 성공하면 SESSION 및 인증 토큰을 생성 / 저장하고, 세션에 저장된 인증 토큰으로 접근이 가능하도록 한다.
😞 인증에 실패하면 로그인 페이지로 redirect 한다.

http.formLogin() : Form 로그인 인증 기능이 작동

http.formLogin()
			.loginPage("/login.html")               // 사용자 정의 로그인 페이지 경로
			.defaultSuccessUrl("/home")             // 로그인 성공 후 이동 페이지 경로
			.failureUrl("/login.html?error=true")   // 로그인 실패 후 이동 페이지 경로
			.usernameParameter("username")          // 아이디 파라미터명 설정 -> form 태그 명
			.passwordParameter("password")          // 패스워드 파라미터명 설정 -> ui 화면 값과
			.loginProcessingUrl("/login")           // 로그인 Form Action Url -> 동일하게 해야 함
			.successHandler(loginSuccessHandler())  // 로그인 성공 후 핸들러 -> 로그인 성공 이후 작업 처리
			.failureHandler(loginFailureHandler())  // 로그인 실패 후 핸들러 -> 로그인 실패 이후 작업 처리

📌 Form Login 인증 필터 : UsernamePasswordAuthenticationFilter
Form Login 요청 시 Filter를 통해 인증 과정을 거친다.

UsernamePasswordAuthenticationFilter
🫧 인증 처리 관련 요청을 담당
AntPathRequestMatcher(/login)
🫧 요청 정보의 url이 login으로 시작되는지 검사 (default는 login이지만 사용자가 설정할 수 있다)
🫧 인증 정보가 일치하지 않으면 다음 필터로 이동(chain.doFilter)
🫧 인증 정보가 일치하면 Authentication 객체 생성
Authentication(Username + Password)
🫧 사용자가 로그인 할 때 입력한 Username + Password를 저장하여 실질적 인증처리를 맡기는 역할
AuthenticationManager
🫧 Filter로부터 처음 인증 객체를 전달받고 인증 처리
🫧 AuthenticationProvider 객체 중 하나를 선택하여 인증 처리 위임
🫧 Provider로 받은 최종적인 인증 객체를 다시 Filter로 return
AuthenticationProvider
🫧 실질적 인증처리 담당
🫧 인증 성공 / 실패 결과를 반환
🫧 인증에 성공하면 Authentication 객체를 생성, 인증에 성공한 정보를 객체에 저장한 후, AuthenticationManager로 return
🫧 인증에 실패하면 AuthenticationException 예외 발생
AuthenticationException
🫧 인증 예외 발생 시 최종적으로 인증 실패 후 후속 작업 처리
Authentication(User + Authorities)
🫧 인증에 성공하여 받은 객체에는 인증 결과 User 객체와 권한 정보(Authorities)를 return 받는다
🫧 SecurityContext(= 인증 객체를 저장, 보관하는 저장소) 객체에 저장
SecurityContext
🫧 인증 객체를 저장, 보관하는 저장소, 보관소
🫧 session에도 저장하여 전형적으로 사용자가 객체를 참조할 수 있도록 처리하는 작업이 가능
SuccessHandler
🫧 인증 성공 이후의 작업을 처리

📌 Logout 처리
사용자가 Logout을 요청하면 세션 무효화, 인증 토큰 삭제(SecurityContext 객체도 함께 삭제), 쿠키 정보 삭제 과정을 거치고 로그인 페이지로 redirect한다.

http.logout() : 로그아웃 기능이 작동

protected void configure(HttpSecurity http) throws Exception {
	http.logout()                                     // 로그아웃 처리
				.logoutUrl("/logout")                         // 로그아웃 처리 URL
				.logoutSuccessUrl("/login")                   // 로그아웃 성공 후 이동페이지
				.deleteCookies("JSESSIONID", "remember-me")   // 로그아웃 후 쿠키 삭제
				.addLogoutHandler(logoutHandler())            // 로그아웃 핸들러
				.logoutSuccessHandler(logoutSuccessHandler()) // 로그아웃 성공 후 핸들러
}

📌 LogoutFilter

LogoutFilter
🫧 사용자로부터 로그아웃 요청을 받아서 로그아웃 처리
AntPathRequestMatcher(/logout)
🫧 맞는 정보로 로그아웃을 요청하고 있는지 검사
🫧 일치하지 않으면 다음 필터로 이동하고 로그아웃 처리하지 않음
Authentication
🫧 SecurityContext로부터 인증 객체를 꺼내고 SecurityContextLogoutHandler에게 전달
SecurityContextLogoutHandler
🫧 css는 무효화, 쿠키, SecurityContext 객체 삭제
🫧 인증 객체 null로 초기화
SimpleUrlLogoutHandler
🫧 SecurityContextLogoutHandler가 성공적으로 종료되면 LogoutFilter가 SimpleUrlLogoutHandler를 호출
🫧 로그인 페이지로 이동하도록 처리

📌 Remember Me 인증
설정 클래스에서 Remember Me를 활성화하면 Spring Security가 Rememeber Me 인증 기능을 제공한다.

Remember Me 인증 기능
1. 세션이 만료되고 웹 브라우저가 종료된 후에도 어플리케이션이 사용자를 기억하는 기능
2. Remember-Me 쿠키에 대한 Http 요청을 확인한 후 토큰 기반 인증을 사용해 유효성을 검사하고 토큰이 검증되는 사용자는 로그인
3. 사용자 라이프 사이클
⠀⠀🫧 인증 성공 (Rememeber-Me 쿠키 설정)
⠀⠀🫧 인증 실패 (쿠키가 존재하면 쿠키 무효화)
⠀⠀🫧 로그아웃 (쿠키가 존재하면 쿠키 무효화

http.rememberMe() : rememberMe 기능이 작동

protected void configure(HttpSecurity http) throws Exception {
	http.rememberMe()
				.rememberMeParameter("remember")       // default는 remember-me -> 사용자 화면 파라미터 명과 일치해야 함
				.tokenValiditySeconds(3600).           // 쿠키 만료 시간, default는 14일
				.alwaysRemember(true)                  // 리멤버 미 기능이 활성화되지 않아도 항상 실행, default는 false
				.userDetailsService(userDetailService) // 사용자 계정을 조회하는 처리 과정에 필요한 클래스, 리멤버 미 인증할 때 필요한 설정, 반드시 해줘야 함
}

📌 Remember Me 인증 필터 : RememberMeAuthenticationFilter

RememberMeAuthenticationFilter
🫧 사용자 요청을 받아서 그 요청을 처리하는 조건이 있다.
1. Authentication 인증 객체가 null일 경우 (인증 받지 않은 사용자)
2. 사용자가 최초의 Form을 받을 당시 RememberMe를 활성화하여 RememberMe 쿠키를 발급받은 경우
에 RememberMeAuthenticationFilter가 동작하여, 사용자 인증을 다시 받게 만들어 인증을 유지해 사용자가 계속 서버에 접속할 수 있도록 한다.
RememberMeService
🫧 실제로 RememberMe 인증 처리하는 TokenBasedRememberMeServices, PersistentTokenBasedRememberMeService를 구현체로 가진다
🫧 Token Cookie를 추출하고
🫧 토큰이 RememberMe 이름을 가진 토큰인지 검사
🫧 토큰이 존재하면 다음으로 진행, 존재하지 않으면 다음 필터로 이동
🫧 토큰이 존재하면 Decode Token (토큰의 포맷이 정상적으로 규칙을 지킨 토큰인지 판단)
🫧 정상 토큰이면 사용자가 요청한 토큰의 값과 서버에 저장된 토큰의 값이 일치하는지 확인
🫧 토큰이 일치하면 토큰의 유저 정보를 DB에서 조회하고, 해당 쿠키에 포함된 유저 정보가 DB에 존재하는지 확인
🫧 유저 정보가 DB에 존재하면, 새로운 Authentication 인증 객체(RememberMeAuthenticationToken)를 생성하고
🫧 AuthenticationManager 인증 관리자에게 전달하여 실질적 인증 처리를 위임한다
TokenBasedRememberMeServices
🫧 메모리에 저장한 토큰과 사용자가 요청한 쿠키 토큰과 비교하여 인증 처리
🫧 토큰 유효기간은 일반적으로 14일
PersistentTokenBasedRememberMeServices
🫧 DB에 서비스 발급한 토큰을 저장, 사용자가 요청하는 클라이언트 값과 DB값을 비교하여 인증 처리
🫧 영구적

📌 익명사용자 인증 필터 : AnonymousAuthenticationFilter
사용자가 자원에 접근을 시도하면 session에 저장된 User 객체가 존재하는지 판단하고, null이라면 인증 받지 않은 사용자라고 판단하고 자원으로 접근하지 못 하게 한다.

AnonymousAuthenticationFilter
🫧 사용자가 request를 요청하면, 요청한 사용자의 Authentication 인증 객체 존재 여부를 판단
🫧 인증 객체가 존재하면(인증 받은 사용자) 다음 필터로 이동
🫧 인증 객체가 존재하지 않으면(익명 사용자) 익명 사용자용 인증 객체(AnonymousAuthenticationToken) 생성(null이 아니다)하여 SecurityContext 객체 안에 익명 사용자용 토큰 객체 저장 (=인증받지 않은 사용자를 null로 처리하는 것이 아니라 별도의 익명 사용자용 인증 객체를 생성)

🫧 익명 사용자 인증 처리 필터
🫧 익명 사용자와 인증 사용자를 구분해서 처리하기 위한 용도로 사용
🫧 화면에서 인증 여부를 구현할 때 isAnonymous()와 isAuthenticated()로 구분해서 사용
🫧 인증 객체를 세션에 저장하지 않는다

📌 동시 세션 제어
동일한 계정으로 인증을 받을 때 생성되는 세션의 허용 개수를 초과한 경우, 어떻게 계속적으로 초과하지 않고 세션을 유지하는지에 대한 제어이다.

Spring Security는 2가지 방법으로 제어한다.
1. 이전 사용자 세션 만료
2. 현재 사용자 인증 실패

http.sessionManagement() : 세션 관리 기능이 작동

protected void configure(HttpSecurity http) throws Exception {
	http.sessionManagement()
				.maximunSessions(1)            // 최대 허용 가능 세션 수, -1 : 무제한 로그인 세션 허용
				.maxSessionPreventsLogin(true) // 동시 로그인 차단 -> 세션을 생성하지 못 하도록 함 (현재 사용자 인증 실패 방법), false : 기존 세션 만료 (default) (이전 사용자 세션 만료 방법)
				.invalidSessionUrl("/invalid") // 세션이 유효하지 않을 때 이동할 페이지
				.expiredUrl("/expired")        // 세션이 만료될 경우 이동 할 페이지 (invalid와 expired 모두 설정하였다면 invalid가 우선시 됨)
}

📌 세션 고정 보호

🗡 세션 고정 공격 : 사용자와 공격자가 세션을 공유하여, 공격자는 인증을 하지 않고 세션만으로 자원에 접근이 가능

🛡 세션 고정 보호 : 인증이 성공할 때마다 새로운 Session을 생성하고 Sessionid를 발급하여 세션 고정 공격에 대비한다.

http.sessionManageMent() : 세션 관리 기능이 작동

protected void configure(HttpSecurity http) throws Exception {
	http.sessionManagement()
				.sessionFixation().changeSessionID() // 기본값 ↔ none, migrateSession, newSession
				// changeSessionID 사용자가 인증에 성공하면 세션은 그대로, 세션 아이디만 변경 (서블릿 3.1 이상에서 기본값으로 설정)
				// migrateSession 새로운 세션도 생성, 세션 아이디로 새로 발금 (서블릿 3.1 이하에서 기본값으로 설정)
				// → 세션에 설정한 여러 속성값을 그대로 사용할 수 있도록 처리
				// newSession 세션과 세션 아이디가 새롭게 발급되지만, 이전 세션에서 설정한 속성값들을 사용할 수 없음
				// none 세션이 새롭게 생성되지 않음 → 세션 고정 공격에 취약
}

📌 세션 정책
http.sessionManageMent().sessionCreationPolicy()

protected void configure(HttpSecurity http) throws Exception {
	http.sessionManagement()
				.sessionCreationPolicy(SessionCreationPolicy.If_Required)
}

SessionCreationPolicy.Always      // 스프링 시큐리티가 항상 세션 생성
SessionCreationPolicy.If_Required // 스프링 시큐리티가 필요 시 세션 생성 (기본값)
SessionCreationPolicy.Never.      // 스프링 시큐리티가 세션을 생성하지 않지만 이미 존재하면 사용
SessionCreationPolicy.Stateless.  // 스프링 시큐리티가 세션을 생성하지 않고 존재해도 사용하지 않음 → 세션을 사용하지 않는 인증 방식을 투입할 경우 사용 (ex. JWT = JSON WEB TOKEN)

📌 세션 제어 필터 : SessionManagementFilter, ConcurrentSessionFilter
☕️ SessionManagementFilter
⠀⠀🥄 세션 관리 : 인증 시 사용자의 세션 정보를 등록, 조회, 삭제 등의 세션 이력 관리
⠀⠀🥄 동시적 세션 제어 : 동일 계정으로 접속이 허용되는 최대 세션 수를 제한
⠀⠀🥄 세션 고정 보호 : 인증 할 때마다 세션쿠키를 새로 발급하여 공격자의 쿠키 조작을 방지
⠀⠀🥄 세션 생성 정책 : Always, If_Requried, Never, Stateless

☕️ ConcurrentSessionFilter
⠀⠀🥄 매 요청 마다 현재 사용자의 세션 만료 여부 체크
⠀⠀🥄 세션이 만료되었을 경우 즉시 만료 처리
⠀⠀🥄 Session.isExpired() == true : 로그아웃 처리, 즉시 오류 페이지 응답

사용자 로그인 시도
👉🏻 이전에 동일한 계정으로 인증을 시도하여 세션이 생성된 상태라면, 세션 새로 생성한다
👉🏻 최대 세션 허용 개수가 초과되었을 경우 이전 사용자의 세션을 만료(session.expiredNow())
👉🏻 만약 이때 이전 사용자가 서버에 접속한다고 가정하면, ConcurrentSessionFilter는 매 요청마다 session 만료 여부를 체크하기 때문에 이전 사용자의 만료 여부를 확인한다
👉🏻 SessionManagementFilter 안에서 이전 사용자의 세션 만료여부를 확인
👉🏻 세션이 만료되도록 설정되어 즉시 이전 사용자의 Session을 만료, 로그아웃 하고 오류 페이지를 응답한다

위처럼 Session 제어 처리는 SessionManagementFilter와 ConcurrentSessionFilter가 연계해서 동시적으로 처리한다.

1️⃣ user1 로그인 시도
👉🏻 UsernamePasswordAuthenticationFilter가 인증 처리
👉🏻 ConcurrentSessionControlAuthenticationStrategy 클래스 호출
⠀⠀🫧 동시적 세션 제어 처리
⠀⠀🫧인증을 시도하는 사용자 계정으로 생성된 세션 개수 카운팅
👉🏻 ChangeSessionIdAuthenticationStrategy 클래스 호출
⠀⠀🫧 세션 고정 보호 처리
⠀⠀🫧 새롭게 세션을 발급
👉🏻 RegisterSessionAuthenticationStrategy
⠀⠀🫧 사용자 세션 등록, 저장 = 세션 카운트 증가
2️⃣ user2 로그인 시도
👉🏻 UsernamePasswordAuthenticationFilter가 인증 처리
👉🏻 ConcurrentSessionControlAuthenticationStrategy 클래스 호출
⠀⠀🫧 인증을 시도하는 사용자 계정(=user1)으로 생성된 세션 카운트가 최대 허용 세션 개수와 동일하면
⠀⠀🫧 인증 실패 전략
⠀⠀⠀⠀🥚 SessionAuthenticationException(인증 예외) 발생
⠀⠀⠀⠀🥚 세션이 생성되지 못 하고 더 이상의 인증 처리를 막음
⠀⠀⠀⠀🥚 인증 실패
⠀⠀🫧 세션 만료 전략
⠀⠀⠀⠀🥚 session.expireNow()를 통해 user1의 세션을 만료, user2의 인증 성공
👉🏻 ChangeSessionIdAuthenticationStrategy 호출하여 세션 고정 보호 실행
👉🏻 RegisterSessionAuthenticationStrategy 호출하여 세션 정보 등록 (세션 카운드 = 2)
3️⃣ user1 자원 접근 시도
👉🏻 ConcurrentSessionFilter가 user1의 세션 만료 여부를 session.expiredNow()에서 확인
⠀⠀🫧 session.isExpired() == true
⠀⠀🫧 즉시 user1의 세션 만료하도록 logout 처리
⠀⠀🫧 오류 페이지 응답

📌 권한설정과 표현식
🗣 권한설정
💬 선언적 방식

// URL
http.andMatchers("/users/**").hasRole("USER")

// Method
@PreAuthorize("hasRole('USER')")
public void user() {
	System.out.println("user")
}

💬 동적 방식 - DB 연동 프로그래밍

// URL
// Method
@Override
protected void configure(HttpSecurity http) throws Exception {
	http
		.andMatcher("/shop/**") // 설정된 경로로 요청할 때만 설정 클래스의 보안 기능 작동
														// = 특정한 Url 정보로 요청을 받아 인증, 인가 처리
														// 경로 설정을 생략하면 모든 요청에 대해 보안 검사
		.authorizeRequests()
			.antMatchers("/shop/login", "/shop/users/**").permitAll() // 리퀘스트와 설정한 경로 정보(login, users)와 매치해서 동일한 정보가 포함되면 권한 정보를 통해 인가 허용
			.antMatchers("/shop/mypage").hasRole("USER") // mapage요청과 매치된 요청은 인가 심사를 하는데 요청하는 사용자는 User 권한을 가져야 접근 가능
			.antMatchers("/shop/admin/pay").access("hasRole('ADMIN')"); // 상세한 권한 설정이 가능함
			.antMatchers("/shop/admin/**").access("hasRole('ADMIN') or hasRole('SYS')");
			.antRequest().authenticated() // 모든 요청에는 인증을 받은 사용자만이 접근이 가능하도록 설정
}
// 여기에 해당하는 모든 인가 심사를 통과해야 자원에 접근 가능. 하나라도 권한이 없으면 최종적으로 해당 자원에 접근이 불가능
// !! 설정 시 구체적인 경로가 먼저 오고 그것보다 큰 범위의 경로가 뒤에 오도록 해야 한다 !!

🗣 표현식

메소드동작
authenticated()인증된 사용자의 접근을 허용
fullyAuthenticated()인증된 사용자의 접근을 허용, rememberMe 인증 제외
permitAll()무조건 접근을 허용 (role과 anonymous 모두 접근 가능)
denyAll()무조건 접근을 허용하지 않음
anonymous()익명 사용자의 접근을 허용 (role 사용자는 접근 불가)
rememberMe()rememberMe를 통해 인증된 사용자의 접근을 허용
access(String)주어진 sql 표현식의 평가 결과가 true면 접근을 허용
hasRole(String)사용자가 주어진 역할이 있다면 접근을 허용
hasAuthority(String)사용자가 주어진 권한이 있다면 접근을 허용
hasAnyRole(String...)사용자가 주어진 역할 중 어떤 것이라도 있다면 접근을 허용
hasAnyAuthority(String...)사용자가 주어진 권한 중 어떤 것이라도 있다면 접근을 허용
hasIpAddress(String)주어진 IP로부터 요청이 왔다면 접근을 허용

위 표현식을 사용하여 SecurityConfig 내에서 사용자를 생성할 수도 있지만 운영 서비스에 추천하는 방식은 아니다.

// SecurityConfig.java

// 사용자 생성
@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		// AuthenticationManagerBuilder 클래스 → 사용자를 생성하고 권한 설정 기능 제공
		// 해당 메소드를 override하여 사용자 생성
		auth.inMemoryAuthentication().withUser("user").password("{noop}1111").roles("USER");
		// {noop} → password의 prefix → password 암호화 할 때의 특정한 password 유형을 prefix로 작성해주어야 함
		// {noop}은 평문으로 암호화가 이루어지도록 하는 prefix
		auth.inMemoryAuthentication().withUser("sys").password("{noop}1111").roles("SYS");
		auth.inMemoryAuthentication().withUser("admin").password("{noop}1111").roles("ADMIN");
		
	}

// 권한 설정
@Override
	protected void configure(HttpSecurity http) throws Exception {
		// 운영 서비스에서 추천하는 방식이 아님
		// 운영 서비스에서는 자원에 해당하는 권한 설정들을 그때마다 실시간적으로 즉각 설정하고 서비스되어야 함
		http
			.authorizeRequests()
			.antMatchers("/user").hasRole("USER") // 사용자가 /user 요청을 하면 사용자의 권한과 설정된 권한이 일치하는지 확인 = 사용자가 설정된 url로 자원을 요청하면 권한 심
			.antMatchers("admin/pay").hasRole("ADMIN") // 구체적인 범위가 상위에 작성되어야 
			.antMatchers("admin/**").access("hasRole('ADMIN') or hasRole('SYS')") // ADMIN 권한이 USER나 SYS 권한에 접근할 수 x, 이후 권한 계층을 통해 설정해야 함
			.anyRequest().authenticated();
		http
			.formLogin();
	}

📌 예외 처리 및 요청 캐시 필터 : ExceptionTranslationFilter, RequestCacheAwareFilter
☕️ExceptionTranslationFilter
⠀⠀🥄 크게 두 가지 종류의 예외(AuthenticationException = 인증 예외, AccessDeniedException = 인가 예외)를 처리
⠀⠀🥄 해당 예외를 발생시키는 필터 = FilterSecurityIntercept
⠀⠀⠀⠀🫧 Spring Security가 관리하는 보안 필터 중 맨 마지막에 위치
⠀⠀⠀⠀🫧 해당 필터 앞에 위치하는 필터가 ExceptionTranslationFilter
⠀⠀⠀⠀🫧 ExceptionTranslationFilter가 try-catch로 감싸서 요청을 전달FilterSecurityIntercept로 전달
⠀⠀⠀⠀🫧 FilterSecurityIntercept는 인증 예외와 인과 예외를 ExceptionTranslationFilter로 through
⠀⠀⠀⠀🫧 ExceptionTranslationFilter는 인증 예외와 인과 예외를 받아서 처리

⠀⠀🥄 AuthenticationException : 인증 예외 처리
⠀⠀⠀⠀🫧 AuthenticationEntryPoint 호출
⠀⠀⠀⠀⠀⠀로그인 페이지 이동, 401 오류 코드 전달 등
⠀⠀⠀⠀⠀⠀사용자가 다시 인증을 시도할 수 있도록 함
⠀⠀⠀⠀🫧 인증 예외가 발생하기 전의 요청 정보를 저장
⠀⠀⠀⠀⠀⠀사용자의 경로를 저장 (로그인한 사용자만 접근할 수 있는 자원에 사용자가 접근을 시도했을 때, 로그인 페이지로 이동해 인증을 유도하고, 로그인에 성공하면 로그인 페이지로 이동하기 전 페이지로 이동) = 캐싱
⠀⠀⠀⠀⠀⠀RequestCache : 사용자의 이전 요청 정보를 세션에 저장하고 이를 꺼내오는 메커니즘
⠀⠀⠀⠀⠀⠀SavedRequest : 사용자가 요청했던 Request 파라미터 값들, 그 당시의 헤더값들 등이 저장
⠀⠀🥄 AccessDeniedException : 인가 예외 처리
⠀⠀⠀⠀🫧 AccessDeniedHandler에서 예외 처리하도록 제공
⠀⠀⠀⠀사용자가 권한이 없어 자원에 접근이 불가능하다는 메시지를 전달
⠀⠀⠀⠀Handler 안에서 후속 처리

사용자가 인증하지 않고 user 자원에 접근한다고 가정했을 때
FilterSecurityInterceptor
🫧 맨 마지막에 위치한 인과처리를 수행하는 필터
🫧 권한 설정에 따라 인과 예외 발생 (익명 사용자가 접근하는 것이므로 인증 예외는 발생하지 않음)
🫧 AccessDeniedException 호출
AccessDeniedException
🫧 익명 사용자 / rememberMe 사용자일 경우에는 AccessDeniedHandler를 호출하지 않고 AuthenticationException로 이동
AuthenticationException
🫧 SecurityContext를 null로 처리하고 AuthenticationEntryPoint 구현체 호출
AuthenticationEntryPoint
🫧 구현체 안에서 로그인 페이지로 이동 (인증 예외가 발생해 로그인하도록 유도)
HttpSessionRequestCache
🫧 예외가 발생하지 이전에 DefaultSaveRequest 객체에 사용자의 url 정보 저장
🫧 객체는 session에 저장
🫧 세션에 저장하는 역할은 HttpSessionRequestCache가 담당

인증을 받아서 자원에 접근하려는데 자원 접근 권한이 없다고 가정했을 때
FilterSecurityInterceptor
🫧 인가 예외 발생
ExceptionTranslationFilter
🫧 AccessDeniedException 호출
AccessDeniedException
🫧 AccessDeniedHandler 호출해서 후속 작업 처리
🫧 denied 페이지 호출 (자원에 접근 권한이 없음을 알리는 페이지)

http.exceptionHandling() : 예외 처리 기능이 작동함

protected void configure(HttpSecurity http) throws Exception {
	http.exceptionHandling()
				.authenticationEntryPoint(authenticationEntryPoint()) // 인증실패 시 처리 (개발자가 직접 클래스 구현, 해당 클래스를 호출)
				.accessDeniedHandler(accessDeniedHandler())           // 인증실패 시 처리 (인터페이스를 구현, 해당 인터페이스를 호출)
}

0개의 댓글