我正在尝试在Web应用程序中使用Spring Security 3.0.5。基本上,我想拥有一个Web服务,该服务通过可以json格式返回数据HTTP GET。
我实现了一个RESTful服务,当http://localhost:8080/webapp/json请求url时该服务返回数据。使用以下curl命令可以正常工作
> curl http://localhost:8080/webapp/json {"key":"values"}
使用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"}
前一条命令返回标准的tomcat错误页面,因为它现在需要用户名和密码。我的问题是,是否有可能处理拒绝访问,以使其打印出我自己的错误消息?即
> curl http://localhost:8080/webapp/json {"error":"401", "message":"Username and password required"}
这是我的春季安全配置和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
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\"}"); } }
我已经解决了我的问题,所以我想在这里分享。此配置允许服务器根据请求的软件以不同方式发送错误消息。如果请求来自Web浏览器,它将检查User-Agent标头并在必要时重定向到表单登录名。例如,如果请求来自,curl则认证失败时,它将打印出纯文本错误消息。
User-Agent
<?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 通过扩展 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()); } }