Spring Security를 통한 세션 방식 로그인 구현

 

SpringBoot 3.1.3 버전을 사용했습니다.

 

 

1. SecurityConfiguration 구현

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

	// 정적 자원에 대한 Security Ignoring 처리
	@Bean
	public WebSecurityCustomizer webSecurityCustomizer() {
		return web -> web.ignoring()
			.requestMatchers(PathRequest.toStaticResources().atCommonLocations())
			.requestMatchers(HttpMethod.POST, "/registers");
	}

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.csrf(AbstractHttpConfigurer::disable)
			.authorizeHttpRequests(auth -> {
				auth.requestMatchers("*").permitAll();
				auth.requestMatchers("auths/**").permitAll();
				auth.requestMatchers("members/**").hasRole("USER");
			});

		http
			.formLogin(form -> form
				.usernameParameter("username")
				.passwordParameter("password")
				.loginPage("/auths/login-form")
				.loginProcessingUrl("/process_login")
				.failureUrl("/auths/login-form?error"));

		http
			.logout(logout -> logout
				.logoutUrl("/logout")
				.logoutSuccessUrl("/"));

		http
			.exceptionHandling(handle -> handle
				.accessDeniedPage("/auths/access-denied"));
		
		http.sessionManagement(session ->
			session
				.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
				.maximumSessions(1)
				.maxSessionsPreventsLogin(false)
				.expiredUrl("/session-expired"));

		return http.build();
	}

	// 만료된 세션 정리
	@Bean
	public static ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
		return new ServletListenerRegistrationBean<>(new HttpSessionEventPublisher());
	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return PasswordEncoderFactories.createDelegatingPasswordEncoder();
	}
}

 

정적 자원과 로그인 템플릿에 대한 권한을 모두에게 허용하고, 커스텀한 로그인 페이지를 통해 로그인을 할 수 있도록 함

 

템플릿 페이지는 Thymeleaf 3.1을 사용하여 구성하였으며, header ・ menu ・ footer fragmentslayouts을 구성한 뒤

레이아웃에 content를 생성하여 구성하도록 함

 

<!-- 메인 페이지 레이아웃 -->
<!DOCTYPE html>
<html lang="ko"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<body>
<head>
    <title>Home</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <!-- CSS Stylesheet -->
</head>
<body>
<div class="container-lg">
    <div th:replace="~{fragments/header::header}"></div>
    <hr>

    <div th:replace="~{fragments/menu::menu}"></div>
    <hr>

    <div layout:fragment="content"></div>
    <hr>

    <div th:replace="~{fragments/footer::footer}"></div>
    <hr>
</div>
<!-- Script -->
</body>

</body>
</html>

 

 

 

CustomAuthorityUtils를 통한 User Role 생성

@Slf4j
@Component
public class CustomAuthorityUtils {
	// 사이드 프로젝트니까 그냥 안 숨기고 admin으로 설정
	private final String ADMIN_USERNAME = "admin";

	private final List<GrantedAuthority> ADMIN_ROLES =
		AuthorityUtils.createAuthorityList("ROLE_ADMIN", "ROLE_OWNER", "ROLE_USER");

	private final List<GrantedAuthority> OWNER_ROLES =
		AuthorityUtils.createAuthorityList("ROLE_OWNER", "ROLE_USER");

	private final List<GrantedAuthority> USER_ROLES =
		AuthorityUtils.createAuthorityList("ROLE_USER");

	private final List<String> ADMIN_ROLES_STRING = List.of("ADMIN", "OWNER", "USER");
	private final List<String> USER_ROLES_STRING = List.of("USER");

	// DB에 저장된 Role 기반으로 권한 정보 생성
	public List<GrantedAuthority> createAuthorities(List<String> roles) {
		List<GrantedAuthority> authorities = roles.stream()
			.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
			.collect(Collectors.toList());

		log.info("Create Authorities : {}", authorities);

		return authorities;
	}

	public List<String> createRoles(String username) {
		if (username.equals(ADMIN_USERNAME)) {
			return ADMIN_ROLES_STRING;
		}

		return USER_ROLES_STRING;
	}
}

 

Role은 ADMIN, OWNER, USER 3가지로 구성하고,

OWNER는 Member Entity에 updateRoles() 메서드를 생성한 뒤 AdminService에서 추가 및 제거하는 로직을 생성할 예정

 

public class Member {
	...
    
	@ElementCollection(fetch = FetchType.EAGER)
	private List<String> roles = new ArrayList<>();
}

 

Roles는 별도의 클래스를 생성하지 않고 @ElementCollection을 사용해서 별도의 테이블을 생성해서 관리

 

 

 

기타 사항

회원가입 폼 및 컨트롤러 생성

Exception 처리를 위한 Exception Controller 및 GlobalExceptionAdvice(Handler) 생성

 

 

 

현재 발생한 문제점

서버를 종료 후 재 실행 시에 로그아웃이 풀리지 않고 그대로 로그인 되어있는 상태로 남아있는 문제가 있음

로그아웃 시에도 쿠키가 삭제되지 않아 쿠키에 세션 ID를 가지고 있는 것을 확인할 수 있음

 

 

 

사용하면서 공부한 기술 정리

Spring Security

 

Thymeleaf

 

 

더 자세한 코드는 여기에서 확인이 가능합니다.

+ Recent posts