[Spring/Thymeleaf] 세션 방식 로그인 및 템플릿 구현
2023. 8. 29. 15:45
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 fragments와 layouts을 구성한 뒤
레이아웃에 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' 카테고리의 다른 글
[Thymeleaf] th:href 경로에 변수를 사용한 경로 설정 (0) | 2023.09.07 |
---|---|
[Thymeleaf] layout fragment로 변수 넘겨주기 (0) | 2023.09.07 |
[Spring/Error] WebSecurityCustomizer를 통해 정적 자원에 대한 Ignore가 안될때 (0) | 2023.08.28 |
[Spring] 다중 칼럼 인덱스를 활용한 쿼리 속도 개선 (0) | 2023.05.14 |
[Spring] SimpleJdbcInsert 사용 시 테이블이나 칼럼을 제대로 불러오지 못할 때 해결 방법 (0) | 2023.04.08 |