session 만료 방식 변경, @Guest annotation 추가
This commit is contained in:
parent
b255a24a2c
commit
6d5649535f
@ -0,0 +1,12 @@
|
||||
package io.company.localhost.common.annotation;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Guest {
|
||||
}
|
||||
@ -1,36 +1,36 @@
|
||||
package io.company.localhost.common.config;
|
||||
|
||||
import io.company.localhost.common.filter.WebCorsFilter;
|
||||
import io.company.localhost.common.security.dsl.RestApiDsl;
|
||||
import io.company.localhost.common.security.handler.MemberAuthFailureHandler;
|
||||
import io.company.localhost.common.security.handler.MemberAuthSuccessHandler;
|
||||
import io.company.localhost.common.security.handler.RestAccessDeniedHandler;
|
||||
import io.company.localhost.common.security.handler.RestAuthenticationEntryPointHandler;
|
||||
import io.company.localhost.common.security.service.CustomRememberMeServices;
|
||||
import io.company.localhost.common.security.service.MemberPrincipalDetailService;
|
||||
import io.company.localhost.common.security.session.AuthenticationSessionControlStrategy;
|
||||
import io.company.localhost.common.security.session.CustomSessionInformationExpiredStrategy;
|
||||
import io.company.localhost.common.security.session.CustomSessionRegistryImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.session.SessionRegistry;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
||||
|
||||
import org.springframework.security.web.authentication.RememberMeServices;
|
||||
import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;
|
||||
import org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;
|
||||
|
||||
import io.company.localhost.common.security.dsl.RestApiDsl;
|
||||
import io.company.localhost.common.security.handler.RestAuthenticationEntryPointHandler;
|
||||
import io.company.localhost.common.filter.WebCorsFilter;
|
||||
import io.company.localhost.common.security.handler.MemberAuthFailureHandler;
|
||||
import io.company.localhost.common.security.handler.MemberAuthSuccessHandler;
|
||||
import io.company.localhost.common.security.handler.RestAccessDeniedHandler;
|
||||
import io.company.localhost.common.security.service.CustomRememberMeServices;
|
||||
import io.company.localhost.common.security.service.MemberPrincipalDetailService;
|
||||
import io.company.localhost.common.security.session.AuthenticationSessionControlStrategy;
|
||||
import io.company.localhost.common.security.session.CustomSessionRegistryImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableMethodSecurity
|
||||
@RequiredArgsConstructor
|
||||
public class SecurityConfig {
|
||||
|
||||
@ -74,7 +74,7 @@ public class SecurityConfig {
|
||||
.sessionAuthenticationStrategy(sessionControlStrategy()) // 세션 제어 전략 설정
|
||||
.maximumSessions(MAX_SESSION) // 최대 세션 수 설정
|
||||
.maxSessionsPreventsLogin(MAX_SESSION_PRENT) // 최대 세션 수 초과 시 로그인 방지 여부
|
||||
.expiredSessionStrategy(new SimpleRedirectSessionInformationExpiredStrategy(INVALID_URL)) // 세션 만료 시 리디렉션
|
||||
.expiredSessionStrategy(new CustomSessionInformationExpiredStrategy()) // 세션 만료 시 401
|
||||
.sessionRegistry(sessionRegistry()) // 세션 레지스트리 설정
|
||||
)
|
||||
.csrf(csrf -> csrf.ignoringRequestMatchers("/api/**")) // CSRF 비활성화
|
||||
|
||||
@ -21,4 +21,8 @@ public enum UserErrorCode implements ErrorCode {
|
||||
public ApiResponse<?> getApiResponse() {
|
||||
return ApiResponse.error(this.getHttpStatus() , this.getMessage());
|
||||
}
|
||||
|
||||
public ApiResponse<?> getApiResponse(String message) {
|
||||
return ApiResponse.error(this.getHttpStatus() , message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
package io.company.localhost.common.security.manager;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.checkerframework.common.reflection.qual.Invoke;
|
||||
import io.company.localhost.common.annotation.Guest;
|
||||
import io.company.localhost.common.security.mapper.MapBasedUrlRoleMapper;
|
||||
import io.company.localhost.common.security.service.DynamicAuthorizationService;
|
||||
import io.company.localhost.vo.MemberVo;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.authorization.AuthorityAuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
@ -16,12 +18,14 @@ import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerExecutionChain;
|
||||
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
|
||||
import io.company.localhost.common.security.mapper.MapBasedUrlRoleMapper;
|
||||
import io.company.localhost.common.security.service.DynamicAuthorizationService;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@ -29,6 +33,7 @@ public class CustomDynamicAuthorizationManager implements AuthorizationManager<R
|
||||
List<RequestMatcherEntry<AuthorizationManager<RequestAuthorizationContext>>> mappings;
|
||||
private static final AuthorizationDecision DENY = new AuthorizationDecision(false);
|
||||
private final HandlerMappingIntrospector handlerMappingIntrospector;
|
||||
private final RequestMappingHandlerMapping requestMappingHandlerMapping;
|
||||
|
||||
// 클래스 초기화 후 동적으로 URL-권한 매핑을 설정
|
||||
@PostConstruct
|
||||
@ -45,6 +50,17 @@ public class CustomDynamicAuthorizationManager implements AuthorizationManager<R
|
||||
// 요청에 대해 권한을 확인하는 메소드
|
||||
@Override
|
||||
public AuthorizationResult authorize(Supplier<Authentication> authentication, RequestAuthorizationContext object) {
|
||||
HttpServletRequest request = object.getRequest();
|
||||
|
||||
Authentication auth = authentication.get();
|
||||
|
||||
// @Guest 메서드인지 확인
|
||||
if (isGuestRequest(request) && !(auth.getPrincipal() instanceof MemberVo)) {
|
||||
boolean allowGuest = !auth.isAuthenticated() || "anonymousUser".equals(auth.getPrincipal());
|
||||
return new AuthorizationDecision(allowGuest);
|
||||
}
|
||||
|
||||
|
||||
for (RequestMatcherEntry<AuthorizationManager<RequestAuthorizationContext>> mapping : this.mappings) {
|
||||
|
||||
RequestMatcher matcher = mapping.getRequestMatcher();
|
||||
@ -68,6 +84,24 @@ public class CustomDynamicAuthorizationManager implements AuthorizationManager<R
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// @Guest가 적용된 요청인지 확인
|
||||
private boolean isGuestRequest(HttpServletRequest request) {
|
||||
try {
|
||||
HandlerExecutionChain handlerExecutionChain = requestMappingHandlerMapping.getHandler(request);
|
||||
|
||||
if (handlerExecutionChain != null) {
|
||||
Object handler = handlerExecutionChain.getHandler();
|
||||
if (handler instanceof HandlerMethod handlerMethod) {
|
||||
return handlerMethod.getMethod().isAnnotationPresent(Guest.class); }
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void verify(Supplier<Authentication> authentication, RequestAuthorizationContext object) {
|
||||
AuthorizationManager.super.verify(authentication, object);
|
||||
@ -76,7 +110,6 @@ public class CustomDynamicAuthorizationManager implements AuthorizationManager<R
|
||||
|
||||
//deprecated 됬는디 값 안 넣으면 세팅이 안 됨
|
||||
@Deprecated
|
||||
@Invoke
|
||||
@Override
|
||||
public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext object) {
|
||||
return (AuthorizationDecision)authorize(authentication,object);
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
package io.company.localhost.common.security.session;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.company.localhost.common.exception.code.UserErrorCode;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.web.session.SessionInformationExpiredEvent;
|
||||
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class CustomSessionInformationExpiredStrategy implements SessionInformationExpiredStrategy {
|
||||
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
@Override
|
||||
public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {
|
||||
// 세션 만료 시 401 상태 코드 설정
|
||||
jakarta.servlet.http.HttpServletResponse response = event.getResponse();
|
||||
|
||||
// HTTP 상태 코드를 401 (Unauthorized)로 설정
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
// 응답의 콘텐츠 타입을 JSON으로 설정
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
|
||||
String message = "세션이 만료되었습니다.";
|
||||
|
||||
// 사용자 정의 에러 코드와 메시지를 JSON 형식으로 응답
|
||||
response.getWriter().write(mapper.writeValueAsString(UserErrorCode.NOT_AUTH_USER.getApiResponse(message)));
|
||||
}
|
||||
}
|
||||
@ -1,15 +1,14 @@
|
||||
package io.company.localhost.controller.common;
|
||||
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import io.company.localhost.common.annotation.Guest;
|
||||
import io.company.localhost.common.annotation.ParameterCheck;
|
||||
import io.company.localhost.common.annotation.ReqMap;
|
||||
import io.company.localhost.common.dto.MapDto;
|
||||
import io.company.localhost.common.response.ApiResponse;
|
||||
|
||||
import io.company.localhost.service.TestService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@Slf4j
|
||||
@RequestMapping("/api/test/")
|
||||
@ -30,11 +29,11 @@ public class TestController {
|
||||
|
||||
//MyBatis TEST
|
||||
@GetMapping("getCong")
|
||||
public ApiResponse getCong() {
|
||||
public ApiResponse<?> getCong() {
|
||||
return ApiResponse.ok(testService.getCong());
|
||||
}
|
||||
|
||||
|
||||
@Guest
|
||||
@ParameterCheck
|
||||
@GetMapping("reqTest1")
|
||||
public ApiResponse<?> reqTest1(@ReqMap MapDto map) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user