쌓고 쌓다

Spring Security 필터 본문

프로그래밍/spring

Spring Security 필터

승민아 2024. 1. 17. 13:50

 

 

디렉터리 구조

 

 

 

 

 

CorsConfig.java

package com.example.spotserver.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class CorsConfig  {

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true); // 내 서버가 응답할때 json을 자바스크립트에서 처리할 수 있게 할지를 설정하는 것 (ajax와 같은걸로 요청했을때 응답을 처리할 수 있게 해줄까말까)
        config.addAllowedOrigin("*"); // 모든 IP에 응답 허용
        config.addAllowedHeader("*"); // 모든 헤더에 응답 허용
        config.addAllowedMethod("*"); // 모든 post, get, put, delete 등등 요청을 허용하겠다.
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

@Bean 어노테이션을 사용한 메서드에서 반환되는 객체는 Spring IoC 컨테이너에 빈으로 등록된다.

CorsFilter를 설정하여 빈으로 등록한다.

 

SecurityConfig.java

package com.example.spotserver.config;

import com.example.spotserver.filter.MyFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
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;
import org.springframework.web.filter.CorsFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private CorsFilter corsFilter;

    @Autowired
    public SecurityConfig(CorsFilter corsFilter) {
        this.corsFilter = corsFilter;
    }

    // 해당 메서드의 리턴되는 오브젝트를 IoC로 등록해준다.
    @Bean
    public BCryptPasswordEncoder encodePwd() {
        return new BCryptPasswordEncoder();
    }


    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf(AbstractHttpConfigurer::disable);
        http.sessionManagement(session ->
                session
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)); // 세션 생성 X

        http.authorizeHttpRequests(request ->
                request
                        .requestMatchers("/user/**").authenticated() // 인증만 된다면 들어갈 수 있는 주소
                        .requestMatchers("/manager/**").hasAnyAuthority("admin", "manager")
                        .requestMatchers("/admin/**").hasAuthority("admin")
                        .anyRequest().permitAll());

        http.formLogin(formLogin ->
                formLogin
                        .disable()); // 폼 태그 로그인 안쓰겠다.

        http.httpBasic(httpBasic ->
                httpBasic
                        .disable()); // 기본적인 HTTP 로그인 안쓰겠다. (ID, PW를 항상 포함하여 요청함)

        http.addFilter(corsFilter);
        return http.build();
    }

}
  • corsFilter에는 앞전 메서드에서 빈으로 등록한 객체가 주입된다.
  • JWT를 사용할거라 sessionManagement로 세션을 생성하지 않고, formLogin으로 폼 태그 로그인 안쓰며, httpBasic을 꺼준다.
  • addFilter로 corsFilter를 추가해준다.

 

 

 

이제 내가 만든 필터를 등록해보는 과정을 해보자.

 

MyFilter.java

package com.example.spotserver.filter;

import jakarta.servlet.*;

import java.io.IOException;

public class MyFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("필터1");
        chain.doFilter(request, response);
    }
}

Filter를 구현하면 된다.

chain.doFilter를 작성하지 않으면 이 필터에서 다음 필터로 넘어가지 않고 종료하니 작성해주자.

 

앞전에 모든 로그를 남기는 필터를 작성한적 있으니 참고해봐도 좋을듯하다.

https://non-stop.tistory.com/588

 

FilterConfig.java

package com.example.spotserver.config;


import com.example.spotserver.filter.MyFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterConfig {


    // 시큐리티 필터(필터 체인)가 먼저 다 실행되고 이 필터들이 실행 됌
    // 가장 빨리 실행되고 싶다면 시큐리티 필터에 addFilterBefore를 써주자. (SecurityContextPersistence 앞에하면 가장 빨리 됌)
    @Bean
    public FilterRegistrationBean<MyFilter> filter1() {
        FilterRegistrationBean<MyFilter> bean = new FilterRegistrationBean<>(new MyFilter());
        bean.addUrlPatterns("/*");
        bean.setOrder(0);
        return bean;
    }
}

FilterConfig에서 등록하지 않고 SecurityConfig에서 이 필터를 등록할 수도 있다.

SecurityConfig에 다음 메서드를 사용하여 특정 스프링 시큐리티 필터 Before, After에 끼워 넣을 수도 있다.

 

 

그럼 내가 만든 토큰인지 검사는 어디서 해줄까?

내가 만든 필터를 시큐리티 필터를 거치기전에 돌도록 만든다.

그리고 이 필터에서 토큰을 검증하면 된다.

 

Myilter1.java

package com.example.spotserver.filter;

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

public class MyFilter1 implements Filter {


    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("필터1");


        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;


        // 토큰 : good을 id, pw로 정상적인 로그인이 되었다면 토큰을 만들어주고 그걸 응답으로 주면 된다.
        // 요청때마다 토큰이 넘어오면 내가 만든 토큰이 맞는지 검증하면 됌 (RSA, HS256으로 검증)
        if(req.getMethod().equals("POST")) {
            String authorization = req.getHeader("Authorization");
            System.out.println("authorization = " + authorization);

            if(authorization.equals("good")) {
                chain.doFilter(req, res); // 이때 필터 체인을 타게할거고,
            } else {
                res.setCharacterEncoding("UTF-8");
                PrintWriter writer = res.getWriter();
                writer.println("인증 불가");
            }
        }

    }
}

헤더에서 Authorization을 꺼내 내가 만든 토큰인지 검증하는 로직을 추가하면 될것이다.

Comments