无法使用 Angular 和 Spring Security 设置身份验证标头

Joe*_*ice 5 spring cors angularjs

我无法很好地使用 Angular、CORS、SpringSecurity 和基本身份验证。我有以下 Angular Ajax 调用,我试图在其中设置标头以在请求中包含基本授权标头。

var headerObj = {headers: {'Authorization': 'Basic am9lOnNlY3JldA=='}};
return $http.get('http://localhost:8080/mysite/login', headerObj).then(function (response) {
    return response.data;
});
Run Code Online (Sandbox Code Playgroud)

我在 localhost:8888 上运行我的 angular 应用程序,在 localhost:8080 上运行我的后端服务器。所以我不得不在我的服务器端添加一个 CORS 过滤器,即 Spring MVC。所以我创建了一个过滤器并将它添加到我的 web.xml 中。

网页.xml

<filter>
    <filter-name>corsFilter</filter-name>
    <filter-class>com.abcinsights.controller.SimpleCorsFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>corsFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
Run Code Online (Sandbox Code Playgroud)

过滤器

public class SimpleCorsFilter extends OncePerRequestFilter {

@Override
public void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
    Enumeration<String> headerNames = req.getHeaderNames();
    while(headerNames.hasMoreElements()){
        String headerName = headerNames.nextElement();
        System.out.println("headerName " + headerName);
        System.out.println("headerVal " + req.getHeader(headerName));
    }               
    HttpServletResponse response = (HttpServletResponse) res;
    response.setHeader("Access-Control-Allow-Origin", "*"); 
    response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
    response.setHeader("Access-Control-Max-Age", "3600");
    response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Range, Content-Disposition, Content-Type, Authorization");
    chain.doFilter(req, res);
}   
}
Run Code Online (Sandbox Code Playgroud)

这一切正常,当我查看标头名称/值时,我看到我的 Authorization 标头带有 Basic 值和硬编码的 Base64 字符串。

然后我尝试添加 SpringSecurity,它将使用 Authorization 标头进行基本身份验证。这是我添加了 Spring Security 的 web.xml 和 spring security 配置。

更新的 web.xml

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/config/security-config.xml</param-value>
</context-param>

<filter>
  <filter-name>springSecurityFilterChain</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
  <filter-name>springSecurityFilterChain</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
Run Code Online (Sandbox Code Playgroud)

安全配置文件

<http auto-config="true" use-expressions="false">
    <intercept-url pattern="/**" access="ROLE_USER" />
    <http-basic/>
    <custom-filter ref="corsHandler" before="SECURITY_CONTEXT_FILTER"/>
</http>

<beans:bean id="corsHandler" class="com.abcinsights.controller.SimpleCorsFilter"/>

<authentication-manager>
    <authentication-provider>
        <jdbc-user-service data-source-ref="dataSource"/>
    </authentication-provider>
</authentication-manager>
Run Code Online (Sandbox Code Playgroud)

如您所见,我必须将我的 CORS 过滤器添加到 spring 安全过滤器链的前面(我还从 web.xml 中删除了它)。这似乎在我发出请求时仍会调用 CORS 过滤器的意义上起作用。但是来自我的 AJAX 调用的请求不再添加授权标头。当我删除 SpringSecurity 过滤器链并将我的 CORS 过滤器放回 web.xml 时,授权标头又回来了。我不知所措,但想知道当我的 CORS 过滤器在 web.xml 中独立时以一种方式工作,而在 Spring Security 过滤器链中以另一种方式工作时,它是否与预检通信有关?任何帮助,将不胜感激。

谢谢

Joe*_*ice 5

问题是 angular 发送的 HTTP OPTIONS 请求没有 HTTP 授权标头。然后 Spring Security 会处理该请求并发回 401 Unauthorized 响应。因此,我的 angular 应用程序在 OPTIONS 请求上收到了来自 spring 的 401 响应,而不是 200 响应,告诉 angular 继续并发送其中确实包含 Authorization 标头的 POST 请求。所以解决方法是

a) 将我的 CORSFilter 和 SpringSecurity 过滤器放在 web.xml 中(而不是从 Spring 中调用我的 CORS 过滤器)。不是 100% 确定这会有所作为,但这就是我最终的 web.xml 的样子。

b) 将以下链接中的代码添加到我的 CORS 过滤器,以便它不会将 OPTIONS 请求类型委托给 SpringSecurity。

这篇文章让我整理了一下:Standalone Spring OAuth2 JWT Authorization Server + CORS

这是我的代码:

网页.xml

<display-name>ABC Insights</display-name>
<servlet>
    <servlet-name>abcInsightsServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/config/servlet-config.xml</param-value>
    </init-param> 
</servlet>
<servlet-mapping>
    <servlet-name>abcInsightsServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<!--  Stand Alone Cross Origin Resource Sharing Authorization Configuration -->
<filter>
    <filter-name>corsFilter</filter-name>
    <filter-class>com.abcinsights.controller.SimpleCorsFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>corsFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>       
<!--  Spring Security Configuration -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/config/security-config.xml</param-value>
</context-param>    
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
Run Code Online (Sandbox Code Playgroud)

这是我的 CORS 过滤器:

public class SimpleCorsFilter extends GenericFilterBean {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;       
        response.setHeader("Access-Control-Allow-Origin", "*"); 
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Range, Content-Disposition, Content-Type, Authorization, X-CSRF-TOKEN");  

        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else {
            chain.doFilter(req, res);
        }           
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的角度代码:

var login = function (credentials) {
    console.log("credentials.email: " + credentials.email);
    console.log("credentials.password: " + credentials.password);

    var base64Credentials = Base64.encode(credentials.email + ':' + credentials.password);

    var req = {
        method: 'GET',
        url: 'http://localhost:8080/abcinsights/loginPage',
        headers: {
            'Authorization': 'Basic ' + base64Credentials
        }
    }

    return $http(req).then(function (response) {
        return response.data;
    });        
};
Run Code Online (Sandbox Code Playgroud)

希望有帮助。