Access to XMLHttpRequest at 'http://localhost:9600/user/logout' from origin 'http://localhost:9528' has been blocked by CORS policy:
Request header field x-token is not allowed by Access-Control-Allow-Headers in preflight response.
Vue axios springboot springsecurity 前后端分离
配置如下: 前端: .env.development: ENV = 'development' # base api VUE_APP_BASE_API = 'http://localhost:9600' request.js: const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url // TODO withCredentials: true, // send cookies when cross-domain requests timeout: 5000 // request timeout }) user.js export function login(data) { return request({ headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, url: '/user/login', method: 'post', data: Qs.stringify(data) }) }
export function logout() {
return request({
url: '/user/logout',
method: 'post'
})
}
后端: package cn.example.project.config; import cn.example.project.config.sec.*; import cn.example.project.module.base.Message; import cn.example.project.module.rbac.Resource; import cn.example.project.module.rbac.ResourceDB; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; import java.util.UUID; @EnableWebSecurity @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired SecurityUserService securityUserService; // @Autowired DynamicallyUrlInterceptor interceptor; @Autowired UnauthorizedEntryPoint unauthorizedEntryPoint; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(securityUserService).passwordEncoder(new BCryptPasswordEncoder()); } /** * https://blog.csdn.net/sinat_33151213/article/details/89931819 * @param web * @throws Exception */ @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/index.html", "/static/**", "/login_p", "/favicon.ico"); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() /*.antMatchers(HttpMethod.OPTIONS,"/**").permitAll()*/ .antMatchers("/index.html", "/static/**","/static/index.html", "/login_p", "/favicon.ico").permitAll() //"/login"不进行权限验证 .anyRequest().authenticated() //其他的需要登陆后才能访问 .and() .formLogin() //loginProcessingUrl用于指定前后端分离的时候调用后台登录接口的名称 .loginProcessingUrl("/user/login") .usernameParameter("username").passwordParameter("password") .successHandler(new AuthenticationSuccessHandler() { //配置登录成功的自定义处理类 @Override public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication auth) throws IOException { Message message = new Message("success", "登录成功!", PermissionUtil.getCurrentUser()); //提供给前端 设置cookie message.setToken(UUID.randomUUID().toString()); PermissionUtil.handle(req,resp, message); } }).failureHandler(new AuthenticationFailureHandler() { @Override public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException { Message message = null; if (e instanceof BadCredentialsException || e instanceof UsernameNotFoundException) { message = new Message("error", "账户名或者密码输入错误!", e.getMessage()); } else { message = new Message("error", "登录失败!", e.getMessage()); } PermissionUtil.handle(req,resp, message); } }) .and() .logout().logoutUrl("/user/logout")//logoutUrl用于指定前后端分离的时候调用后台注销接口的名称 .logoutSuccessHandler(new LogoutSuccessHandler() { @Override public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException { Message message = new Message("success", "注销成功!", null); PermissionUtil.handle(req,resp, message); } }) .and().exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint)// 配置没有权限的自定义处理类 .and() .cors().disable().csrf().disable(); // 取消跨站请求伪造防护 http.addFilterBefore(interceptor, FilterSecurityInterceptor.class); } } package cn.example.project.config; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import org.springframework.web.servlet.config.annotation.*; /** * 配置允许跨域 localhost:9900/aa 登录 localhost:9900/bb 也认可登录 */ @Configuration public class WebMvcConfg implements WebMvcConfigurer { /** * 配置静态资源 * https://blog.csdn.net/sinat_33151213/article/details/89931819 * @param registry */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); registry.addResourceHandler("/public/**").addResourceLocations("classpath:/public/"); } // @Override // public void addCorsMappings(CorsRegistry registry) { // registry.addMapping("/**") // .allowCredentials(true) // .allowedHeaders("*") // .allowedOrigins("*") // .allowedMethods("*"); // // } // // private CorsConfiguration corsConfig() { // CorsConfiguration corsConfiguration = new CorsConfiguration(); // /* // * 请求常用的三种配置,*代表允许所有,当时你也可以自定义属性(比如header只能带什么,只能是post方式等等) // */ // corsConfiguration.addAllowedOrigin("*"); // corsConfiguration.addAllowedHeader("*"); // corsConfiguration.addAllowedMethod("*"); // corsConfiguration.setAllowCredentials(true); // corsConfiguration.setMaxAge(3600L); // return corsConfiguration; // } // @Bean // public CorsFilter corsFilter() { // UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); // source.registerCorsConfiguration("/**", corsConfig()); // return new CorsFilter(source); // } // @Override // public void addCorsMappings(CorsRegistry registry) { // registry.addMapping("/**") // .allowedOrigins("*") // .allowedMethods("PUT", "DELETE","GET","POST") // .allowedHeaders("*") // .exposedHeaders("access-control-allow-headers", // "access-control-allow-methods", // "access-control-allow-origin", // "access-control-max-age", // "X-Frame-Options") // .allowCredentials(true).maxAge(36000); // // } // // @Bean // public CorsFilter corsFilter() { // final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); // final CorsConfiguration config = new CorsConfiguration(); // config.setAllowCredentials(true); // config.addAllowedOrigin("*"); // config.addAllowedHeader("*"); // config.addAllowedMethod("OPTIONS"); // config.addAllowedMethod("HEAD"); // config.addAllowedMethod("GET"); // config.addAllowedMethod("PUT"); // config.addAllowedMethod("POST"); // config.addAllowedMethod("DELETE"); // config.addAllowedMethod("PATCH"); // source.registerCorsConfiguration("/**", config); // final CorsFilter bean = new CorsFilter(source); // return bean; // } //------------加了拦截器,所以需要用如下的cors 才生效。----------------------------- /** * * @param registry */ @Override public void addCorsMappings(CorsRegistry registry) { //设置允许跨域的路径 registry.addMapping("/**") //设置允许跨域请求的域名 .allowedOrigins("*") //是否允许证书 不再默认开启 .allowCredentials(true) //设置允许的方法 .allowedMethods("*") //跨域允许时间 .maxAge(3600); } @Bean public FilterRegistrationBean corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); source.registerCorsConfiguration("/**", config); FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source)); bean.setOrder(0); return bean; } } 我开始请求登录:
登录成功后的cookie, http://localhost:9528/#/login 提供登录页面。 登录到 http://localhost:9600/user/login 登录成功后cookie保存到浏览器,下次请求 http://localhost:9600 后端服务,携带cookie表示登录过的用户在请求资源。
但是当我退出时:http://localhost:9600/user/logout
Access to XMLHttpRequest at 'http://localhost:9600/user/logout' from origin 'http://localhost:9528' has been blocked by CORS policy:
Request header field x-token is not allowed by Access-Control-Allow-Headers in preflight response.
找了好多种方法很痛苦,为了一句承诺:用户角色权限管理模板半天就出来了。我写了两个月了。我还没有写完。现在还在写登录。(太傻,太天真)
解决 axios 跨域时,发送 post 请求变 options 的问题 确实我发的是post ,但是变为post. 大意是说有options预请求,在发实际请求。 最后在 https://stackoverflow.com/questions/36705874/request-options-logout-doesnt-match-post-logout 找到一个回答:
我的代码修改为允许所有options请求:
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
最后logout成功