当前位置: 首页 > 知识库问答 >
问题:

如何在Spring security 3.1中处理不同的身份验证异常?

井昊乾
2023-03-14

首先,我想说明一下,我已经检查了Stack Overflow中的其他问题,并根据答案实现了自己的方法:https://stackOverflow.com/A/14425801/2487263和https://stackOverflow.com/A/16101649/2487263

我正在尝试使用Spring security 3.1保护REST API的安全,在Spring 3.2,Spring MVC应用程序中,我使用了一种简单配置的基本身份验证方法:

<http create-session="stateless" entry-point-ref="authenticationFailedEntryPoint">
    <intercept-url pattern="/**" access="ROLE_USER"/>
    <http-basic />
</http>

正如您所看到的,我正在使用我的自定义入口点,我有我自己的ErrorResponse对象,我将以json格式添加到http响应中,请参阅下面的代码:

    @Component
public class AuthenticationFailedEntryPoint implements AuthenticationEntryPoint {
    static Logger log = Logger.getLogger(AuthenticationFailedEntryPoint.class);

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        log.error(ExceptionUtils.getStackTrace(authException));
        ErrorResponse errorResponse = new ErrorResponse();  

            ... here I fill my errorResponse object ...

        ObjectMapper jsonMapper = new ObjectMapper();

        response.setContentType("application/json;charset=UTF-8");
        response.setStatus(status); 

        PrintWriter out = response.getWriter();
        out.print(jsonMapper.writeValueAsString(errorResponse));   
    }
}

我用两个测试案例尝试了这种方法

  1. 尝试在不提供基本身份验证标头的情况下使用一个服务:

这是请求/响应:

GET http://localhost:8081/accounts/accounts?accountNumber=1013


 -- response --
401 Unauthorized
Server:  Apache-Coyote/1.1

Content-Type:  application/json;charset=UTF-8

Content-Length:  320

Date:  Fri, 25 Oct 2013 17:11:15 GMT

Proxy-Connection:  Keep-alive

{"status":401,"messages":[{"code":"000011","message":"You are not authorized to reach this endpoint"}]}

2.-尝试使用相同的服务,但现在使用错误的密码发送基本身份验证头:

这是请求/响应:

GET http://localhost:8081/accounts/accounts?accountNumber=1013
Authorization: Basic bXl1c2VyOmdvb2RieWU=


 -- response --
401 Unauthorized
Server:  Apache-Coyote/1.1

WWW-Authenticate:  Basic realm="Spring Security Application"

Content-Type:  text/html;charset=utf-8

Content-Length:  1053

Date:  Fri, 25 Oct 2013 17:03:09 GMT

Proxy-Connection:  Keep-alive

<html> ... ugly html generated by tc server ... </html>

正如您可以看到的,在第一种情况下,到达了入口点,并在正确处理异常的情况下执行了开始方法,并返回了json响应。但如果密码是错误的,就不会发生这种情况。

在日志中,我发现这两种情况产生了不同的流:

对于情况1(无身份验证标头):

...
2013-10-25 13:11:15,830 DEBUG tomcat-http--13 org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /accounts?accountNumber=1013; Attributes: [ROLE_USER]
2013-10-25 13:11:15,830 DEBUG tomcat-http--13 org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
2013-10-25 13:11:15,830 DEBUG tomcat-http--13 org.springframework.security.access.vote.AffirmativeBased - Voter: org.springframework.security.access.vote.RoleVoter@11da1f99, returned: -1
2013-10-25 13:11:15,831 DEBUG tomcat-http--13 org.springframework.security.access.vote.AffirmativeBased - Voter: org.springframework.security.access.vote.AuthenticatedVoter@7507ef7, returned: 0
2013-10-25 13:11:15,831 DEBUG tomcat-http--13 org.springframework.security.web.access.ExceptionTranslationFilter - Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
...

对于情况2(密码错误):

...
2013-10-25 13:03:08,941 DEBUG tomcat-http--11 org.springframework.security.web.authentication.www.BasicAuthenticationFilter - Basic Authentication Authorization header found for user 'myuser'
2013-10-25 13:03:08,941 DEBUG tomcat-http--11 org.springframework.security.authentication.ProviderManager - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
2013-10-25 13:03:09,544 DEBUG tomcat-http--11 org.springframework.security.authentication.dao.DaoAuthenticationProvider - Authentication failed: password does not match stored value
2013-10-25 13:03:09,545 DEBUG tomcat-http--11 org.springframework.security.web.authentication.www.BasicAuthenticationFilter - Authentication request for failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials
2013-10-25 13:00:30,136 DEBUG tomcat-http--9 org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed
...

第一种情况抛出一个AccessDeniedException,它正在被捕获并发送到我的入口点中的开始方法,但是第二种情况抛出一个BadCredentialsException并没有到达入口点。

