Home 관리자 권한, 메뉴 관리 (5)
Post
Cancel

관리자 권한, 메뉴 관리 (5)

user_menu_authority


  • 이전 포스팅글들에서 구성한 정보를 가지고 어떤 식으로 인가 처리를 하는지에 대해 작성해보려 합니다.
    • 실제 코드에서는 @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());
      
          }
      
      }
      
      • result
This post is licensed under CC BY 4.0 by the author.