我无法正常使用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过滤器链中,它是否与预检通信有关?任何帮助将不胜感激。
由于
答案 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
});
希望这可以帮助某人!