2010-12-09 13 views
12

Estoy tratando de usar Spring Security 3.0.5 en mi aplicación web. Básicamente, quiero tener un servicio web que devuelva datos en formato json a través del HTTP GET.Manejar el mensaje de error no autorizado para la autenticación básica en Spring Security

He implementado un servicio RESTful que devuelve datos cuando se solicita la url http://localhost:8080/webapp/json. Esto funciona muy bien con el siguiente comando curl

> curl http://localhost:8080/webapp/json 
{"key":"values"} 

Después añadí autenticación básica mediante la seguridad de la primavera, puedo usar los siguientes comandos para obtener los datos

> 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"} 

La página de error Tomcat estándar ex comando devuelve desde ahora requiere nombre de usuario y contraseña. Mi pregunta es si es posible manejar el acceso denegado de tal forma que imprima mi propio mensaje de error. decir

> curl http://localhost:8080/webapp/json 
{"error":"401", "message":"Username and password required"} 

Aquí está mi configuración de seguridad primavera y AccessDeniedHandler. Como puede ver, estoy tratando de agregar access-denied-handler que simplemente imprime una cadena a través de la respuesta de servlet, pero todavía no imprime mi propio mensaje en la línea de comandos.

<?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\"}"); 
    } 

} 
+0

Se puede comprobar la salida de rizo o -S -v rizo? – Raghuram

+0

He modificado mi pregunta. No estaba imprimiendo nada, pero no imprime lo que quiero. El rizo -S y el rizo -v imprimen la página de error 401 de tomcat estándar. -v tiene más detalles de la solicitud/respuesta – gigadot

Respuesta

15

He resuelto mi problema, así que creo que debería compartirlo aquí. Esta configuración permite al servidor enviar mensajes de error de manera diferente según el software solicitante. Si la solicitud proviene de un navegador web, comprobará el encabezado User-Agent y lo redireccionará a un inicio de sesión de formulario si es necesario. Si la solicitud proviene, por ejemplo, curl, imprimirá un mensaje de texto sin formato cuando la autenticación falle.

<?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 extendiendo 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()); 
    } 
} 
0

Me pregunto si alguna vez se hacía a su AccessDeniedHandler. ¿El error se debe a Autenticación o Autorización? ¿Se llama a AccessDeniedHandeler para ambos escenarios? Estoy en el proceso de resolver esto yo mismo ahora mismo.

+0

Tienes razón. No hace acceso a AccessDeniedHandler porque todavía está en procesos de Autenticación o Autorización. – gigadot

+0

Tengo una especie de solucionado esto. Spring Security lo activa y lo devuelve a las 401 páginas definidas por su aplicación en web.xml. Bueno, si quieres atrapar eso, tienes que escribir tu propio AuthenticationProcessFilter y manejar el tipo de contenido y la respuesta de error allí. Me he dado cuenta de que algunas personas simplemente están dejando que el estado HTTP esté allí, pero no me gusta eso y terminaré escribiendo un CustomAuthenticationProcessingFilter que mira la extensión de solicitud original o acepta el encabezado y devuelve la respuesta adecuada. – bh5k

+0

Lo he resuelto de forma ligeramente diferente, pero es bastante potente que me permite tener el inicio de sesión de formulario al mismo tiempo. He publicado a continuación. Si comparte su código, puede ser muy útil – gigadot

Cuestiones relacionadas