JAVA/SPRING

[spring security] TEST방법.

gracelove91 2019. 10. 11. 17:37

SPRING SECURITY.


준비

먼저 maven을 통해 spring-security-test 모듈을 받아오자.

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>

SecurityConfig.java

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .mvcMatchers("/", "/info", "/account/**").permitAll()
                .mvcMatchers("/admin").hasRole("ADMIN")
                .anyRequest().authenticated()

                .and()
                .formLogin()

                .and()
                .httpBasic();
    }
...
}
  1. 웹루트이하 "/", "/info", "/account/**" 에는 인증안된 사용자포함 모든 사용자가 접근 가능하다.
  2. "/admin" 페이지는 "ADMIN" 권한을 가진 사용자만 접근이 가능하다.
  3. 그 외 페이지는 인증된 사용자만 접근이 가능하다.
  4. formLogin()을 사용할 것이고, httpBasic()을 사용할 것이다.

SecurityTest(1).java

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class SecurityTest {
    @Autowired
    MockMvc mockMvc;

    @Test
    public void index_anonymous() throws Exception {
        mockMvc.perform(get("/").with(anonymous()))
                .andDo(print())
                .andExpect(status().isOk());
    }

    @Test
    public void index_user() throws Exception {
        mockMvc.perform(get("/").with(user("grace").roles("USER")))
                .andDo(print())
                .andExpect(status().isOk());
    }
  • mockMvc.perform(get("/").with(anonymous()))
    • annonymous(), 그러니까 인증이 안된 사용자가 접근할 때의 테스트다.
  • mockMvc.perform(get("/").with(user("grace").roles("USER")))
    • username = "grace", roles = "USER" 를 가진 계정이 '/'에 접근할 때의 테스트다.

SecurityTest(2).java

    @Test
    @WithAnonymousUser
    public void index_anonymous_annotatedAnonymousUser() throws Exception {
        mockMvc.perform(get("/"))
                .andDo(print())
                .andExpect(status().isOk());
    }

    @Test
    @WithMockUser(username = "grace", roles = "ADMIN")
    public void index_user_annotatedMockUser() throws Exception {
        mockMvc.perform(get("/"))
                .andDo(print())
                .andExpect(status().isOk());
    }
  • 위의 SecyrityTest(1).java 와 같다. 'with(...)'를 어노테이션으로 대체한 것.
  • 그런데 @WithMockUser 어노테이션을 보면 알다시피 너무 길다. 따라서 @WithMockUser를 메타에노테이션으로 활용해서 더 짧게 바꿔보자.

WithAdmin.java

@WithMockUser(username = "grace", roles = "ADMIN")
@Retention(RetentionPolicy.RUNTIME)
public @interface WithAdmin {
}

SecurityTest(3).java

    @Test
    @WithAdmin
    public void admin_admin_customAnnotated() throws Exception {
        mockMvc.perform(get("/admin"))
                .andDo(print())
                .andExpect(status().isOk());
    }
  • @WithAdmin 어노테이션을 붙이면 @WithMockUser(username = "grace", roles = "ADMIN") 을 사용하는 거나 마찬가지.

폼로그인에 대한 테스트.

SecurityTest(4).java

@Autowired
    AccountService accountService;

    @Test
    @Transactional
    public void login_success() throws Exception{
        String username = "grace";
        String password = "123";
        Account user = this.createUser(username, password);


//        mockMvc.perform(formLogin().user(user.getUsername()).password(user.getPassword())) //안된다! bycrypt로 암호화된 비밀번호가 들어오기 때문에.
        mockMvc.perform(formLogin().user(user.getUsername()).password(password))
                .andExpect(authenticated());
    }

    @Test
    @Transactional
    public void login_fail() throws Exception{
        String username = "grace";
        String password = "123";
        Account user = this.createUser(username, password);
        mockMvc.perform(formLogin().user(user.getUsername()).password("1234"))
                .andExpect(unauthenticated());
    }

    private Account createUser(String username, String password) {
        Account account = new Account();
        account.setUsername(username);
        account.setPassword(password);
        account.setRole("USER");
        return accountService.createNew(account);
    }
  • @Transactinal 어노테이션을 붙여서 DB에 영향이 없도록 하자.