스프링 시큐리티 설정 요약

1. XML 설정

web.xml

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring/root-context.xml
		/WEB-INF/spring/security-context.xml</param-value>
	</context-param>

security-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:security="http://www.springframework.org/schema/security"
	xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<bean id="customAccessDenied" class="org.example.security.CustomAccessDeniedHandler"></bean>
	<bean id="customLoginSuccessHandler" class="org.example.security.CustomLoginSuccessHandler"></bean>
	<bean id="customLogoutSuccessHandler" class="org.example.security.CustomLogoutSuccessHandler"></bean>
<!-- 	<bean id="customNoOpPasswordEncoder" class="org.example.security.CustomNoOpPasswordEncoder"></bean> -->
	<bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
	<bean id="customUserDetailsService" class="org.example.security.CustomUserDetailsService"></bean>
	
	<security:http>
		<security:intercept-url pattern="/sample/all" access="permitAll"/>
		<security:intercept-url pattern="/sample/member" access="hasRole('ROLE_MEMBER')"/>
		<security:intercept-url pattern="/sample/admin" access="hasRole('ROLE_ADMIN')"/>
		<security:form-login login-page="/customLogin" authentication-success-handler-ref="customLoginSuccessHandler"/>
		<security:logout logout-url="/customLogout" invalidate-session="true" delete-cookies="remember-me, JSESSIONID" success-handler-ref="customLogoutSuccessHandler"/>
		<security:remember-me data-source-ref="dataSource" token-validity-seconds="604800"/>
	<!-- 	<security:access-denied-handler error-page="/accessError"/> -->
		<security:access-denied-handler ref="customAccessDenied"/>
	</security:http>
	
	<security:authentication-manager>
		<security:authentication-provider user-service-ref="customUserDetailsService">
			<!-- <security:user-service>
				<security:user name="member" password="{noop}member" authorities="ROLE_MEMBER"/>
				<security:user name="admin" password="{noop}admin" authorities="ROLE_MEMBER, ROLE_ADMIN"/>
			</security:user-service> -->
<!-- 			<security:jdbc-user-service data-source-ref="dataSource"/> -->
<!-- 			<security:password-encoder ref="customNoOpPasswordEncoder"/> -->

			<security:password-encoder ref="bcryptPasswordEncoder"/>
			<!-- <security:jdbc-user-service data-source-ref="dataSource" users-by-username-query="select userid, userpw, enabled from tbl_member where userid = ?"
				authorities-by-username-query="select userid, auth from tbl_member_auth where userid = ?"/> -->
		</security:authentication-provider>
	</security:authentication-manager>

</beans>

2. JAVA 설정

SecurityInitializer.java
AbstractSecurityWebApplicationInitializer를 상속받은 SecurityInitializer 클래스를 생성하는 것만으로 Filter 설정이 완료된다.

package org.example.config;

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
	// AbstractSecurityWebApplicationInitializer 를 상속받는 클래스를 추가하는 것 만으로,스프링 내부적으로 DelegatingFilterProxy 를 추가하므로
	// 별도의 구현없이도 필터 설정이 완료됨.
}

SecurityConfig.java : WebSecurityConfigurerAdapter를 상속받는 클래스를 생성한다.
configure(HttpSecurity http) 에는 http 관련 시큐리티 설정을 오버라이드 한다.
configure(AuthenticationManagerBuilder auth) 에는 Provider 관련 시큐리티 설정을 오버라이드 한다.

package org.example.config;

import javax.sql.DataSource;
@Configuration
@EnableWebSecurity
@Log4j
public class SecurityConfig extends WebSecurityConfigurerAdapter{

	@Autowired
	DataSource dataSource;
	
	@Override
	public void configure(HttpSecurity http) throws Exception{
		/*Http 관련 설정
		 * intercept-url
		 * login/logout
		 * remember-me
		 * */
		
		http.authorizeRequests()
		.antMatchers("/sample/all").permitAll()
		.antMatchers("/sample/admin").access("hasRole('ROLE_ADMIN')")
		.antMatchers("/sample/member").access("hasRole('ROLE_MEMBER')");
		
		http.formLogin()
		.loginPage("/customLogin")
		.loginProcessingUrl("/login")
		.successHandler(loginSuccessHandler());
		
		http.logout()
		.logoutUrl("/customLogout")
		.invalidateHttpSession(true)
		.deleteCookies("remember-me", "JSESSION_ID");
		
		http.rememberMe()
		.key("example")
		.tokenRepository(persistentTokenRepository())
		.tokenValiditySeconds(604800);

	}
	
	
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		log.info("configure..........");
		auth.inMemoryAuthentication().withUser("admin").password("{noop}admin").roles("ADMIN");
		auth.inMemoryAuthentication().withUser("member").password("{noop}member").roles("MEMBER");
		
//		String queryUser = "select userid, userpw, enabled from tbl_member where userid = ? ";
//		String queryDetails = "select userid, auth from tbl_member_auth where userid= ? ";
		
