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

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

user_menu_authority


  • 이전에 포스팅에서 작성한 UserDetails를 구현한 User엔티티를 AuthenticationProvider에서 어떤 식으로 사용하는지 작성해보려 합니다.
    • AuthenticationProvider에 대한 설명은 생략하겠습니다.
  • 해당 설정으로 기대하는 기능은 로그인한 사용자의 메뉴 권한정보 셋팅과 접근 가능한 메뉴 리스트를 SecurityContextHolder에 포함하 여 이를 이용한 권한체크와 화면단에 메뉴 UI를 그려주는 기능입니다.


CustomAuthenticationProvider

  • 간단한 인증 공급자 코드 입니다. 포함하고 있는 유저 정보가 있으면 이전에 작성한 메소드들을 이용하여 필요한 데이터를 설정해줍니다.
    • 이전 글에 포스팅한 User엔티티를 매핑해주는 mybatis코드를 보면 알겠지만 유저가 접근 할 수 있는 메뉴 리스트를 따로 매핑해주지 않았습니다. 해당 정보는 CustomAuthenticationProvider를 이용하여 서버단에서 구성하여 매퍼 코드를 단순화 하기 위함이었습니다.
      • BackOfficeMenuService backOfficeMenuService에 대한 설명은 해당 다음 포스팅에 하겠습니다.
    • 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
      
      @Slf4j
      @Component
      @RequiredArgsConstructor
      public class CustomAuthenticationProvider implements AuthenticationProvider {
      
          private final PasswordEncoder passwordEncoder;
          private final DefaultUserService defaultUserService;
          private final BackOfficeMenuService backOfficeMenuService;
      
          @Override
          public Authentication authenticate(Authentication authentication) throws AuthenticationException {
      
              String email = authentication.getName();
              String password = authentication.getCredentials().toString();
      
              User userDetails = (User) defaultUserService.loadUserByUsername(email);
      
              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");
              }
      
          }
      
          @Override
          public boolean supports(Class<?> authentication) {
              return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
          }
      
      }
      


  • 위의 설정으로 기대하는 결과는 User엔티티의 모든 필드가 채워지는것 입니다. 이전 포스팅에서는 유저가 접근 할 수 있는 메뉴 리스트가 비어있었습니다. 해당 설정을 인증 공급자를 통해 설정되는것을 기대했기에 해당 필드가 의도한대로 셋팅이 되었는지 확인하기 위한 테스트 코드를 작성하고 그 결과를 JSON형태로 직관해보겠습니다.
    • setSecurityContextHolder를 구성하는 부분은 작성되어있는 CustomAuthenticationProvider코드와 같게 구성하여 화면단에서 유저가 로그인한 이후의 흐름을 모방하기 위해 작성되었습니다. 실제 로그인 앤드포인트를 호출하기가 귀찮아서 작성하게 되었습니다.(어차피 걔도 인증 공급자를 호출할거니까….)
    • 테스트와 관련된 다른 코드 설명은 생략하겠습니다.
    • 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
      
      @Slf4j
      @SpringBootTest
      @ActiveProfiles("local")
      @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
      class PostingTest {
      
          private final String ADMIN_USER_ID = "ad_sub_admin";
          private final String ADMIN_PASSWORD = "1234";
      
          @Autowired
          private ObjectMapper objectMapper;
      
          @Autowired
          private PasswordEncoder passwordEncoder;
      
          @Autowired
          private DefaultUserService defaultUserService;
      
          @Autowired
          private BackOfficeMenuService backOfficeMenuService;
      
          private void setSecurityContextHolder(String userId, String password) {
              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");
              }
          }
      
          @AfterEach
          void afterEach() {
              clearSecurityContextHolder();
          }
      
          protected void clearSecurityContextHolder() {
              SecurityContextHolder.clearContext();
          }
      
          @Test
          @Order(1)
          void securityContextHolderSetUserTest() throws Exception {
      
              setSecurityContextHolder(ADMIN_USER_ID, ADMIN_PASSWORD);
              SecurityContext context = SecurityContextHolder.getContext();
      
              Authentication authentication = context.getAuthentication();
              Assertions.assertNotNull(authentication);
      
              Object principal = authentication.getPrincipal();
              Assertions.assertInstanceOf(User.class, principal);
      
              User user = (User) principal;
              Assertions.assertFalse(user.getUserMenuAuthorityResList().isEmpty());
      
              log.info("user = {}", objectMapper.writeValueAsString(user));
      
          }
      
      }
      


  • 츨력된 결과 입니다. 인증 공급자를 통해 사이트를 구성하는데 필요한 해당 유저에 대한 모든 정보를 설정할 수 있게 된거같습니다. 해당 값을 이용하여 화면을 구성하는건 이제 FRONT의 몫입니다.
    • result
This post is licensed under CC BY 4.0 by the author.