source

JWT 토큰보안에 의한 스프링부트 유닛 테스트

ittop 2023. 3. 8. 21:50
반응형

JWT 토큰보안에 의한 스프링부트 유닛 테스트

Spring Boot을 사용하여 백엔드를 만들고 있으며 JWT 보안을 추가한 지 얼마 되지 않았습니다.

REST 클라이언트를 사용하여 몇 가지 테스트를 수행했으며 JWT 보안은 정상적으로 작동하지만 모든 장치 테스트에서 403 오류 코드가 반환되었습니다.

를 추가했습니다.@WithMockUser주석을 달았지만 여전히 작동하지 않습니다.

@Test
@WithMockUser
public void shouldRedirectToInstaAuthPage() throws Exception {
    mvc.perform(MockMvcRequestBuilders.get("/instaAuth")).andExpect(status().is3xxRedirection());
}

여기에 없는 다른 설정이 있나요?

보안 설정은 다음과 같습니다.

@Configuration
@EnableWebSecurity
public class ServerSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
      protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests()
            .antMatchers("/").permitAll()
            .antMatchers(HttpMethod.POST, "/login").permitAll()
            .anyRequest().authenticated()
            .and()
            // We filter the api/login requests
            .addFilterBefore(new JWTLoginFilter("/login", authenticationManager()),
                    UsernamePasswordAuthenticationFilter.class)
            // And filter other requests to check the presence of JWT in header
            .addFilterBefore(new JWTAuthenticationFilter(),
                    UsernamePasswordAuthenticationFilter.class);
      }

      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // Create a default account
        auth.inMemoryAuthentication()
            .withUser("john")
            .password("123")
            .roles("ADMIN");
      }
}

방법 보안:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        return new OAuth2MethodSecurityExpressionHandler();
    }

}

문제를 해결했다고 생각합니다(또한 백엔드에 보안 취약점을 만들거나 나쁜 방법을 사용하지 않았으면 합니다).

저는 @punkrocker27ka의 조언에 따라 이 답변을 보았습니다.테스트용으로 Oauth 토큰을 수동으로 생성한다고 되어 있기 때문에 JWT 토큰에도 같은 처리를 하기로 했습니다.

그래서 JWT 토큰을 생성하는 클래스를 업데이트하고 다음과 같이 검증했습니다.

public class TokenAuthenticationService {

    static final long EXPIRATIONTIME = 864_000_000; // 10 days
    static final String SECRET = "ThisIsASecret";
    static final String TOKEN_PREFIX = "Bearer";
    static final String HEADER_STRING = "Authorization";

    public static void addAuthentication(HttpServletResponse res, String username) {

        String jwt = createToken(username);

        res.addHeader(HEADER_STRING, TOKEN_PREFIX + " " + jwt);
    }

    public static Authentication getAuthentication(HttpServletRequest request) {
        String token = request.getHeader(HEADER_STRING);
        if (token != null) {
            // parse the token.
            String user = Jwts.parser()
                    .setSigningKey(SECRET)
                    .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
                    .getBody()
                    .getSubject();

            return user != null ?
                    new UsernamePasswordAuthenticationToken(user, null, Collections.emptyList()) :
                        null;
        }
        return null;
    }

    public static String createToken(String username) {
        String jwt = Jwts.builder()
                .setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATIONTIME))
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .compact();

        return jwt;
    }
}

그리고 나서 새로운 테스트를 만들었습니다.

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class TokenAuthenticationServiceTest {

    @Autowired
    private MockMvc mvc;

    @Test
    public void shouldNotAllowAccessToUnauthenticatedUsers() throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/test")).andExpect(status().isForbidden());
    }

    @Test
    public void shouldGenerateAuthToken() throws Exception {
        String token = TokenAuthenticationService.createToken("john");

        assertNotNull(token);
        mvc.perform(MockMvcRequestBuilders.get("/test").header("Authorization", token)).andExpect(status().isOk());
    }

}

그 후 테스트를 실행했는데 통과했으므로 토큰이 승인되었습니다.@WithMockUser주석입니다.나는 이것을 나의 다른 시험 수업에 추가할 것이다.

PS: 테스트 엔드 포인트는 다음과 같습니다.

/**
 * This controller is used only for testing purposes.
 * Especially to check if the JWT authentication is ok.
 */
@RestController
public class TestController {

    @RequestMapping(path = "/test", method = RequestMethod.GET)
    public String testEndpoint() {
        return "Hello World!";
    }
}

이 Create를 사용하여 테스트하는 경우 주의해야 할 것은Token() 메서드는 존재하지 않는 사용자를 테스트할 수 없습니다.
이는 create가Token()은 입력한 문자열을 기반으로 JWT 토큰만 만듭니다.
존재하지 않는 사용자가 액세스 할 수 없도록 하려면 다음 명령을 생성할 것을 권장합니다.Token() 메서드 private는 다음과 같이 요구를 사용하여 토큰을 가져옵니다.

@Test
public void existentUserCanGetTokenAndAuthentication() throws Exception {
    String username = "existentuser";
    String password = "password";

    String body = "{\"username\":\"" + username + "\", \"password\":\" 
                  + password + "\"}";

    MvcResult result = mvc.perform(MockMvcRequestBuilders.post("/v2/token")
            .content(body))
            .andExpect(status().isOk()).andReturn();

    String response = result.getResponse().getContentAsString();
    response = response.replace("{\"access_token\": \"", "");
    String token = response.replace("\"}", "");

    mvc.perform(MockMvcRequestBuilders.get("/test")
        .header("Authorization", "Bearer " + token))
        .andExpect(status().isOk());
}

마찬가지로 존재하지 않는 사용자가 다음 결과를 얻을 수 없음을 나타낼 수 있습니다.

@Test
public void nonexistentUserCannotGetToken() throws Exception {
    String username = "nonexistentuser";
    String password = "password";

    String body = "{\"username\":\"" + username + "\", \"password\":\" 
                  + password + "\"}";

    mvc.perform(MockMvcRequestBuilders.post("/v2/token")
            .content(body))
            .andExpect(status().isForbidden()).andReturn();
}

언급URL : https://stackoverflow.com/questions/45241566/spring-boot-unit-tests-with-jwt-token-security

반응형