admin: custom 메뉴 인가 추가

This commit is contained in:
geonhos 2024-05-20 14:15:59 +09:00
parent a153536521
commit 44daa3058b
5 changed files with 87 additions and 7 deletions

View File

@ -4,6 +4,9 @@ import com.bpgroup.poc.admin.security.authentication.CustomAuthenticationEntryPo
import com.bpgroup.poc.admin.security.authentication.CustomAuthenticationFilter; import com.bpgroup.poc.admin.security.authentication.CustomAuthenticationFilter;
import com.bpgroup.poc.admin.security.authentication.CustomAuthenticationProvider; import com.bpgroup.poc.admin.security.authentication.CustomAuthenticationProvider;
import com.bpgroup.poc.admin.security.authentication.service.LoginService; import com.bpgroup.poc.admin.security.authentication.service.LoginService;
import com.bpgroup.poc.admin.security.authorization.CustomAccessDeniedHandler;
import com.bpgroup.poc.admin.security.authorization.CustomAuthorizationManager;
import com.bpgroup.poc.admin.security.authorization.service.AuthorizationService;
import com.bpgroup.poc.admin.security.jwt.JwtTokenConstants; import com.bpgroup.poc.admin.security.jwt.JwtTokenConstants;
import com.bpgroup.poc.admin.security.jwt.JwtTokenValidateFilter; import com.bpgroup.poc.admin.security.jwt.JwtTokenValidateFilter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@ -35,6 +38,9 @@ public class SecurityConfig {
private final LoginService loginService; private final LoginService loginService;
private final AuthorizationService authorizationService;
@Bean @Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
@ -48,12 +54,14 @@ public class SecurityConfig {
.cacheControl(cache -> cache.disable()) //ERR_CACHE_MISS .cacheControl(cache -> cache.disable()) //ERR_CACHE_MISS
); );
// 설정 // 설정
http.authorizeHttpRequests(c -> c http.authorizeHttpRequests(c -> c
.requestMatchers("/css/**", "/images/**", "/js/**", "/font/**", "/favicon.ico").permitAll() .requestMatchers("/css/**", "/images/**", "/js/**", "/font/**", "/favicon.ico").permitAll()
.requestMatchers("/common/modal/**").permitAll() .requestMatchers("/common/modal/**").permitAll()
.requestMatchers(LOGIN_PATH, LOGOUT_PATH, ERROR_PATH).permitAll() .requestMatchers(LOGIN_PATH, LOGOUT_PATH, ERROR_PATH).permitAll()
.anyRequest().authenticated()); .anyRequest()
.access(new CustomAuthorizationManager(authorizationService))
);
http.formLogin(AbstractHttpConfigurer::disable); // Form 로그인이 아닌 Json 로그인으로 분리 http.formLogin(AbstractHttpConfigurer::disable); // Form 로그인이 아닌 Json 로그인으로 분리
http.addFilterBefore(authenticationGenerateFilter(), UsernamePasswordAuthenticationFilter.class); // 로그인 관련 Filter 설정 http.addFilterBefore(authenticationGenerateFilter(), UsernamePasswordAuthenticationFilter.class); // 로그인 관련 Filter 설정
@ -67,6 +75,7 @@ public class SecurityConfig {
http.exceptionHandling(c -> { http.exceptionHandling(c -> {
c.authenticationEntryPoint(new CustomAuthenticationEntryPoint()); // Authentication 실패 처리 c.authenticationEntryPoint(new CustomAuthenticationEntryPoint()); // Authentication 실패 처리
c.accessDeniedHandler(new CustomAccessDeniedHandler()); // Authorization 실패 처리
}); });
http.logout(c -> c http.logout(c -> c

View File

@ -1,5 +0,0 @@
package com.bpgroup.poc.admin.security;
public class SecurityFilterConstants {
public static final String[] EXCLUDE_FILTER_STARTS_WITH_URI = {"/login", "/logout", "/error", "/css", "/js", "/images", "/favicon.ico", "/common/modal", "/font"};
}

View File

@ -0,0 +1,18 @@
package com.bpgroup.poc.admin.security.authorization;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.sendRedirect("/error?error=" + URLEncoder.encode("메뉴에 접근할 수 있는 권한이 없습니다.", StandardCharsets.UTF_8));
}
}

View File

@ -0,0 +1,28 @@
package com.bpgroup.poc.admin.security.authorization;
import com.bpgroup.poc.admin.security.authorization.service.AuthorizationService;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import java.util.function.Supplier;
@RequiredArgsConstructor
public class CustomAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {
private final AuthorizationService authorizationService;
@Override
public AuthorizationDecision check(Supplier authentication, RequestAuthorizationContext context) {
Authentication auth = (Authentication) authentication.get();
String userName = (String) auth.getPrincipal();
String requestUri = context.getRequest().getRequestURI();
boolean hasAuthorization = authorizationService.isAuthorized(userName, requestUri);
return new AuthorizationDecision(hasAuthorization);
}
}

View File

@ -0,0 +1,30 @@
package com.bpgroup.poc.admin.security.authorization.service;
import com.bpgroup.poc.admin.domain.base.admin.entity.Admin;
import com.bpgroup.poc.admin.domain.base.admin.entity.AdminRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
public class AuthorizationService {
private final AdminRepository adminRepository;
public boolean isAuthorized(String username, String requestUri) {
Optional<Admin> findAdmin = adminRepository.findByLoginId(username);
if (findAdmin.isEmpty()) {
return false;
}
Admin admin = findAdmin.get();
return admin.getAdminRole().getRole().getRoleMenus().stream()
.anyMatch(roleMenu -> roleMenu.getMenu().getUri().equals(requestUri));
}
}