02.LoginFilter
LoginFilter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import java.io.IOException;
@RequiredArgsConstructor
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
// UsernamePasswordAuthenticationFilter는 원래 HttpSecurity#formLogin에서 진행을 했는데
// jwt를 위해 formLogin을 disable 시켰기 때문에 개발자가 구현해줘야 한다
private final AuthenticationManager authenticationManager;
@Override
// 미승인 Authentication 생성하는 메소드
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException{
// 요청을 가로채서 username과 password를 추출
String username = obtainUsername(request);
String password = obtainPassword(request);
// 필터를 타는지 확인
System.out.println("username : " + username);
// 스프링 시큐리티에서 username과 password를 검증하기 위해서는 token에 담아야 한다
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(username, password, null);
// username, password, role
// token에 담은 검증을 AuthenticationManager로 전달
return authenticationManager.authenticate(authToken);
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
System.out.println("로그인 성공");
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
System.out.println("로그인 실패");
}
}
Security Config 클래스
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import lombok.AllArgsConstructor;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
@AllArgsConstructor
public class SecurityConfig {
private final AuthenticationConfiguration authenticationConfiguration;
// 로그인필터의 생성자를 위해
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception{
return configuration.getAuthenticationManager();
}
// 스프링 시큐리티에서 제공하는 인증, 인가를 위한 필터 모음
// Application Context 초기화가 이루어 지면서 HttpSecurity 객체가 설정한 filterChain 형성
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable);
// == .csrf((auth) -> auth.disable());
// CSRF(Cross-Site-Request Forgery 보호를 비활성화하는 메서드 호출
// AbstractHttpConfigurer::disable -> AbstractHttpConfigurer에 정의된 disable 메소드에 대한 참조
http
.formLogin(AbstractHttpConfigurer::disable);
// == formLogin((auth) -> auth.disable());
http
.httpBasic(AbstractHttpConfigurer::disable);
http
.authorizeHttpRequests(
(authorizeRequest) -> authorizeRequest
.requestMatchers("/", "/api/users/login", "/api/users/loginProc", "/api/users/join" , "/api/users/joinProc").permitAll()
.requestMatchers("/api/users/admin").hasRole("ADMIN")
.anyRequest().authenticated()
);
// jwt 구현으로 인해 수동적으로 loginFilter를 만들었기 때문에 시큐리티에서 filter를 인식하도록 해야함
// 필터등록은 addFilter가 있는데
// addFilterAt() 은 그 자리에 등록 , addFilterAfter()는 특정 필터 뒤에 등록 , addFilterBefore() 특정 필터 전에 등록
// 첫번째 인자는 무슨 필터를, 두번째 인자는 위치
// UsernamePasswordAuthenticationFilter의 역할을 수동적으로 만들어줬기 때문에 위치가 여기가 됨
// 로그인 진행 url을 따로 만들어서 맵핑해두었다면
LoginFilter loginFilter = new LoginFilter(authenticationManager(authenticationConfiguration));
loginFilter.setFilterProcessesUrl("/api/users/loginProc");
// 필터가 실행되기 위해 유입되는 경로
http
.addFilterAt(loginFilter, UsernamePasswordAuthenticationFilter.class);
// 세션 설정. STATELESS 상태로 변경
http
.sessionManagement((session) -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
}