【问题标题】:Spring Security, REST basic authentication issueSpring Security,REST 基本身份验证问题
【发布时间】:2013-05-13 02:18:00
【问题描述】:

在使用 Spring 的基本身份验证时,我遇到了与 HTTP 响应标头“Access-Control-Allow-Origin”相关的问题。当我手动进行身份验证时,如下面的代码(我正在使用 REST):

@RequestMapping(value = "/login", method = RequestMethod.POST, consumes = "application/json")
@ResponseStatus(value = HttpStatus.OK)
public void login(@RequestBody String body, HttpServletResponse response)
        throws IOException {
    try {
        User user = gson.fromJson(body, User.class);

        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
                usuario.getUsername(), usuario.getPassword());

        authenticationManager.authenticate(token);
    } catch (BadCredentialsException e) {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
    } catch (Exception e) {
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
    }
}

一切正常,我收到以下 HTTP 响应:

HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
Content-Type: text/html;charset=utf-8
Content-Length: 951
Date: Fri, 17 May 2013 19:14:36 GMT

如您所见,“Access-Control-Allow-Origin”出现在响应中。 这里一切都很好。我可以在我的 ajax 调用中捕获 401 错误。

但是当自动执行身份验证时,如下代码:

@RequestMapping(value = "/name", method = RequestMethod.POST, consumes = "application/json")
@PreAuthorize("hasRole('ROLE_CUSTOMER')")
public @ResponseBody String getName(HttpServletResponse response) throws IOException {
    String json = null;

    try {
        User userSession = (User) SecurityContextHolder.getContext()
                .getAuthentication().getPrincipal();

        Customer customer = customerDao.getNameByUsername(userSession.getUsername());

        json = gson.toJson(customer);

    } catch (Exception e) {
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
    }

    return json;
}

HTTP 响应是:

HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
WWW-Authenticate: Basic realm="Spring Security Application"
Content-Type: text/html;charset=utf-8
Content-Length: 981
Date: Fri, 17 May 2013 19:41:08 GMT

响应中没有“Access-Control-Allow-Origin”

Google Chrome 控制台显示以下错误:

Origin null is not allowed by Access-Control-Allow-Origin

我的 ajax 调用不会返回 401 Unauthorized 错误,即使 HTTP 响应返回它(上面的响应),我也会收到未知错误。

我发现对于所有浏览器,我需要在 HTTP 响应中添加一个“Access-Control-Allow-Origin”,否则它们会产生某种静默错误并且我的 ajax 调用将失败(无法捕获 401错误)。实际上,javascript 会默默地失败。 XMLHttpRequest 不接受没有“Access-Control-Allow-Origin”的 HTTP 响应。

如何让 Spring 在 HTTP 响应中注入此“Access-Control-Allow-Origin”以进行基本身份验证?

这是我的 Spring Security xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:security="http://www.springframework.org/schema/security"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
          http://www.springframework.org/schema/security
          http://www.springframework.org/schema/security/spring-security-3.1.xsd">

    <security:http create-session="stateless" entry-point-ref="authenticationEntryPoint">
        <security:intercept-url pattern="/customer/**" />
        <security:http-basic />
        <security:custom-filter ref="basicAuthenticationFilter"
            after="BASIC_AUTH_FILTER" />
    </security:http>

    <bean id="basicAuthenticationFilter"
        class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
        <property name="authenticationManager" ref="authenticationManager" />
        <property name="authenticationEntryPoint" ref="authenticationEntryPoint" />
    </bean>

    <bean id="authenticationEntryPoint"
        class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint">
        <property name="realmName" value="teste.com" />
    </bean>

    <!-- It is responsible for validating the user's credentials -->
    <security:authentication-manager alias="authenticationManager">

        <!-- It is responsible for providing credential validation to the AuthenticationManager -->
        <security:authentication-provider>
            <security:password-encoder ref="passwordEncoder" />

            <security:jdbc-user-service
                data-source-ref="mySQLdataSource"
                users-by-username-query="select username, password, enabled from usuario where username = ?"
                authorities-by-username-query="select username, papel from autoridade where username = ?" />

        </security:authentication-provider>

    </security:authentication-manager>

    <bean class="org.springframework.security.crypto.password.StandardPasswordEncoder"
        id="passwordEncoder" />

