Prevenção de Submissão Duplicada com Spring MVC
Em aplicações web, submissões repetidas de formulários podem causar prbolemas como inserções de dados múltiplas. Uma abordagem eficaz envolve o uso de tokens únicos associados à sessão do usuário, gerenicados por um enterceptor no Spring MVC para validar requisições e bloquear duplicatas.
Princípio Funcional
Um token aleatório, gerado por UUID, é armazenado na sessão do servidor. Ao processar uma requisição, o interceptor verifica se o token fornecido coincide com o da sessão. Se já foi utilizado, a requisição é rejeitada, evitando ação repetida.
Implementação via Código
1. Anotação Customizada
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DuplicatePrevention {
boolean generateToken() default false;
boolean checkToken() default false;
}
2. Interceptor de Validação
public class RequestValidationInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
DuplicatePrevention annotation = method.getAnnotation(DuplicatePrevention.class);
if (annotation != null) {
if (annotation.generateToken()) {
String uniqueToken = UUID.randomUUID().toString();
request.getSession().setAttribute("requestToken", uniqueToken);
}
if (annotation.checkToken()) {
if (hasDuplicateRequest(request)) {
return false; // Interrompe a execução
}
request.getSession().removeAttribute("requestToken");
}
}
}
return true;
}
private boolean hasDuplicateRequest(HttpServletRequest request) {
String serverSideToken = (String) request.getSession().getAttribute("requestToken");
if (serverSideToken == null) {
return true;
}
String clientToken = request.getParameter("requestToken");
if (clientToken == null || !serverSideToken.equals(clientToken)) {
return true;
}
return false;
}
}
3. Configuração no Spring MVC
<!-- Definição de interceptors -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.project.RequestValidationInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
4. Aplicação em Controllers e Views
Para controladores que inicializam o token, utilize @DuplicatePrevention(generateToken=true). Naqueles que validam a submissão, adicione @DuplicatePrevention(checkToken=true).
No formulário HTML, insira um campo oculto para transmitir o token:
<input type="hidden" name="requestToken" value="${requestToken}" />