- 이전에 포스팅에서 작성한
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
의 몫입니다.