2010-12-09 37 views
12

J'essaie d'utiliser Spring Security 3.0.5 dans mon application Web. Fondamentalement, je veux avoir un service Web qui retourne des données au format json via HTTP GET.Traitement du message d'erreur non autorisé pour l'authentification de base dans Spring Security

J'ai implémenté un service RESTful qui renvoie des données lorsque l'adresse http://localhost:8080/webapp/json est demandée. Cela fonctionne très bien avec la commande boucle suivante

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

Après avoir ajouté l'authentification de base en utilisant la sécurité de printemps, je peux utiliser les commandes suivantes pour obtenir les données

> 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 première renvoie commande page standard d'erreur tomcat depuis maintenant il nécessite un nom d'utilisateur et un mot de passe. Ma question est de savoir s'il est possible de gérer l'accès refusé de telle sorte qu'il imprime mon propre message d'erreur? dire

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

Voici ma configuration de sécurité du printemps et AccessDeniedHandler. Comme vous pouvez le voir, j'essaie d'ajouter access-denied-handler qui imprime simplement une chaîne par l'intermédiaire de la réponse de servlet mais il n'imprime toujours pas mon propre message sur la ligne de commande.

<?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

Pouvez-vous vérifier la sortie de boucle S ou -v boucle? – Raghuram

+0

J'ai modifié ma question. Ça n'imprimait rien mais ça n'imprime pas ce que je veux. La curl -S et curl -v impriment tous deux la page d'erreur standard tomcat 401. -v a plus de détails sur la demande/réponse – gigadot

Répondre

15

J'ai résolu mon problème et je pense que je devrais le partager ici. Cette configuration permet au serveur d'envoyer un message d'erreur différemment en fonction du logiciel demandeur. Si la requête provient d'un navigateur Web, elle vérifie l'en-tête User-Agent et redirige vers un formulaire de connexion si nécessaire. Si la demande provient, par exemple, curl, il affichera un message d'erreur en texte brut lorsque l'authentification échoue.

<?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 par l'extension 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

Je me demande si jamais il est fait à votre AccessDeniedHandler. L'erreur est-elle due à l'authentification ou à l'autorisation? AccessDeniedHandeler est-il appelé pour les deux scénarios? Je suis en train de résoudre cela moi-même en ce moment.

+0

Vous avez raison. Il ne se rend pas à AccessDeniedHandler car il est toujours dans les processus d'authentification ou d'autorisation. – gigadot

+0

J'ai en quelque sorte résolu cela. Spring Security y répond et le remet aux 401 pages définies par votre application dans le fichier web.xml. Eh bien, si vous voulez piéger cela, vous devez écrire votre propre AuthenticationProcessFilter et gérer le type de contenu et la réponse d'erreur. J'ai remarqué que certaines personnes laissent juste le statut HTTP là où il y a un message d'erreur, mais je n'aime pas cela et finira par écrire un CustomAuthenticationProcessingFilter qui regarde l'extension de requête d'origine ou Accepter l'en-tête et redonner la réponse appropriée. – bh5k

+0

Je l'ai résolu légèrement différemment mais c'est assez puissant qui me permet d'avoir un formulaire de connexion en même temps. J'ai posté ci-dessous. Si vous partagez votre code, cela peut être très utile – gigadot