본문 바로가기
Spring/Spring-Security

SpringSecurity DB 없이 테스트 방법

by YoonJong 2022. 10. 20.
728x90

1. 기본인증

 -> 처음 애플리케이션 시작하면 콘솔에 있는 랜덤 비밀번호 사용

 Id : user (고정)

password : 콘솔창 확인


2. yml에 사용자 추가하기

application.yml 에 사용자 추가하기 

spring:
  security:
    user:
      name: user1
      password: 1234
      roles: USER

3. UserDetailsService 서비스 빈을 추가하기

USER , ADMIN 으로 테스트하기 위해서는 yml 에 추가하는 것으로는 불가능

서비스빈을 사용해 여러명을 등록

 

설정추가 ( @Configuration , @Bean 사용 )

@Configuration //설정 정보를 추가해준다
public class SecurityRoleTest {

    @Bean // 스프링 컨테이너에 빈으로 등록
    UserDetailsService users () {
        UserDetails user1 = User.builder()
                .username("user1")
                .password(passwordEncoder().encode("1234"))
                .roles("USER")
                .build();

        UserDetails admin = User.builder()
                .username("admin")
                .password(passwordEncoder().encode("1234"))
                .roles("ADMIN")
                .build();

        return new InMemoryUserDetailsManager(user1, admin); // 인메모리에 저장
    }

    @Bean
    PasswordEncoder passwordEncoder() { // 비밀번호 암호화
        return new BCryptPasswordEncoder();
    }
}

 

TestController 추가

@RestController
public class TestController {

    @Secured({"ROLE_USER", "ROLE_ADMIN"}) //해당 url 접근은 user 와 admin 가능
    @GetMapping("/user")
    public SecurityMessage user() {
        return SecurityMessage.builder()
                .message("user page")
                .auth(SecurityContextHolder.getContext().getAuthentication())
                .build();
    }

    @Secured({"ROLE_ADMIN"}) // 해당 url 접근은 admin만 가능
    @GetMapping("/admin")
    public SecurityMessage admin() {
        return SecurityMessage.builder()
                .message("admin page")
                .auth(SecurityContextHolder.getContext().getAuthentication())
                .build();
    }
}

 

SecurityMessage 클래스 생성

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class SecurityMessage {

    private String message;

    private Authentication auth;
}

4. WebSecurityConfigurerAdapter 를 사용하기

@EnableWebSecurity // SpringSecurityFilterChain 에 자동으로 포함
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable() // csrf 무시
                .formLogin() // 폼로그인 설정
                .loginPage("/login") // 로그인페이지는 "/login"
                .successForwardUrl("/") // 성공하면 / url 로 이동
                .failureUrl("/login?error=true"); // 실패하면 해당 url 로 이동

    }

    
    // 테스트 유저 만들기
    @Bean
    UserDetailsService users () {
        UserDetails user1 = User.builder()
                .username("user1")
                .password(passwordEncoder().encode("1234"))
                .roles("USER")
                .build();

        UserDetails admin = User.builder()
                .username("admin")
                .password(passwordEncoder().encode("1234"))
                .roles("ADMIN")
                .build();

        return new InMemoryUserDetailsManager(user1, admin);
    }

    // 비밀번호 암호화
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

5. 로그인 화면 커스터마이징 하기

 타임리프를 이용해서 만들기


6. Junit으로 테스트하기

@WebMvcTest
public class UserAccessTest {

    @Autowired
    MockMvc mockMvc;

    @Autowired
    ObjectMapper objectMapper;

    @Autowired
    PasswordEncoder passwordEncoder;

    @Test
    @DisplayName("2. user 로 user 페이지에 접근할 수 있다.")
    @WithMockUser(username = "user1", roles = "USER")
    void test_user_access_userPage() throws Exception {
        String resp = mockMvc.perform(get("/user"))
                .andExpect(status().isOk())
                .andReturn().getResponse().getContentAsString();

        System.out.println("resp = " + resp); // resp = {"message":"user page"}

        SecurityMessage message = objectMapper.readValue(resp, SecurityMessage.class);
        System.out.println("message = " + message);
        assertEquals("user page", message.getMessage());

    }

    @Test
    @DisplayName("1. user 로 admin 페이지에 접근할 수 없다.")
    @WithMockUser(username = "user1" , roles = "USER")
    void test_user_access_adminPage() throws Exception {
        mockMvc.perform(get("/admin"))
                .andExpect(status().isForbidden());
    }

    @Test
    @DisplayName("3. admin 으로 user 페이지와 admin 페이지에 접근할 수 있다.")
    @WithMockUser(username = "admin" , roles = "ADMIN")
    void test_admin_access_userPage_adminPage() throws Exception {
        String resp = mockMvc.perform(get("/admin"))
                .andExpect(status().isOk())
                .andReturn().getResponse().getContentAsString();

        SecurityMessage message = objectMapper.readValue(resp, SecurityMessage.class);
        assertEquals("admin page", message.getMessage());

        String resp1 = mockMvc.perform(get("/user"))
                .andExpect(status().isOk())
                .andReturn().getResponse().getContentAsString();

        SecurityMessage message1 = objectMapper.readValue(resp1, SecurityMessage.class);
        assertEquals("user page", message1.getMessage());

    }

    @Test
    @DisplayName("4. 로그인 페이지는 아무나 접근 가능해야 한다.")
    void test_all_access_loginPage() throws Exception {
        mockMvc.perform(get("/login"))
                .andExpect(status().isOk());
    }

    @Test
    @DisplayName("5. 홈페이지는 로그인 하지 않은 계정은 들어올 수 없다.")
    void test_notLogin_notAccess_homePage() throws Exception {
        mockMvc.perform(get("/")).andExpect(status().is3xxRedirection());
        mockMvc.perform(get("/user")).andExpect(status().is3xxRedirection());
        mockMvc.perform(get("/admin")).andExpect(status().is3xxRedirection());

    }
}

 

 

참고

https://www.youtube.com/watch?v=MNEgiFeUy_U&list=PLcaKom3xthg63Qq5qCG7EG7XY3nN05ypd&index=1 

 

728x90

댓글