세미 프로젝트 진행 중에 스프링 시큐리티를 적용해야 했다.
기존에 부트 환경에서 WebSecurityConfigurerAdapter
를 상속받아서 클래스단에서 config를 작성하던 나에게는 xml은 새로웠고, 어려웠다. 그래서 레거시 환경에서 xml로 작성하던 시큐리티 환경설정을 config클래스로 변경해야 할 필요를 느꼇고 그러다 보니 숙제가 생겼다. 어떤 설정은 xml로 되어있고, 어떤 설정은 config클래스로 되어있으면 팀원들 입장에서 헷갈릴 수 도 있다고 생각이 들어 팀원들에게 양해를 구하고 기존 레거시의 xml설정을 전부다 config클래스로 변경하였다.
스프링 레거시에 기본적인 xml 환경설정 파일은 대표적으로 web.xml
, root-context.xml
, servlet-context.xml
이 있다. 하나씩 차례대로 알아보자
우리가 익히 보던 web.xml
파일이다. 해당 파일을 자바 config클래스로 만들기 위해 Spring Framework의 Initializer 클래스인 AbstractAnnotationConfigDispatcherServlet
를 사용해서 구현할 것이다. 해당 클래스는 Dispatcher Servlet을 등록하고 루트 어플리케이션을 구성하여 Spring 웹 응용 프로그램을 초기화 하는 편리한 방법을 제공한다.
public class WebConfig
extends AbstractAnnotationConfigDispatcherServletInitializer{
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {RootConfig.class, SecurityConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] {ServletContext.class};
}
@Override
protected String[] getServletMappings() {
return new String[] {"/"};
}
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter
= new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
return new Filter[] { characterEncodingFilter };
}
}
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {RootConfig.class, SecurityConfig.class}; --- 1
}
우리가 기존에 사용하던 xml파일의 경로를 설정해주는
web.xml
의 상단 부분이다. (해당 파일은 초기 설정이라security.xml
경로가 지정되어 있지않다.)
기존에 해당root-context.xml
와security-context.xml
의 경로를 지정 한 것처럼 모든 xml파일을 java config클래스로 변경해서 적용 시킬 것 이므로RootConfig.class
와SecurityConfig.class
를 등록해준다.
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] {ServletContext.class};
}
@Override
protected String[] getServletMappings() {
return new String[] {"/"};
}
servlet-context.xml
를 등록하는 부분이다.web.xml
에서는 /WEB-INF/spring/appServlet안에 있는servlet-context.xml
파일을 읽어서 실행시킬 것이다. 나는servlet-context.xml
파일 또한 class로 만들어 사용할 것 이므로ServletContext.class
를 등록해준다. 또한web.xml
이미지를 보면 servlet-mapping 부분의 url-pattern또한getServletMappings()
를 통해 설정한 것을 볼 수 있다.
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter
= new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
return new Filter[] { characterEncodingFilter };
}
web.xml
의 마지막 부분의 필터 부분이다. filter-class의 설정부분을 보자.org.springframework.web.filter.CharacterEncodingFilter
클래스를 사용하고 있는데 java code를 보면 해당 클래스를 new 로 인스턴스로 만들고 xml파일과 똑같이 UTF-8로 설정하고 필터로 등록하는 것을 볼 수 있다.
이렇게 web.xml을 자바 클래스로 변경했다. 그렇다면 추가적으로 RootConfig.class
, SecurityConfig.class
, ServletContext.class
를 구현해보자.
우리가 자주 보던 servlet-context.xml파일이다. resource의 위치를 지정하는 곳도 있고 뷰 리졸버 설정하는 부분도 있고, 컴포넌트 스캔경로도 지정할 수 있다.
@EnableWebMvc
@ComponentScan(basePackages = {"com.world.Y2K"})
public class ServletContext implements WebMvcConfigurer{
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setViewClass(JstlView.class);
bean.setPrefix("/WEB-INF/views/");
bean.setSuffix(".jsp");
registry.viewResolver(bean);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
}
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setViewClass(JstlView.class);
bean.setPrefix("/WEB-INF/views/");
bean.setSuffix(".jsp");
registry.viewResolver(bean);
}
뷰 리졸버를 설정하는 부분이다. ViewResolver 구현체인
InternalResourceViewResolver
클래스에 Prefix와 Suffix 설정을 해준 것을 볼 수 있다. 그리고 해당 클래스를ViewResolverRegistry
에 등록해준다.
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
resource를 지정하는 것은 직관적으로 동일하다.
@ComponentScan(basePackages = {"com.world.Y2K"})
해당 서블릿.xml파일이 스캔할 경로를 지정하는 것인데, 이는 클래스단에서 어노테이션을 통해 설정해주었다.
스프링이 컨테이너에 올릴 수 있게 빈을 등록하는 root-context.xml파일이다. 이를 클래스로 변경해보자.
@Configuration
public class RootConfig {
@Autowired
private ApplicationContext applicationContext;
@Bean
public SqlSessionTemplate sqlSessionTemplate() throws Exception {
return new SqlSessionTemplate((SqlSessionFactory) sqlSessionFactoryBean());
}
@Bean
public SqlSessionFactory sqlSessionFactoryBean() throws Exception {
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setConfigLocation(applicationContext.getResource("classpath:/mybatis-config.xml"));
sqlSessionFactory.setDataSource(dataSource());
return (SqlSessionFactory)sqlSessionFactory.getObject();
}
@Bean
public DataSourceTransactionManager transactionManager() {
DataSourceTransactionManager transaction = new DataSourceTransactionManager();
transaction.setDataSource(dataSource());
return transaction;
}
@Bean
public BasicDataSource dataSource() {
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
basicDataSource.setUrl("jdbc:oracle:thin:@localhost:1521:{pid}");
basicDataSource.setUsername("{username}");
basicDataSource.setPassword("{password}");
return basicDataSource;
}
@Bean
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
commonsMultipartResolver.setMaxUploadSize(100000000);
commonsMultipartResolver.setMaxInMemorySize(100000000);
return commonsMultipartResolver;
}
}
(클래스경로에서 리소스를 가져오는 데 사용되는
ApplicationContext
객체를 의존성 주입한다.)
@Bean
public BasicDataSource dataSource() {
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
basicDataSource.setUrl("jdbc:oracle:thin:@localhost:1521:{pid}");
basicDataSource.setUsername("{username}");
basicDataSource.setPassword("{password}");
return basicDataSource;
}
xml에 명시된 드라이버 클래스 이름, URL, 사용자 이름 및 암호와 같은 데이터 소스의 속성을 설정하는 클래스를 인스턴스로 만들고 위의 설정과 같이 set을 통해 설정해주고 해당
BasicDataSource
의 인스턴스를 리턴해준다.
@Bean
public SqlSessionFactory sqlSessionFactoryBean() throws Exception {
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setConfigLocation(applicationContext.getResource("classpath:/mybatis-config.xml"));
sqlSessionFactory.setDataSource(dataSource());
return (SqlSessionFactory)sqlSessionFactory.getObject();
}
처음에는 헷갈렷던 부분인데, 해당 xml파일에는
SqlSessionFactoryBean
을 빈으로 등록한 것을 보고 자바 클래스 단에서 SqlSessionFactoryBean으로 반환하려니 에러가 발생했다. 알고보니 SqlSessionFactory의 구현체 이었고, 따라서 Mybatis SQL 세션을 생성하는 데 사용되는 SqlSessionFactoryBean의 인스턴스에 세팅을 하고 해당 인스턴스를 다운캐스팅을 통해 반환해준다.
@Bean
public SqlSessionTemplate sqlSessionTemplate() throws Exception {
return new SqlSessionTemplate((SqlSessionFactory) sqlSessionFactoryBean());
}
@Bean
public DataSourceTransactionManager transactionManager() {
DataSourceTransactionManager transaction = new DataSourceTransactionManager();
transaction.setDataSource(dataSource());
return transaction;
}
각 xml문서에 명시된 대로 규칙에 맞게 Mybatis SQL 세션을 관리하는 데 사용되는
SqlSessionTemplate
을 빈으로 등록하고, 데이터 원본에 대한 트랙잭션을 관리하는데 사용되는DataSourceTransactionManager
을 빈으로 등록한다.
@Bean
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
commonsMultipartResolver.setMaxUploadSize(100000000);
commonsMultipartResolver.setMaxInMemorySize(100000000);
return commonsMultipartResolver;
}
xml파일 설정과 동일하게 업로드 사이즈와 메모리 사이즈를 지정해준다.
xml파일들을 전부다 클래스로 바꾼 것은 스프링 시큐리티 설정을 xml파일에서 config클래스로 사용하기 위함이었다. 지금 현재 우리는 web.xml을 자바 클래스단에서 구현했기 때문에 스프링에 등록해야 할 녀석이 있다.
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer{
}
AbstractSecurityWebApplicationInitializer
을 이용하면 web.xml에 spring security 필터를 직접 등록하지 않고도 사용할 수 있게 한다. Spring에 의해 자동으로 탐지되며 security config클래스의 설정을 자동으로 로드해준다.
여기 까지 했으면 수많은 구글에 있는 스프링시큐리티 config 클래스 설정들을 적용 시킬 수 있다. 이 글에서는 spring security의 환경설정에 관한 글이 아니므로, 여기서 마치겠다.