You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Security:无需表单登录,仅用REST端点认证求助

我明白你的需求——你已经有一个自定义的REST认证端点/checkSubscriberLogin,功能正常,但不想依赖Spring Security自带的http-basic、form-login或者stateless会话配置,还遇到了No AuthenticationEntryPoint could be established的错误。下面是具体的解决思路和代码示例:

解决方案思路

首先,那个错误出现的核心原因是:Spring Security在没有配置默认认证入口(比如http-basic/form-login)时,找不到处理未认证请求的兜底逻辑。所以我们需要**自定义一个AuthenticationEntryPoint**来替代默认实现,同时在Security配置中完全禁用默认认证方式,只保留你的自定义端点逻辑。

1. 自定义AuthenticationEntryPoint

创建一个类实现AuthenticationEntryPoint接口,返回你想要的未认证响应(比如JSON格式提示,而非默认的401页面):

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.ObjectMapper;

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

@Component
public class CustomAuthEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
        response.setContentType("application/json");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        
        Map<String, String> responseBody = new HashMap<>();
        responseBody.put("message", "Authentication is required to access this resource");
        
        // 写入JSON响应
        new ObjectMapper().writeValue(response.getWriter(), responseBody);
    }
}

2. 配置Spring Security

在你的Security配置类中,关闭所有默认认证方式,注册自定义的AuthenticationEntryPoint,并确保你的/checkSubscriberLogin端点允许匿名访问(因为用户需要先调用这个端点完成认证):

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final CustomAuthEntryPoint customAuthEntryPoint;

    public SecurityConfig(CustomAuthEntryPoint customAuthEntryPoint) {
        this.customAuthEntryPoint = customAuthEntryPoint;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            // 禁用默认的http-basic和form-login
            .httpBasic(httpBasic -> httpBasic.disable())
            .formLogin(formLogin -> formLogin.disable())
            // 绑定自定义的认证入口处理类
            .exceptionHandling(exceptionHandling -> 
                exceptionHandling.authenticationEntryPoint(customAuthEntryPoint)
            )
            // 配置端点权限:允许匿名访问认证端点,其他端点需认证
            .authorizeHttpRequests(authorize -> 
                authorize.requestMatchers("/checkSubscriberLogin").permitAll()
                        .anyRequest().authenticated()
            );
        
        return http.build();
    }

    // 暴露AuthenticationManager供你的自定义端点使用
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
        return authConfig.getAuthenticationManager();
    }
}

3. 保留你的核心认证逻辑

你已经实现的/checkSubscriberLogin端点可以完全保留,它依赖AuthenticationManager完成的用户名密码验证逻辑不需要改动,示例如下:

import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AuthController {

    private final AuthenticationManager authenticationManager;

    public AuthController(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @PostMapping("/checkSubscriberLogin")
    public ResponseEntity<?> validateLogin(@RequestBody LoginRequest loginRequest) {
        try {
            // 调用AuthenticationManager完成认证
            Authentication authResult = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword())
            );
            // 认证成功返回自定义消息
            return ResponseEntity.ok("{\"message\":\"authenticated\"}");
        } catch (AuthenticationException e) {
            // 认证失败返回用户不存在提示
            return ResponseEntity.status(401).body("{\"message\":\"user not found\"}");
        }
    }

    // 登录请求的参数封装类
    public static class LoginRequest {
        private String username;
        private String password;

        // Getter和Setter
        public String getUsername() { return username; }
        public void setUsername(String username) { this.username = username; }
        public String getPassword() { return password; }
        public void setPassword(String password) { this.password = password; }
    }
}

关键说明

  • 自定义AuthenticationEntryPoint彻底解决了No AuthenticationEntryPoint could be established的错误,因为Spring Security现在明确知道如何处理未认证的请求。
  • 我们完全禁用了默认的认证方式,只保留你自定义的认证端点作为唯一的入口,符合你的需求。
  • 原有的认证核心逻辑(通过AuthenticationManager验证用户名密码)没有任何改动,保证功能的连续性。

内容的提问来源于stack exchange,提问作者venkatReddi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:45:02