有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java在Spring Security中处理未经授权的基本身份验证错误消息

我正在尝试在我的web应用程序中使用Spring Security 3.0.5。基本上,我想要一个web服务,它通过HTTP GET返回json格式的数据

我实现了一个RESTful服务,它在请求url http://localhost:8080/webapp/json时返回数据。这可以与下面的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"}

这是我的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

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

}

共 (2) 个答案

  1. # 1 楼答案

    我想知道它是否能到达你的AccessDeniedHandler。错误是因为身份验证还是授权?在这两种情况下,AccessDeniedHandler都会被调用吗?我现在正在自己解决这个问题

  2. # 2 楼答案

    我已经解决了我的问题,所以我想我应该在这里分享。此配置允许服务器根据请求的软件以不同的方式发送错误消息。如果请求来自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>
    

    通过扩展{}

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