401 Unauthorized due to CORS 发送请求以获取用户令牌时
401 Unauthorised due to CORS when sending request to get user token
最近我开始使用 angularjs 和 spring mvc 实现基于令牌的安全系统。这个想法如下:
1.访问/user/authenticate获取安全令牌并将令牌保存到本地存储
2、对于angularJS客户端发送的每个请求,使用拦截器向请求中注入一个X-Auth-Tokenheader
在我的 spring back-end 中,我实现了 AuthenticationTokenProcessingFilter 和 CustomAuthenticationEntryPoint。第一个用于从 header 中提取令牌并检查它是否有效,第二个用于 return 在请求未通过身份验证时显示 401 未授权状态。
请查找有关我的后端代码的一些详细信息
AuthenticationController.java
@RestController
@RequestMapping(value="user")
public class AuthenticationController {
@RequestMapping(value="authenticate", method = RequestMethod.POST)
public ResponseEntity<?> login(@RequestParam("email") String email,
@RequestParam("password") String password) {
//Check if user is valid and return token
}
}
SecurityConfig.java
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UsersRepository usersRepo;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {...}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(
new AuthenticationTokenProcessingFilter(usersRepo),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(this.corsFilter(), UsernamePasswordAuthenticationFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable().exceptionHandling()
.and()
.httpBasic()
.authenticationEntryPoint(new CustomAuthenticationEntryPoint())
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/user/authenticate").permitAll()
.antMatchers("/**").authenticated()
.anyRequest().authenticated();
}
@Bean
public CORSFilter corsFilter() {
return new CORSFilter();
}
}
CORSFilter.java
public class CORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With, Origin, X-Auth-Token");
response.addHeader("Access-Control-Expose-Headers", "X-Auth-Token");
chain.doFilter(req, res);
}
}
现在我正在使用以下 angularjs 代码来查询不在防火墙后面的 /user/authenticate 端点
return $http.post(baseUrl + 'user/authenticate', 'email='+username+'&password='+password,
{
headers : {
'content-type' : 'application/x-www-form-urlencoded'
}
}
);
当我使用上面的代码时,一切正常。但是,如果我从请求中删除 headers 参数,我的 angularjs 客户端会发送一个 OPTION 请求(而不是 POST 请求——我想这与我的 CORS 过滤器有关)和我的支持end 发送 401 Unauthorized 响应。
你能告诉我更多细节,为什么会这样吗?
提前致谢!
OPTIONS 请求是所谓的 preflight 请求:
To protect resources against cross-origin requests that could not
originate from certain user agents before this specification existed a
preflight request is made to ensure that the resource is aware of this
specification.
基本上,这意味着每当资源(后端服务器)未在响应(对客户端)中提供 Origin
和 Access-Control-*
时,就会发出 OPTIONS
请求。一旦您的后端和客户端 (webapp) 位于不同的域下,您将需要 CORS
激活,例如后端在 domainA
下可用,您的客户端在 domainB
下可用。甚至 domainA:80
和 domain:8080
也被视为不同的域。
我想我有一个类似(相同?)的问题,我在 WebSecurityConfigurerAdapter::configure(HttpSecurity http)
中稍微调整了我的过滤器链来解决它。
我认为你的情况是浏览器发送一个 OPTIONS 请求来确定你的服务器是否允许 CORS。 OPTIONS 请求在您的过滤器链中处理,最终将匹配
.antMatchers("/**").authenticated()
现在您可能会进入 CustomAuthenticationEntryPoint() 和 return 401。
您可以添加:
.antMatchers(HttpMethod.OPTIONS, "/user/authenticate/").permitAll()
不过,我想你之后还是会遇到问题。当客户端现在想要访问其他资源时,必须在 header 中发送令牌。这很可能会导致另一个不包含您的令牌的 OPTIONS 请求,因此您最终会遇到同样的问题。
所以我所做的是在不检查身份验证的情况下明确允许所有 OPTIONS 请求。
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
现在你绕过你的安全过滤器,你不会 return 401。我认为这不会影响应用程序的安全,只要你不自己处理 OPTIONS 请求控制器并允许某人访问关键数据。
最近我开始使用 angularjs 和 spring mvc 实现基于令牌的安全系统。这个想法如下: 1.访问/user/authenticate获取安全令牌并将令牌保存到本地存储 2、对于angularJS客户端发送的每个请求,使用拦截器向请求中注入一个X-Auth-Tokenheader
在我的 spring back-end 中,我实现了 AuthenticationTokenProcessingFilter 和 CustomAuthenticationEntryPoint。第一个用于从 header 中提取令牌并检查它是否有效,第二个用于 return 在请求未通过身份验证时显示 401 未授权状态。
请查找有关我的后端代码的一些详细信息
AuthenticationController.java
@RestController
@RequestMapping(value="user")
public class AuthenticationController {
@RequestMapping(value="authenticate", method = RequestMethod.POST)
public ResponseEntity<?> login(@RequestParam("email") String email,
@RequestParam("password") String password) {
//Check if user is valid and return token
}
}
SecurityConfig.java
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UsersRepository usersRepo;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {...}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(
new AuthenticationTokenProcessingFilter(usersRepo),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(this.corsFilter(), UsernamePasswordAuthenticationFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable().exceptionHandling()
.and()
.httpBasic()
.authenticationEntryPoint(new CustomAuthenticationEntryPoint())
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/user/authenticate").permitAll()
.antMatchers("/**").authenticated()
.anyRequest().authenticated();
}
@Bean
public CORSFilter corsFilter() {
return new CORSFilter();
}
}
CORSFilter.java
public class CORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With, Origin, X-Auth-Token");
response.addHeader("Access-Control-Expose-Headers", "X-Auth-Token");
chain.doFilter(req, res);
}
}
现在我正在使用以下 angularjs 代码来查询不在防火墙后面的 /user/authenticate 端点
return $http.post(baseUrl + 'user/authenticate', 'email='+username+'&password='+password,
{
headers : {
'content-type' : 'application/x-www-form-urlencoded'
}
}
);
当我使用上面的代码时,一切正常。但是,如果我从请求中删除 headers 参数,我的 angularjs 客户端会发送一个 OPTION 请求(而不是 POST 请求——我想这与我的 CORS 过滤器有关)和我的支持end 发送 401 Unauthorized 响应。
你能告诉我更多细节,为什么会这样吗?
提前致谢!
OPTIONS 请求是所谓的 preflight 请求:
To protect resources against cross-origin requests that could not originate from certain user agents before this specification existed a preflight request is made to ensure that the resource is aware of this specification.
基本上,这意味着每当资源(后端服务器)未在响应(对客户端)中提供 Origin
和 Access-Control-*
时,就会发出 OPTIONS
请求。一旦您的后端和客户端 (webapp) 位于不同的域下,您将需要 CORS
激活,例如后端在 domainA
下可用,您的客户端在 domainB
下可用。甚至 domainA:80
和 domain:8080
也被视为不同的域。
我想我有一个类似(相同?)的问题,我在 WebSecurityConfigurerAdapter::configure(HttpSecurity http)
中稍微调整了我的过滤器链来解决它。
我认为你的情况是浏览器发送一个 OPTIONS 请求来确定你的服务器是否允许 CORS。 OPTIONS 请求在您的过滤器链中处理,最终将匹配
.antMatchers("/**").authenticated()
现在您可能会进入 CustomAuthenticationEntryPoint() 和 return 401。
您可以添加:
.antMatchers(HttpMethod.OPTIONS, "/user/authenticate/").permitAll()
不过,我想你之后还是会遇到问题。当客户端现在想要访问其他资源时,必须在 header 中发送令牌。这很可能会导致另一个不包含您的令牌的 OPTIONS 请求,因此您最终会遇到同样的问题。
所以我所做的是在不检查身份验证的情况下明确允许所有 OPTIONS 请求。
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
现在你绕过你的安全过滤器,你不会 return 401。我认为这不会影响应用程序的安全,只要你不自己处理 OPTIONS 请求控制器并允许某人访问关键数据。