【问题标题】:401 Unauthorised due to CORS when sending request to get user token401 Unauthorized 由于 CORS 发送请求以获取用户令牌时
【发布时间】:2023-03-09 02:33:01
【问题描述】:

最近我开始使用 angularjs 和 spring mvc 实现基于令牌的安全系统。思路如下: 1.访问/user/authenticate获取安全令牌并将令牌保存到本地存储 2.对于angularJS客户端发送的每一个请求,使用拦截器为请求注入一个X-Auth-Token头。

在我的 spring 后端中,我实现了一个 AuthenticationTokenProcessingFilter 和一个 CustomAuthenticationEntryPoint。第一个用于从标头中提取令牌并检查其是否有效,第二个用于在请求未通过身份验证时返回 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 过滤器有关)并且我的后端发送一个 401 Unauthorized 响应。

能否请您提供更多详细信息,为什么会发生这种情况?

提前谢谢你!

【问题讨论】:

标签: angularjs spring-mvc authentication cors


【解决方案1】:

OPTIONS 请求是所谓的preflight 请求:

保护资源免受无法实现的跨域请求 在此规范存在之前源自某些用户代理 发出预检请求以确保资源知道这一点 规范。

基本上,这意味着只要资源(后端服务器)没有在响应(给客户端)中提供OriginAccess-Control-*,就会发出OPTIONS 请求。只要您的后端和客户端(webapp)位于不同的域下,您就需要激活CORS,例如后端在domainA 下可用,您的客户端在domainB 下可用。甚至domainA:80domain:8080 也被视为不同的域。

【讨论】:

    【解决方案2】:

    我认为我遇到了类似(相同?)的问题,我稍微调整了WebSecurityConfigurerAdapter::configure(HttpSecurity http) 中的过滤器链来解决它。

    我认为在您的情况下发生的情况是浏览器发送一个 OPTIONS 请求来确定您的服务器是否允许 CORS。 OPTIONS 请求在您的过滤器链中处理并最终匹配

    .antMatchers("/**").authenticated()
    

    现在您可能会在 CustomAuthenticationEntryPoint() 中结束并返回 401。

    您可以添加:

    .antMatchers(HttpMethod.OPTIONS, "/user/authenticate/").permitAll()
    

    但是,我认为您之后仍然会遇到问题。当客户端现在想要访问其他资源时,必须在标头中发送令牌。这很可能会导致另一个不包含您的令牌的 OPTIONS 请求,因此您最终会遇到同样的问题。

    所以我所做的是明确允许所有 OPTIONS 请求而不检查身份验证。

    .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
    

    现在你绕过你的安全过滤器,你不会返回 401。我认为这不会影响应用程序的安全性,只要你不会在控制器中自己处理 OPTIONS 请求并允许某人访问关键数据。

    【讨论】:

    • 你把.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()放在哪里了?
    • 它必须进入 Spring 的 SecurityConfig 看看这个博文的例子:tux2323.blogspot.com/2013/07/… 网上有更多的例子。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-09
    • 2017-08-27
    • 2019-11-02
    • 2014-12-02
    • 2017-02-02
    • 1970-01-01
    • 2017-07-12
    相关资源
    最近更新 更多