这里奇怪的是,commitence方法应该接收AuthenticationException,但AccessDeniedException不是AuthenticationException,而BadCredentialsException是,请参阅Spring security 3.1 API文档中的继承树:

java.lang.Object
  extended by java.lang.Throwable
      extended by java.lang.Exception
          extended by java.lang.RuntimeException
              extended by org.springframework.security.access.AccessDeniedException

java.lang.Object
  extended by java.lang.Throwable
      extended by java.lang.Exception
          extended by java.lang.RuntimeException
              extended by org.springframework.security.core.AuthenticationException
                  extended by org.springframework.security.authentication.BadCredentialsException

为什么用不正确类型的异常调用开始方法,为什么在具有正确类型的BadCredentialsException时不调用?

编辑--由@luke实现答案

所描述的两个解决方案使用问题中显示的自定义AuthenticationEntryPoint,需要修改配置,选择以下两个选项之一:

>

  • 添加自定义basic_auth_filter:

    <http create-session="stateless" entry-point-ref="authenticationFailedEntryPoint">
        <intercept-url pattern="/**" access="ROLE_USER"/>
        <custom-filter position="BASIC_AUTH_FILTER" ref="authenticationFilter" /> 
    </http>
    
    <beans:bean id="authenticationFilter" class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
        <beans:constructor-arg name="authenticationManager" ref="authenticationManager" />
        <beans:constructor-arg name="authenticationEntryPoint" ref="authenticationFailedEntryPoint" />
    </beans:bean>
    

    或者将入口点添加到http-basic元素,IMO是最干净的解决方案:

    <http create-session="stateless" entry-point-ref="authenticationFailedEntryPoint">
        <intercept-url pattern="/**" access="ROLE_USER"/>
        <http-basic entry-point-ref="authenticationFailedEntryPoint" />
    </http>
    
  • 共有1个答案

    古彦
    2023-03-14

    我认为问题在于,在一个失败的基本身份验证之后,对入口点的调用是直接从BasicAuthenticationFilter进行的,默认情况下,BasicAuthenticationFilter将是内置实现。

    您还需要在http-basic元素上设置entry-point-ref属性来解决这个问题。

    或者,您可以将基本身份验证过滤器定义为bean并完全避免使用名称空间。

     类似资料:
    • 我现在读了很多关于微服务的书,但仍然不了解其中的一些部分。我画了以下图: 每个微服务有2个访问: REST:对于超文本传输协议使用 gRPC:用于内部/后台通信/交换 如果我想登录,我可以向我的身份验证服务发送一个Http请求。但是,如果我想访问需要您连接的Stuff服务,该怎么办? 假设用户希望显示数据库中可用的内容,服务人员将首先通过与身份验证服务交换来检查连接用户的“令牌”是否正确,然后返回

    • 问题内容: 我正在使用Spring MVC 并处理REST Api的所有异常。对于Web MVC控制器抛出的异常,它工作正常,但对于Spring Security自定义过滤器抛出的异常,它不工作,因为它们在调用控制器方法之前运行。 我有一个自定义的spring安全过滤器,它执行基于令牌的身份验证: 使用此自定义入口点: 并使用此类来全局处理异常: 我需要做的是返回一个详细的JSON主体,即使对于s

    • 我使用Spring MVC的和来处理REST API的所有异常。它对web mvc控制器引发的异常很好,但对spring security自定义筛选器引发的异常不起作用,因为它们在调用控制器方法之前运行。 我有一个自定义Spring Security筛选器,它执行基于令牌的身份验证: 使用此自定义入口点:

    • 问题内容: 尝试进行某些路由需要身份验证。 我有这个: 注意:是的,身份验证服务可以正常工作。 对于每条路由,我都会检查用户是否已通过身份验证,如果没有通过身份验证,则要将他们重定向到登录页面,如果已通过身份验证,则它将以“ /”路由登陆到第一页。 我得到的是: 我在哪里做错了? 问题答案: 一个简单的解决方案是制作一个包含所有受保护路线的(高阶组件)。 根据您的应用程序的嵌套方式,您可能需要利用

    • 我正在尝试使用paramiko对设备进行ssh操作,并在虚拟环境中使用以下代码运行一些命令 从getpass导入paramiko导入getpass 如果name==“main”: 当我尝试运行上面的代码时,我得到以下错误: 文件“param.py”,第14行,s.connect(hostname=主机名,username=用户名,password=密码)文件“/users/myuser/myvir

    • 如何在处理正文请求之前验证身份验证? 我正在使用vertx: 我想在处理正文请求之前验证身份验证。但我得到了错误的java。lang.IllegalStateException:请求已被读取 使用暂停功能上的延迟复制: 当运行delay(100)时(这是验证过程的示例),我得到了上面的错误。如果我对延迟(100)进行注释,它可以正常工作。