[스프링] spring security를 사용하자.

구동현·2024년 2월 2일

스프링

목록 보기
21/21

User 관련 회원가입, 로그인, 인증/인가 기능을 구현하며,
AuthFilter를 통해 관리를 해왔다.

@Slf4j(topic = "AuthFilter")
@Component
@Order(2)
@RequiredArgsConstructor
public class AuthFilter implements Filter {

    private final UserRepository userRepository;
    private final JwtUtil jwtUtil;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String url = httpServletRequest.getRequestURI();

        //url이 존재하고, 회원가입, 로그인 관련 API 일 경우, 인증 필요없이 요청 진행
        if (StringUtils.hasText(url) && (url.startsWith("/user") || url.startsWith("/css") || url.startsWith("/js"))) {
            chain.doFilter(request, response); // 다음 Filter 로 이동
        } else {
            // 나머지 API 요청은 인증 처리 진행
            // 토큰 확인, 원래 하던대로, 쿠키로 뽑아올 수는 없음
            String tokenValue = jwtUtil.getTokenFromRequest(httpServletRequest);
            log.info("url :" + url);

            if (StringUtils.hasText(tokenValue)) { // 토큰이 존재하면 검증 시작
                // JWT 토큰 substring
                String token = jwtUtil.substringToken(tokenValue);

                // 토큰 검증
                if (!jwtUtil.validateToken(token)) {
                    throw new IllegalArgumentException("Token Error");
                }

                // 토큰에서 사용자 정보 가져오기
                Claims info = jwtUtil.getUserInfoFromToken(token);

                User user = userRepository.findByUsername(info.getSubject()).orElseThrow(() ->
                        new NullPointerException("Not Found User")
                );

                request.setAttribute("user", user);
                chain.doFilter(request, response); // 다음 Filter 로 이동
            } else {
                throw new IllegalArgumentException("Not Found Token");
            }
        }
    }

}

메커니즘은 다음과 같다.

  1. url을 불러와서, 해당url이 user관련 api일 경우, pass 아닐경우 계속 진행한다.
  2. http에서 토큰을 꺼내온다.
  3. 토큰에서 JWT 토큰을 가져온다.
  4. 토큰이 유효한지 검증한다.
  5. 토큰과 같은 사용자정보를 가져온다.
  6. 다음 필터로 이동한다.

Spring Security를 사용해 다시 구현해보자.

의존성 추가

implementation 'org.springframework.boot:spring-boot-starter-security'

annotation 추가

@Configuration
@EnableWebSecurity // Spring Security 지원을 가능하게 함
public class WebSecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // CSRF 사용하지 않게 설정
        http.csrf(AbstractHttpConfigurer::disable);

        http.authorizeHttpRequests((authorizeHttpRequests) ->
                authorizeHttpRequests
                        .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll() // resources 접근 허용 설정
                        .requestMatchers("/user/**").permitAll() // user 관련 url 은 접근 허용
                        .anyRequest().authenticated() // 그 외 모든 요청 인증처리
        );

        // 로그인 사용
        http.formLogin(Customizer.withDefaults());

        return http.build();
    }
}

@EnableWebSecurity

  • spring security 자원임을 선언하게 함

@Bean 빈 수동등록

  • securityFilterChain을 빈으로 등록한다.

CSRF

  • 대충 RestApi에서는 필요가 없다고 한다.

http.formLogin(Customizer.withDefaults());

  • security에서 제공하는 로그인폼을 사용한다.
		http.authorizeHttpRequests((authorizeHttpRequests) ->
                authorizeHttpRequests
                        .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll() // resources 접근 허용 설정
                        .requestMatchers("/user/**").permitAll() // user 관련 url 은 접근 허용
                        .anyRequest().authenticated() // 그 외 모든 요청 인증처리
        );

resources관련 접근허용, user관련 접근허용 나머지는 인증을 처리한다.

사용목적에 맞게 코드 수정

@Configuration
@EnableWebSecurity // Spring Security 지원을 가능하게 함
public class WebSecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // CSRF 설정
        http.csrf((csrf) -> csrf.disable());

        http.authorizeHttpRequests((authorizeHttpRequests) ->
                authorizeHttpRequests
                        .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll() // resources 접근 허용 설정
                        .requestMatchers("/user/**").permitAll() // '/user/'로 시작하는 요청 모두 접근 허가
                        .anyRequest().authenticated() // 그 외 모든 요청 인증처리
        );

        // 로그인 사용
        http.formLogin((formLogin) ->
                formLogin
                        // 로그인 View 제공 (GET /user/login-page)
                        .loginPage("/user/login-page")
                        // 로그인 처리 (POST /api/user/login)
                        .loginProcessingUrl("/user/login")
                        // 로그인 처리 후 성공 시 URL
                        .defaultSuccessUrl("/")
                        // 로그인 처리 후 실패 시 URL
                        .failureUrl("/user/login-page?error")
                        .permitAll()
        );

        return http.build();
    }
}

로그인 폼을 내 사용목적에 맞게 변경하였다.

profile
개발합시다

0개의 댓글