无法使用Angular和Spring Security

时间:2015-06-19 10:43:22

标签: angularjs spring cors

我无法正常使用Angular,CORS,SpringSecurity和Basic Authentication。我有以下Angular Ajax调用,我正在尝试设置标头以在请求中包含基本授权标头。

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

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

的web.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>

CorsFilter

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

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

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

更新了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>

安全-config.xml中

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

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

由于

2 个答案:

答案 0 :(得分:4)

问题是angular正在发送没有HTTP Authorization标头的HTTP OPTIONS请求。然后,Spring安全性处理该请求并发回401 Unauthorized响应。因此,我的角度应用程序在OPTIONS请求中从spring获得401响应,而不是200响应告诉angular继续并发送具有Authorization标头的POST请求。所以修复是

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

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

这篇文章让我整理出来:Standalone Spring OAuth2 JWT Authorization Server + CORS

这是我的代码:

的web.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>

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

这是我的Angular代码:

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

希望有所帮助。

答案 1 :(得分:0)

参加聚会有点晚,但我遇到了类似的问题,并认为我的解决方案可能会对某人有所帮助。 Joe Rice的解决方案确实对我有所帮助,但我必须进行一些修改。我还应该提到我不是Spring或Tomcat的专家,所以这可能不是一个完美的解决方案。

我使用React客户端(在localhost:3000上运行),Tomcat服务器(localhost:8080)和Spring Security进行了设置。我必须让Tomcat处理CORS标头,因为我在应用程序服务器上运行了两个.war文件:我的应用程序和Geowebcache。让Spring Security处理标头对我来说是不够的,因为那意味着当客户端直接与Geowebcache通信时,将不会添加标头。

我还使用通过Spring Security实现的登录页面。对于所有常规请求,使用Tomcat'CorsFilter'都可以正常工作,但是由于某些原因,未在客户端的登录请求中添加CORS标头,从而由于缺少“ Access-Control”而导致缺少该响应,导致浏览器阻止了该响应-Allow-Origin'标头。我的解决方案是手动将标头添加到客户端的登录调用中,然后让Tomcat配置处理其余的标头。这是我的设置。

Spring Security-web.xml:

<filter>
    <filter-name>corsFilter</filter-name>
    <filter-class>path.to.cors.filter.security.SimpleCorsFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>corsFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Spring Security Config:

 http.csrf().disable()
            .formLogin().loginPage("/login").permitAll()
            .loginProcessingUrl("/perform_login")
            .defaultSuccessUrl("/index.html", true)
            .failureUrl("/login?error=true");

SimpleCorsFilter.java:

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;
    if (request.getRequestURI().endsWith("perform_login")) {
        response.setHeader("Access-Control-Allow-Origin", "http://localhost:3000");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        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");
    }

    chain.doFilter(req, res);
}

Tomcat-web.xml

<filter>
  <filter-name>CorsFilter</filter-name>
  <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
  <init-param>
    <param-name>cors.allowed.origins</param-name>
    <param-value>http://localhost:3000</param-value>
  </init-param>
  <init-param>
    <param-name>cors.support.credentials</param-name>
    <param-value>true</param-value>
  </init-param>
  <init-param>
    <param-name>cors.exposed.headers</param-name>
    <param-value>Content-Disposition,Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>CorsFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

我正在使用Axios来从客户端发出请求,并且我必须添加'withCredentials:true'来使服务器接受请求。这是我的Axios配置:

axios.create({
  baseURL: process.env.NODE_ENV === 'development' ? 'http://localhost:8080/api/' : '/api/',
  timeout: 300000,
  withCredentials: true
});

希望这可以帮助某人!