Java 处理 Spring Security 中基本身份验证的未授权错误消息
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4397062/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Handle unauthorized error message for Basic Authentication in Spring Security
提问by gigadot
I am trying to use Spring Security 3.0.5 in my web application. Basically, I want to have a web service which return data in json format via HTTP GET
.
我正在尝试在我的 Web 应用程序中使用 Spring Security 3.0.5。基本上,我想要一个 Web 服务,它通过 .json 格式以 json 格式返回数据HTTP GET
。
I have implemented a RESTful service which returns data when the url http://localhost:8080/webapp/json
is requested. This works fine with the following curl command
我已经实现了一个 RESTful 服务,它在http://localhost:8080/webapp/json
请求url 时返回数据。这适用于以下 curl 命令
> curl http://localhost:8080/webapp/json
{"key":"values"}
After I added basic authentication using spring security, I can use following commands to get the data
在使用 spring security 添加基本身份验证后,我可以使用以下命令获取数据
> curl http://localhost:8080/webapp/json
<html><head><title>Apache Tomcat/6.0.29 - Error report .....
> curl -u username:password http://localhost:8080/webapp/json
{"key":"values"}
The former command returns standard tomcat error page since now it requires username and password. My question is whether it is possible to handle access denied in such a way that it prints out my own error message?i.e.
前一个命令返回标准的 tomcat 错误页面,因为现在它需要用户名和密码。我的问题是是否有可能以打印出我自己的错误消息的方式处理拒绝访问?IE
> curl http://localhost:8080/webapp/json
{"error":"401", "message":"Username and password required"}
Here is my spring security configuration and AccessDeniedHandler
. As you can see, I am trying to add access-denied-handler
which simply prints out a string through servlet response but it still does not print my own message on command line.
这是我的 spring 安全配置和AccessDeniedHandler
. 如您所见,我正在尝试添加access-denied-handler
它只是通过 servlet 响应打印出一个字符串,但它仍然没有在命令行上打印我自己的消息。
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<global-method-security secured-annotations="enabled"/>
<beans:bean name="access-denied" class="webapp.error.JSONAccessDeniedHandler"></beans:bean>
<http auto-config="true">
<access-denied-handler ref="access-denied"/>
<intercept-url pattern="/json" access="ROLE_USER,ROLE_ADMIN" />
<http-basic />
</http>
<authentication-manager>
<authentication-provider>
<password-encoder hash="md5"/>
<user-service>
...
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>
AccessDeniedHandler.java
AccessDeniedHandler.java
package webapp.error;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
public class JSONAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
PrintWriter writer = response.getWriter();
writer.print("{\"error\":\"401\", \"message\":\"Username and password required\"}");
}
}
采纳答案by gigadot
I have solved my problem so I think I should share it here. This configuration allows server to send out error message differently depending on the requesting software. If the request comes from a web browser, it will check the User-Agent
header and redirect to a form login if necessary. If the request comes from, for example, curl
, it will print out plain text error message when the authentication fails.
我已经解决了我的问题,所以我想我应该在这里分享它。此配置允许服务器根据请求的软件发送不同的错误消息。如果请求来自 Web 浏览器,它将检查User-Agent
标题并在必要时重定向到表单登录。例如,如果请求来自 ,curl
则会在身份验证失败时打印出纯文本错误消息。
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<!-- AspectJ pointcut expression that locates our "post" method and applies security that way
<protect-pointcut expression="execution(* bigbank.*Service.post*(..))" access="ROLE_TELLER"/>-->
<sec:global-method-security secured-annotations="enabled"/>
<bean id="basicAuthenticationFilter"
class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter"
p:authenticationManager-ref="authenticationManager"
p:authenticationEntryPoint-ref="basicAuthenticationEntryPoint" />
<bean id="basicAuthenticationEntryPoint"
class="webapp.PlainTextBasicAuthenticationEntryPoint"
p:realmName="myWebapp"/>
<bean id="formAuthenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"
p:loginFormUrl="/login.jsp"/>
<bean id="daep" class="org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint">
<constructor-arg>
<map>
<entry key="hasHeader('User-Agent','Mozilla') or hasHeader('User-Agent','Opera') or hasHeader('User-Agent','Explorer')" value-ref="formAuthenticationEntryPoint" />
</map>
</constructor-arg>
<property name="defaultEntryPoint" ref="basicAuthenticationEntryPoint"/>
</bean>
<sec:http entry-point-ref="daep">
<sec:intercept-url pattern="/login.jsp*" filters="none"/>
<sec:intercept-url pattern="/json" access="ROLE_USER,ROLE_ADMIN" />
<sec:intercept-url pattern="/json/*" access="ROLE_USER,ROLE_ADMIN" />
<sec:logout
logout-url="/logout"
logout-success-url="/home.jsp"/>
<sec:form-login
login-page="/login.jsp"
login-processing-url="/login"
authentication-failure-url="/login.jsp?login_error=1" default-target-url="/home.jsp"/>
<sec:custom-filter position="BASIC_AUTH_FILTER" ref="basicAuthenticationFilter" />
</sec:http>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider>
...
</sec:authentication-provider>
</sec:authentication-manager>
</beans>
PlainTextBasicAuthenticationEntryPoint
by extending org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint
PlainTextBasicAuthenticationEntryPoint
通过扩展 org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
public class PlainTextBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.addHeader("WWW-Authenticate", "Basic realm=\"" + getRealmName() + "\"");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
PrintWriter writer = response.getWriter();
writer.println("HTTP Status " + HttpServletResponse.SC_UNAUTHORIZED + " - " + authException.getMessage());
}
}
回答by bh5k
I am wondering if it is ever making it to your AccessDeniedHandler. Is the error because of Authentication or Authorization? Does the AccessDeniedHandeler get called for both scenarios? I am in the process of solving this myself right now.
我想知道它是否曾经进入您的 AccessDeniedHandler。错误是因为身份验证还是授权?AccessDeniedHandeler 是否在这两种情况下都被调用?我现在正在自己解决这个问题。