lenve/vhr · warning · AuthenticationServiceException

Authentication method not supported

Error message

Authentication method not supported: {request.getMethod()}

What it means

Thrown by the overridden UsernamePasswordAuthenticationFilter.attemptAuthentication when the HTTP method of the incoming request is not POST. UsernamePasswordAuthenticationFilter is contract-bound to POST form/json credentials; LoginFilter preserves that contract and rejects GET (or any other verb) immediately with an AuthenticationServiceException before parsing the body or verifying the captcha.

Solutions

  1. Change the login request to POST: in the front-end use axios.post('/doLogin', data) or fetch(url, { method: 'POST', body: ... }).
  2. Ensure the form's method attribute is 'post' (method='post') if using a traditional HTML form submit to the login URL.
  3. If a reverse proxy is in front, confirm it preserves the HTTP method and is not rewriting POST to GET (check X-Forwarded-* and proxy config).
  4. Allow CORS preflight by ensuring the OPTIONS method is permitted/short-circuited before reaching this filter (configure CORS filter ordering).
  5. Verify the configured loginProcessingUrl matches the URL the client targets so the POST actually lands here.

Example fix

// before
axios.get('/doLogin', { params: { username, password } });
// after
axios.post('/doLogin', { username, password, code });
Defensive patterns

Strategy: validation

Validate before calling

// Front-end: assert the method before sending.
function login(payload) {
  // UsernamePasswordAuthenticationFilter requires POST
  return axios.post('/doLogin', payload); // never axios.get here
}

Type guard

// Java: a guard inside the filter to give a cleaner message (optional; the throw is correct).
String method = request.getMethod();
if (!"POST".equalsIgnoreCase(method)) {
    response.setStatus(405);
    response.setHeader("Allow", "POST");
    return; // or keep the existing throw
}

Try / catch

// Not typically caught per-request; configure the framework instead.
// Ensure the loginProcessingUrl is only reachable via POST by mapping it on POST only,
// and let AuthenticationServiceException flow to the failure handler:
// http.formLogin().loginProcessingUrl("/doLogin")  // Spring binds POST by default
// Add a 405 fallback for non-POST so clients get a clear signal:
@Override
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp,
                                    AuthenticationException ex) {
    if (ex instanceof AuthenticationServiceException
        && ex.getMessage().startsWith("Authentication method not supported")) {
        resp.setStatus(405);
    }
}

Prevention

When it happens

Trigger: A GET request (e.g., a browser navigation, a link, or a misconfigured fetch) hits the configured login-processing URL (default /doLogin or /login). The first guard in attemptAuthentication compares request.getMethod() to 'POST', and on mismatch throws AuthenticationServiceException with the actual method name interpolated into the message.

Common situations: Front-end developer used axios.get or fetch without method:'POST' for the login call; a browser pre-filled the login URL into the address bar and someone hit Enter; a redirect from an intercepting filter converted the POST to GET; a proxy/load balancer rewrites the method; Spring Security's loginProcessingUrl was triggered by a navigation rather than a form action; CORS preflight (OPTIONS) reached this filter.

Understand the failure class

Related errors


AI-assisted analysis of lenve/vhr@03abbd35af (2026-08-14). Data as JSON: /api/errors/1dcbcacdc305b2f0. Report an issue: GitHub.

Appendix: source

Thrown at vhr/vhrserver/vhr-web/src/main/java/org/javaboy/vhr/config/LoginFilter.java:34

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @作者 江南一点雨
 * @微信公众号 江南一点雨
 * @网站 http://www.javaboy.org
 * @微信 a_java_boy
 * @GitHub https://github.com/lenve
 * @Gitee https://gitee.com/lenve
 */
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
    @Autowired
    SessionRegistry sessionRegistry;
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (!request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }
        String verify_code = (String) request.getSession().getAttribute("verify_code");
        if (request.getContentType().contains(MediaType.APPLICATION_JSON_VALUE) || request.getContentType().contains(MediaType.APPLICATION_JSON_UTF8_VALUE)) {
            Map<String, String> loginData = new HashMap<>();
            try {
                loginData = new ObjectMapper().readValue(request.getInputStream(), Map.class);
            } catch (IOException e) {
            }finally {
                String code = loginData.get("code");
                checkCode(response, code, verify_code);
            }
            String username = loginData.get(getUsernameParameter());
            String password = loginData.get(getPasswordParameter());
            if (username == null) {
                username = "";
            }
            if (password == null) {

View on GitHub (pinned to 03abbd35af)