		auth.jdbcAuthentication()
		.dataSource(dataSource);
		
//		.passwordEncoder(passwordEncoder())
//		.usersByUsernameQuery(queryUser)
//		.authoritiesByUsernameQuery(queryDetails);
		
		auth.userDetailsService(customUserService())
		.passwordEncoder(passwordEncoder());
	}
	
	@Bean
	public AuthenticationSuccessHandler loginSuccessHandler() {
		return new CustomLoginSuccessHandler();
	}
	
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
	@Bean
	public UserDetailsService customUserService() {
		return new CustomUserDetailsService();
	}
	
	public PersistentTokenRepository persistentTokenRepository() {
		JdbcTokenRepositoryImpl repo = new JdbcTokenRepositoryImpl();
		repo.setDataSource(dataSource);
		return repo;
	}
}

WebConfig.java : getRootConfigClasses()에 SecurityConfig.class 추가

package org.example.config;

import javax.servlet.Filter;
import javax.servlet.ServletRegistration.Dynamic;

import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class WebConfig extends
AbstractAnnotationConfigDispatcherServletInitializer {

	@Override
	protected Class<?>[] getRootConfigClasses() {
		// TODO Auto-generated method stub
		return new Class[] {RootConfig.class, SecurityConfig.class};
	}

	@Override
	protected Class<?>[] getServletConfigClasses() {
		// TODO Auto-generated method stub
		return new Class[] {ServletConfig.class};
	}

	@Override
	protected String[] getServletMappings() {
		// TODO Auto-generated method stub
		return new String[] {"/"};
	}

	@Override
	protected void customizeRegistration(Dynamic registration) {
		// TODO Auto-generated method stub
		registration.setInitParameter("throwExceptionIfNoHandlerFound", "true");
	}
	
	@Override
	protected Filter[] getServletFilters() {
		// TODO Auto-generated method stub
		CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
		characterEncodingFilter.setEncoding("UTF-8");
		characterEncodingFilter.setForceEncoding(true);
		
		return new Filter[] {characterEncodingFilter};
	}
}

3. 어노테이션 설정 (XML이나 JAVA로 시큐리티 설정을 한 후 부가적으로 사용)

체크리스트

  • security-context.xml 추가
  • org.example.security 및 이하 패키지 추가
  • org.example.domain 내에 MemberVO 와 AuthVO 추가
  • web.xml 에서 security-context.xml 설정과 필터 추가
  • MemberMapper 인터페이스와 MemberMapper.xml 추가
  • org.example.controller 패키지의 CommonController 추가

XML 설정의 경우, sevlet-context.xml
특이하게 security-context.xml이 아닌 servlet-context.xml에 설정을 추가한다.
먼저 security 네임스페이스(버전번호는 제거)를 추가한 후, 설정을 추가한다.

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:security="http://www.springframework.org/schema/security"
	xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
		http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 생략 -->
	<security:global-method-security pre-post-annotations="enabled" secured-annotations="enabled"/>
</beans:beans>

Java 설정의 경우, ServletConfig.java

package org.example.config;

@EnableWebMvc
@ComponentScan(basePackages = {"org.example.controller"})
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class ServletConfig implements WebMvcConfigurer {
    // 생략
}

예제

package org.example.controller;

@Controller
@RequestMapping("/sample/*")
@Log4j
public class SecuritySampleController {
    
    // 생략
	
	@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_MEMBER')")
	@GetMapping("/annoMember")
	public void doMember2() {
		
		log.info("logined annotation member");
	}
	
	
	@Secured({"ROLE_ADMIN"})
	@GetMapping("/annoAdmin")
	public void doAdmin2() {
		
		log.info("logined annotation admin");
	}
}