- 이전 포스팅글들에서 구성한 정보를 가지고 어떤 식으로 인가 처리를 하는지에 대해 작성해보려 합니다.
- 실제 코드에서는
@PreAuthorize
,@PostAuthorize
를 이용하여authority
에 대한 사용자 제한을 구현했습니다.
- 실제 코드에서는
테스트 컨트롤러
- 테스트를 위한 컨트롤러입니다. 코드를 보면 알겠지만
[메뉴명]_[CRUD]
와 같은 식으로 접근 권한을 주었습니다.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
@Slf4j @RestController @RequiredArgsConstructor @RequestMapping(path = "/api/menu-access-test") public class MenuAccessTestController { @GetMapping(path = "/adspm-create") @PreAuthorize("hasAnyAuthority('ST_ADSPM_CREATE')") public ResponseEntity<String> stAdspmCreate() { return new ResponseEntity<>("success", HttpStatus.OK); } @GetMapping(path = "/adspm-read") @PreAuthorize("hasAnyAuthority('ST_ADSPM_READ')") public ResponseEntity<String> stAdspmRead() { return new ResponseEntity<>("success", HttpStatus.OK); } @GetMapping(path = "/adspm-update") @PreAuthorize("hasAnyAuthority('ST_ADSPM_UPDATE')") public ResponseEntity<String> stAdspmUpdate() { return new ResponseEntity<>("success", HttpStatus.OK); } @GetMapping(path = "/adspm-delete") @PreAuthorize("hasAnyAuthority('ST_ADSPM_DELETE')") public ResponseEntity<String> stAdspmDelete() { return new ResponseEntity<>("success", HttpStatus.OK); } @GetMapping(path = "/aspm-create") @PreAuthorize("hasAnyAuthority('ST_ASPM_CREATE')") public ResponseEntity<String> stAspmCreate() { return new ResponseEntity<>("success", HttpStatus.OK); } @GetMapping(path = "/aspm-read") @PreAuthorize("hasAnyAuthority('ST_ASPM_READ')") public ResponseEntity<String> stAspmRead() { return new ResponseEntity<>("success", HttpStatus.OK); } @GetMapping(path = "/aspm-update") @PreAuthorize("hasAnyAuthority('ST_ASPM_UPDATE')") public ResponseEntity<String> stAspmUpdate() { return new ResponseEntity<>("success", HttpStatus.OK); } @GetMapping(path = "/aspm-delete") @PreAuthorize("hasAnyAuthority('ST_ASPM_DELETE')") public ResponseEntity<String> stAspmDelete() { return new ResponseEntity<>("success", HttpStatus.OK); } }
flyway
를 이용해 초기에 설정한 메뉴 권한 정보 입니다.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
INSERT INTO user_type_menu_authority(common_code_id, menu_authority_id, create_id) VALUES ('UT_ADA', 'ST_ADSPM_CREATE', 'site_admin'); INSERT INTO user_type_menu_authority(common_code_id, menu_authority_id, create_id) VALUES ('UT_ADA', 'ST_ADSPM_READ', 'site_admin'); INSERT INTO user_type_menu_authority(common_code_id, menu_authority_id, create_id) VALUES ('UT_ADA', 'ST_ADSPM_UPDATE', 'site_admin'); INSERT INTO user_type_menu_authority(common_code_id, menu_authority_id, create_id) VALUES ('UT_ADA', 'ST_ADSPM_DELETE', 'site_admin'); INSERT INTO user_type_menu_authority(common_code_id, menu_authority_id, create_id) VALUES ('UT_ADA', 'ST_ASPM_CREATE', 'site_admin'); INSERT INTO user_type_menu_authority(common_code_id, menu_authority_id, create_id) VALUES ('UT_ADA', 'ST_ASPM_READ', 'site_admin'); INSERT INTO user_type_menu_authority(common_code_id, menu_authority_id, create_id) VALUES ('UT_ADA', 'ST_ASPM_UPDATE', 'site_admin'); INSERT INTO user_type_menu_authority(common_code_id, menu_authority_id, create_id) VALUES ('UT_ADA', 'ST_ASPM_DELETE', 'site_admin'); INSERT INTO user_type_menu_authority(common_code_id, menu_authority_id, create_id) VALUES ('UT_ADSA', 'ST_ADSPM_READ', 'site_admin'); INSERT INTO user_type_menu_authority(common_code_id, menu_authority_id, create_id) VALUES ('UT_ADSA', 'ST_ASPM_READ', 'site_admin'); INSERT INTO user_type_menu_authority(common_code_id, menu_authority_id, create_id) VALUES ('UT_AA', 'ST_ASPM_CREATE', 'site_admin'); INSERT INTO user_type_menu_authority(common_code_id, menu_authority_id, create_id) VALUES ('UT_AA', 'ST_ASPM_READ', 'site_admin'); INSERT INTO user_type_menu_authority(common_code_id, menu_authority_id, create_id) VALUES ('UT_AA', 'ST_ASPM_UPDATE', 'site_admin'); INSERT INTO user_type_menu_authority(common_code_id, menu_authority_id, create_id) VALUES ('UT_AA', 'ST_ASPM_DELETE', 'site_admin'); INSERT INTO "user"(user_id, password, user_type) VALUES ('admin', '1234', 'UT_ADA'); INSERT INTO "user"(user_id, password, user_type) VALUES ('sub_admin', '1234', 'UT_ADSA'); INSERT INTO "user"(user_id, password, user_type) VALUES ('affiliate_admin', '1234', 'UT_AA');
- 테스트 코드 설정 클래스 입니다. 설명은 생략하겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
@SpringBootTest @AutoConfigureMockMvc @ActiveProfiles("local") @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public abstract class TestControllerTestBasic { @Autowired protected MockMvc mockMvc; @Autowired protected UserMapper userMapper; @Autowired protected ObjectMapper objectMapper; @MockBean protected BackOfficeMenuMapper backOfficeMenuMapper; protected final String MAIN_LOG_IN_PAGE = "/auth/loginPage"; protected final String BACK_OFFICE_MENU_MODEL_KEY = "_backOfficeMenuRes"; protected final String ADMIN_USER_ID = "admin"; protected final String ADMIN_PASSWORD = "1234"; protected final String SUB_ADMIN_USER_ID = "sub_admin"; protected final String SUB_ADMIN_PASSWORD = "1234"; protected final String AFFILIATE_ADMIN_USER_ID = "affiliate_admin"; protected final String AFFILIATE_ADMIN_PASSWORD = "1234"; @Autowired protected AuthenticationManagerBuilder authenticationManagerBuilder; @Autowired private PasswordEncoder passwordEncoder; @Autowired private DefaultUserService defaultUserService; @Autowired private BackOfficeMenuService backOfficeMenuService; @Autowired private CacheManager cacheManager; protected void setSecurityContextHolder(String userId, String password) throws Exception { Authentication authentication = authenticationHelper(userId, password); SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); securityContext.setAuthentication(authentication); SecurityContextHolder.setContext(securityContext); } private Authentication authenticationHelper(String userId, String password) { User userDetails = (User) defaultUserService.loadUserByUsername(userId); if (passwordEncoder.matches(password, userDetails.getPassword())) { List<BackOfficeMenuRes> backOfficeMenuRes = backOfficeMenuService.backOfficeMenuToBackOfficeMenuRes(userDetails.getUserId()); userDetails.loginUserMenuList(backOfficeMenuRes); return new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities()); } else { throw new BadCredentialsException("Bad credentials"); } } protected void clearSecurityContextHolder() { SecurityContextHolder.clearContext(); } @BeforeEach void beforeEach(WebApplicationContext wac) { mockMvc = MockMvcBuilders .webAppContextSetup(wac) .apply(springSecurity()).build(); } @AfterEach void afterEach() { // security context holder 초기화 clearSecurityContextHolder(); // ehcache 초기화 Collection<String> cacheNames = cacheManager.getCacheNames(); for (String cacheName : cacheNames) { Cache cache = cacheManager.getCache(cacheName); Objects.requireNonNull(cache).clear(); } } }
- 실제 테스트 코드를 실행하는 클래스 입니다. 간단하게 메소드에 선언된 권한,
setSecurityContextHolder()
로 설정된 유저의 권한에 맞게 요청에 대한 응답 상태값을 검증하였습니다. 관리자별로 설정한 권한대로 전체 테스트가 잘 통과하는것을 볼 수 있습니다.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
class TestControllerTest extends TestControllerTestBasic { @Test @Order(1) void adminAuthorityTest() throws Exception { setSecurityContextHolder(ADMIN_USER_ID, SUB_ADMIN_PASSWORD); mockMvc.perform(get("/api/menu-access-test/adspm-create")) .andExpect(status().isOk()); mockMvc.perform(get("/api/menu-access-test/adspm-read")) .andExpect(status().isOk()); mockMvc.perform(get("/api/menu-access-test/adspm-update")) .andExpect(status().isOk()); mockMvc.perform(get("/api/menu-access-test/adspm-delete")) .andExpect(status().isOk()); mockMvc.perform(get("/api/menu-access-test/aspm-create")) .andExpect(status().isOk()); mockMvc.perform(get("/api/menu-access-test/aspm-read")) .andExpect(status().isOk()); mockMvc.perform(get("/api/menu-access-test/aspm-update")) .andExpect(status().isOk()); mockMvc.perform(get("/api/menu-access-test/aspm-delete")) .andExpect(status().isOk()); } @Test @Order(2) void subAdminAuthorityTest() throws Exception { setSecurityContextHolder(SUB_ADMIN_USER_ID, SUB_ADMIN_PASSWORD); mockMvc.perform(get("/api/menu-access-test/adspm-create")) .andExpect(status().isForbidden()); mockMvc.perform(get("/api/menu-access-test/adspm-read")) .andExpect(status().isOk()); mockMvc.perform(get("/api/menu-access-test/adspm-update")) .andExpect(status().isForbidden()); mockMvc.perform(get("/api/menu-access-test/adspm-delete")) .andExpect(status().isForbidden()); mockMvc.perform(get("/api/menu-access-test/aspm-create")) .andExpect(status().isForbidden()); mockMvc.perform(get("/api/menu-access-test/aspm-read")) .andExpect(status().isOk()); mockMvc.perform(get("/api/menu-access-test/aspm-update")) .andExpect(status().isForbidden()); mockMvc.perform(get("/api/menu-access-test/aspm-delete")) .andExpect(status().isForbidden()); } @Test @Order(3) void affiliateAdminAuthorityTest() throws Exception { setSecurityContextHolder(AFFILIATE_ADMIN_USER_ID, AFFILIATE_ADMIN_PASSWORD); mockMvc.perform(get("/api/menu-access-test/adspm-create")) .andExpect(status().isForbidden()); mockMvc.perform(get("/api/menu-access-test/adspm-read")) .andExpect(status().isForbidden()); mockMvc.perform(get("/api/menu-access-test/adspm-update")) .andExpect(status().isForbidden()); mockMvc.perform(get("/api/menu-access-test/adspm-delete")) .andExpect(status().isForbidden()); mockMvc.perform(get("/api/menu-access-test/aspm-create")) .andExpect(status().isOk()); mockMvc.perform(get("/api/menu-access-test/aspm-read")) .andExpect(status().isOk()); mockMvc.perform(get("/api/menu-access-test/aspm-update")) .andExpect(status().isOk()); mockMvc.perform(get("/api/menu-access-test/aspm-delete")) .andExpect(status().isOk()); } }