</beans>

【问题讨论】:

    标签: java spring spring-mvc spring-security


    【解决方案1】:

    刚刚找到了自己的路:

    首先,我真的不记得为什么我把这行代码放在这里了,但是它弄乱了我的代码:

    <security:http-basic />
    

    其次,这个答案告诉我路径:Handle unauthorized error message for Basic Authentication in Spring Security。我必须创建一个自定义身份验证入口点才能发送 Access-Control-Allow-Origin 东西。

    这是我现在的代码:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:security="http://www.springframework.org/schema/security"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="
                  http://www.springframework.org/schema/beans
                  http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
                  http://www.springframework.org/schema/security
                  http://www.springframework.org/schema/security/spring-security-3.1.xsd">
    
        <security:http create-session="stateless"
            entry-point-ref="authenticationEntryPoint">
            <security:intercept-url pattern="/api/admin/**" />
            <security:intercept-url pattern="/medico/**" />
            <!-- <security:http-basic />  -->
            <security:custom-filter ref="basicAuthenticationFilter"
                after="BASIC_AUTH_FILTER" />
        </security:http>
    
        <bean id="basicAuthenticationFilter"
            class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
            <property name="authenticationManager" ref="authenticationManager" />
            <property name="authenticationEntryPoint" ref="authenticationEntryPoint" />
        </bean>
    
                <!-- 
        <bean id="authenticationEntryPoint" 
            class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint">
            <property name="realmName" value="test.com" />
        </bean> -->
    
    
        <bean id="authenticationEntryPoint" 
            class="com.test.util.PlainTextBasicAuthenticationEntryPoint">
            <property name="realmName" value="test.com" />
        </bean> 
    
        <!-- It is responsible for validating the user's credentials -->
        <security:authentication-manager alias="authenticationManager">
    
            <!-- It is responsible for providing credential validation to the AuthenticationManager -->
            <security:authentication-provider>
                <security:password-encoder ref="passwordEncoder" />
    
                <security:jdbc-user-service
                    data-source-ref="mySQLdataSource"
                    users-by-username-query="select username, password, enabled from usuario where username = ?"
                    authorities-by-username-query="select username, papel from autoridade where username = ?" />
    
            </security:authentication-provider>
    
        </security:authentication-manager>
    
        <bean
            class="org.springframework.security.crypto.password.StandardPasswordEncoder"
            id="passwordEncoder" />
    
    </beans>
    
    package com.test.util;
    
    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("Access-Control-Allow-Origin", "null");
                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());
            }
    
    }
    

    我现在的 http 响应:

    HTTP/1.1 401 Unauthorized
    Server: Apache-Coyote/1.1
    Access-Control-Allow-Origin: null
    WWW-Authenticate: Basic realm="test.com"
    Content-Length: 35
    Date: Mon, 20 May 2013 20:05:03 GMT
    
    HTTP Status 401 - Bad credentials
    

    在修改之前,我收到了这个错误信息:

    OPTIONS http://localhost:8080/test/customer/name 200 (OK) jquery-1.8.2.min.js:2
    XMLHttpRequest cannot load http://localhost:8080/test/customer/name. Origin null is     not allowed by Access-Control-Allow-Origin. 
    

    现在正如预期的那样,我得到了这个:

    OPTIONS http://localhost:8080/test/customer/name 200 (OK) jquery-1.8.2.min.js:2
    POST http://localhost:8080/test/customer/name 401 (Unauthorized) 
    

    【讨论】:

    • 嗨,我想问你以下问题。我想知道是否有特定的东西可以启用 CROSS 请求 + 基本授权 + Spring Security。到目前为止,我已经完成了设置 Tomcat 过滤器所需的一切(我在标头中添加了授权),我应该在 ajax 中执行我的请求,因为它无需身份验证即可工作,所以我想知道弹簧安全性是否没有引起问题.....也许你有一些关于它的意见
    • Access-Control-Allow-Origin 为前端地址。您可以设置“*”以允许所有。
    猜你喜欢
    • 2016-03-20
    • 2016-04-30
    • 2021-11-22
    • 2019-04-08
    • 2011-02-11
    • 1970-01-01
    • 1970-01-01
    • 2013-01-11
    • 2015-07-22
    相关资源
    最近更新 更多