【发布时间】:2015-06-05 10:07:38
【问题描述】:
我能够使用来自https://github.com/dsyer/spring-security-angular/blob/master/vanilla/README.md 的示例 Spring Boot Groovy Maven 实现成功运行此配置。当应用程序代码在 Java 和 Gradle 2.3 中实现时,它不起作用。在这种情况下,OPTIONS 响应有一个新的 X-Auth-Token。
尝试将提供的 maven 构建与我的 java 类一起使用,但仍然得到相同的 OPTIONS 401 未经授权的响应。所以这不是 Gradle 问题。
将两个 ResourceApplication Groovy 类复制到我的 Gradle 构建中,Angular ui 成功获得 OPTIONS 200 OK。所以java中的Spring CORS过滤器存在问题。
我有用于 java 和 groovy 的 Gradle 插件、sourceCompatibility 和 目标兼容性 = 1.7
java 版本“1.8.0_31” Java(TM) SE 运行时环境 (build 1.8.0_31-b13) Java HotSpot(TM) 64 位服务器 VM(内部版本 25.31-b07,混合模式)
开发者控制台日志验证相同的令牌是从 ui 服务器发送并由 angular 客户端接收的,但报告了 CORS 错误。
跨域请求被阻止:同源策略不允许读取位于http://localhost:9000/ 的远程资源。这可以通过将资源移动到同一域或启用 CORS 来解决。本地主机:9000
@SpringBootApplication
@RestController
@EnableRedisHttpSession
public class AngularDemoApplication {
@RequestMapping("/user")
public Principal user(Principal user) {
return user;
}
@RequestMapping("/token")
@ResponseBody
public Map<String,String> token(HttpSession session) {
logger.debug("********** TOKEN *********** = "+session.getId());
return Collections.singletonMap("token", session.getId());
}
@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().logout().and().authorizeRequests()
.antMatchers("/index.html", "/home.html", "/login.html", "/").permitAll()
.anyRequest().authenticated().and().csrf().csrfTokenRepository(csrfTokenRepository())
.and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
}
private Filter csrfHeaderFilter() {
return new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class
.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie == null || token != null
&& !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
};
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
public static void main(String[] args) {
SpringApplication.run(AngularDemoApplication.class, args);
}
}
@SpringBootApplication
@RestController
@EnableRedisHttpSession
public class ResourceApplication {
@RequestMapping("/")
public Map<String,Object> home() {
Map<String,Object> model = new HashMap<String,Object>();
model.put("id", UUID.randomUUID().toString());
model.put("content", "Hello World");
return model;
}
@Bean
public HeaderHttpSessionStrategy sessionStrategy() {
return new HeaderHttpSessionStrategy();
}
public static void main(String[] args) {
SpringApplication.run(ResourceApplication.class, args);
}
}
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-auth-token, x-requested-with");
if (request.getMethod() != "OPTIONS" ) {
chain.doFilter(req, res);
} else {
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
Groovy 版本:
请求方法:选项
状态码:200 OK
请求标头:
主机:本地主机:9000
用户代理:Mozilla/5.0 (Windows NT 6.1; WOW64; rv:36.0) Gecko/20100101 Firefox/36.0
接受:text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
接受语言:en-US,en;q=0.5
接受编码:gzip、放气
来源:本地主机:8080
访问控制请求方法:GET
访问控制请求标头:x-auth-token、x-requested-with
连接:保持活动
响应标头:
访问控制允许标头:x-auth-token、x-requested-with
访问控制允许方法:POST、PUT、GET、OPTIONS、DELETE
访问控制允许来源:*
访问控制最大年龄:3600
内容长度:0
日期:格林威治标准时间 2015 年 3 月 31 日星期二 21:20:28
服务器:Apache-Coyote/1.1
请求方法:GET
状态码:200 OK
请求标头:
主机:本地主机:9000
用户代理:Mozilla/5.0 (Windows NT 6.1; WOW64; rv:36.0) Gecko/20100101 Firefox/36.0
接受:application/json, text/plain, /
接受语言:en-US,en;q=0.5
接受编码:gzip、放气
X-Auth-令牌:80e0c2d2-dab4-435d-886e-ae28bc8e636f
X-Requested-With: XMLHttpRequest
响应标头:
访问控制允许标头:x-auth-token、x-requested-with
访问控制允许方法:POST、PUT、GET、OPTIONS、DELETE
访问控制允许来源:*
访问控制最大年龄:3600
内容类型:application/json;charset=UTF-8
日期:格林威治标准时间 2015 年 3 月 31 日星期二 21:20:28
服务器:Apache-Coyote/1.1
严格的传输安全性: max-age=31536000 ;包括子域
传输编码:分块
参考:本地主机:8080/
来源:本地主机:8080
连接:保持活动
Redis 服务器密钥:
1)“春天:会话:过期:1427838660000”
2)“弹簧:会话:会话:80e0c2d2-dab4-435d-886e-ae28bc8e636f”
Java 版本:
请求方法:选项
状态码:401 未授权
请求标头:
主机:本地主机:9000
用户代理:Mozilla/5.0 (Windows NT 6.1; WOW64; rv:36.0) Gecko/20100101 Firefox/36.0
接受:text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
接受语言:en-US,en;q=0.5
接受编码:gzip、放气
来源:本地主机:8080
访问控制请求方法:GET
访问控制请求标头:x-auth-token、x-requested-with
连接:保持活动
响应标头:
访问控制允许标头:x-auth-token、x-requested-with
访问控制允许方法:POST、PUT、GET、OPTIONS、DELETE
访问控制允许来源:*
访问控制最大年龄:3600
允许:GET、HEAD、POST、PUT、DELETE、TRACE、OPTIONS、PATCH
内容长度:0
日期:格林威治标准时间 2015 年 3 月 31 日星期二 20:50:26
服务器:Apache-Coyote/1.1
严格的传输安全性: max-age=31536000 ;包括子域
WWW-Authenticate: Basic realm="Spring"
X-Auth-Token:8af7e1f4-e723-4ce6-8d21-54a7b10369f8
Redis 服务器密钥:
1)“弹簧:会话:会话:8af7e1f4-e723-4ce6-8d21-54a7b10369f8”
2)“春天:会话:过期:1427836860000”
3)“弹簧:会话:会话:c6a6cc31-eddc-40dd-99de-a6e1eecbf519”
【问题讨论】:
标签: java angularjs spring-security spring-boot spring-session