首先,我想评论一下,我已经检查了堆栈溢出中的其他问题,并根据以下答案实施了自己的方法:https : //stackoverflow.com/a/14425801/2487263和https://stackoverflow.com/a/16101649 / 2487263
我试图在Spring 3.2 Spring MVC应用程序中使用Spring Security 3.1来保护REST API,但我正在使用具有简单配置的基本身份验证方法:
<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));
}
}
我用两个测试案例尝试了该方法:
这是请求/响应:
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>
如您所见,在第一种情况下,已到达入口点,并且以适当的异常处理方式执行了begin方法,并返回了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,该异常将被捕获并发送到我的入口点中的begin方法,但是第二种情况将抛出BadCredentialsException并没有到达入口点。
奇怪的是,Begin方法应该接收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
为什么使用非正确类型的异常调用begin方法,并且当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>
我认为问题在于,在基本身份验证失败之后,直接从BasicAuthenticationFilter进行对入口点的调用,默认情况下,它将是内置实现。
您还需要entry-point-ref
在http-basic
元素上设置属性以解决此问题。
另外,您可以将基本身份验证过滤器定义为Bean,并完全避免使用名称空间。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句