본문 바로가기

Framework/SpringBoot

[Spring Boot] Spring Security 적용하기

1. 프로젝트 생성

dependencies 추가

1) 프로젝트 설정

✔ bulid.gradle

버전 3.1.5 에서 3.1.0으로 변경

// jsp 설정 추가
eclipse.wtp.facet {
    // Change the version of the Dynamic Web Module facet
    facet name: 'jst.web', version: '5.0'
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-security'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.security:spring-security-test'
	// 추가
	implementation 'jakarta.servlet:jakarta.servlet-api'
	implementation 'jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api'
	implementation 'org.glassfish.web:jakarta.servlet.jsp.jstl'
	implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
}

 

 

✔application.properties

server.port=8081
# JSP 설정
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

 

2) jsp 파일 생성을 위한 폴더 구성

src / main / webapp / WEB-INF / views 폴더 차례로 생성

 

2. 코드작성

1) Controller

- [com.study.springboot] 패키지에 MyController 생성

@Controller
public class MyController {
	@RequestMapping("/")
	public @ResponseBody String root() {
		return "security";
	}
	
	@RequestMapping("/guest/welcome")
	public String welcome1() {
		return "guest/welcome1";
	}
	@RequestMapping("/member/welcome")
	public String welcome2() {
		return "member/welcome2";
	}
	@RequestMapping("/admin/welcome")
	public String welcome3() {
		return "admin/welcome3";
	}
}

 

- 위 컨트롤러에 맞게 jsp파일 생성

각 폴더에 welcome1.jsp,  welcome2.jsp,  welcome3.jsp 생성

 

2) SecurityConfig

-  [com.study.springboot.auth] 패키지 생성하여 WebSecurityConfig 생성

: SecurityFilterChain과 UserDetailsService를 빈으로 등록하기

package com.study.springboot.auth;

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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

import jakarta.servlet.DispatcherType;

@Configuration
public class WebSecurityConfig {
	// 두가지 빈을 등록해서 사용
	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http.csrf((csrf) -> csrf.disable())
			.cors((cors) -> cors.disable())
			.authorizeHttpRequests(request -> request
					.dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
					.requestMatchers("/").permitAll()
					.requestMatchers("/css/**", "/js/**", "/img/**").permitAll()
					.requestMatchers("/guest/**").permitAll()
					.requestMatchers("/member/**").hasAnyRole("USER", "ADMIN")
					.requestMatchers("/admin/**").hasRole("ADMIN")
					.anyRequest().authenticated() // 어떠한 요청이라도 인증 필요
					);
		http.formLogin().permitAll(); // 기본 로그인 페이지
		http.logout().permitAll(); // 로그아웃 기본설정
		
		return http.build();
	}
	@Bean
	public UserDetailsService userService() {
		UserDetails user = User.builder()
				.username("user")
				.password(passwordEncoder().encode("1234"))
				.roles("USER") // ROLE_USER 에서 ROLE_은 자동으로 붙음
				.build();
		
		UserDetails admin = User.builder()
				.username("admin")
				.password(passwordEncoder().encode("1234"))
				.roles("USER","ADMIN")
				.build();
		
		return new InMemoryUserDetailsManager(user, admin);
	}

	private PasswordEncoder passwordEncoder() {
		return PasswordEncoderFactories.createDelegatingPasswordEncoder();
	}
}

 

3. 💻 구동 확인

메인화면과 기본 로그인 페이지

 

1) 비로그인 (guest) 상태

 

2) user로 로그인(member) 

 

3) admin으로 로그인

4. 커스텀 화면 적용하기

Security의 기본 로그인 폼을 사용하지않고 직접 jsp파일을 커스텀하여 적용해보자.

1) jsp 파일 추가

페이지 변경되는 것을 확인하기 위해 별다른 스타일 적용 없이 간단하게만 작성했다.

view - security 폴더에 loginError.jsp, loginForm.jsp 생성

 

2) Controller 

loginError.jsp, loginForm.jsp에 대한 각각의 매핑 추가하기

	@RequestMapping("/loginForm")
	public String loginForm() {
		return "security/loginForm";
	}
	@RequestMapping("/loginError")
	public String loginError() {
		return "security/loginError";
	}

 

3) WebSecurityConfig 수정

filterChain() 내부의 http.formLogin, http.logout을 기본설정에서 커스텀한 페이지로 변경하여 적용한다.

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http.csrf((csrf) -> csrf.disable())
			.cors((cors) -> cors.disable())
			.authorizeHttpRequests(request -> request
					.dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
					.requestMatchers("/").permitAll()
					.requestMatchers("/css/**", "/js/**", "/img/**").permitAll()
					.requestMatchers("/guest/**").permitAll()
					.requestMatchers("/member/**").hasAnyRole("USER", "ADMIN")
					.requestMatchers("/admin/**").hasRole("ADMIN")
					.anyRequest().authenticated() // 어떠한 요청이라도 인증 필요
					);
//		http.formLogin().permitAll(); // 기본 로그인 페이지
//		http.logout().permitAll(); // 로그아웃 기본설정
		
		http.formLogin((formLogin) -> formLogin
				.loginPage("/loginForm")
				.loginProcessingUrl("/j_spring_security_check")
				.failureUrl("/loginError")
				.usernameParameter("j_username")
				.passwordParameter("j_password")
				.permitAll()
				);
		http.logout((logout) -> logout
				.logoutUrl("/logout")
				.logoutSuccessUrl("/")
				.permitAll()
				);
		
		return http.build();
	}

 

💻 구동 확인

변경된 로그인 화면과 로그인 실패시 에러페이지로 이동하는 것을 확인할 수 있다.

member와 admin 페이지의 정상/비정상 접근 처리는 동일하게 적용된다.