经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring Boot » 查看文章
Security框架:如何使用CorsFilter解决前端跨域请求问题
来源:jb51  时间:2021/11/16 11:12:21  对本文有异议

项目情况

最近做的pmdb项目是前后端分离的, 由于测试的时候是前端与后端联调,所以出现了跨域请求的问题。

浏览器默认会向后端发送一个Options方式的请求,根据后端的响应来判断后端支持哪些请求方式,支持才会真正的发送请求。

CORS介绍

CORS(Cross-Origin Resource Sharing 跨源资源共享),当一个请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。

在日常的项目开发时会不可避免的需要进行跨域操作,而在实际进行跨域请求时,经常会遇到类似 No 'Access-Control-Allow-Origin' header is present on the requested resource.这样的报错。

这样的错误,一般是由于CORS跨域验证机制设置不正确导致的。

解决方案

注释:本项目使用的是SprintBoot+Security+JWT+Swagger

第一步

新建CorsFilter,在过滤器中设置相关请求头

  1. package com.handlecar.basf_pmdb_service.filter;
  2. import org.springframework.web.filter.OncePerRequestFilter;
  3. import javax.servlet.*;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. public class CorsFilter extends OncePerRequestFilter {
  8. //public class CorsFilter implements Filter {
  9. // static final String ORIGIN = "Origin";
  10. protected void doFilterInternal(
  11. HttpServletRequest request, HttpServletResponse response,
  12. FilterChain filterChain) throws ServletException, IOException {
  13. // String origin = request.getHeader(ORIGIN);
  14. response.setHeader("Access-Control-Allow-Origin", "*");//* or origin as u prefer
  15. response.setHeader("Access-Control-Allow-Credentials", "true");
  16. response.setHeader("Access-Control-Allow-Methods", "PUT, POST, GET, OPTIONS, DELETE");
  17. response.setHeader("Access-Control-Max-Age", "3600");
  18. // response.setHeader("Access-Control-Allow-Headers", "content-type, authorization");
  19. response.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With, Authorization");
  20. response.setHeader("XDomainRequestAllowed","1");
  21. //使前端能够获取到
  22. response.setHeader("Access-Control-Expose-Headers","download-status,download-filename,download-message");
  23. if (request.getMethod().equals("OPTIONS"))
  24. // response.setStatus(HttpServletResponse.SC_OK);
  25. response.setStatus(HttpServletResponse.SC_NO_CONTENT);
  26. else
  27. filterChain.doFilter(request, response);
  28. }
  29. // @Override
  30. // public void doFilter(ServletRequest req, ServletResponse res,
  31. // FilterChain chain) throws IOException, ServletException {
  32. //
  33. // HttpServletResponse response = (HttpServletResponse) res;
  34. // //测试环境用【*】匹配,上生产环境后需要切换为实际的前端请求地址
  35. // response.setHeader("Access-Control-Allow-Origin", "*");
  36. // response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
  37. //
  38. // response.setHeader("Access-Control-Max-Age", "0");
  39. //
  40. // response.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With, auth");
  41. //
  42. // response.setHeader("Access-Control-Allow-Credentials", "true");
  43. //
  44. // response.setHeader("XDomainRequestAllowed","1");
  45. // chain.doFilter(req, res);
  46. // }
  47. //
  48. // @Override
  49. // public void destroy() {
  50. // }
  51. //
  52. // @Override
  53. // public void init(FilterConfig arg0) throws ServletException {
  54. // }
  55. }

注释:这里的Access-Control-Expose-Headers的请求头是为了使前端能够获得到后端在response中自定义的header,不设置的话,前端只能看到几个默认显示的header。我这里是在使用response导出Excel的时候将文件名和下载状态信息以自定义请求头的形式放在了response的header里。

第二步

在Security的配置文件中初始化CorsFilter的Bean

  1. @Bean
  2. public CorsFilter corsFilter() throws Exception {
  3. return new CorsFilter();
  4. }

第三步

在Security的配置文件中添加Filter配置,和映射配置

  1. .antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
  2. // 除上面外的所有请求全部需要鉴权认证。 .and() 相当于标示一个标签的结束,之前相当于都是一个标签项下的内容
  3. .anyRequest().authenticated().and()
  4. .addFilterBefore(corsFilter(), UsernamePasswordAuthenticationFilter.class)

附:该配置文件

  1. package com.handlecar.basf_pmdb_service.conf;
  2. import com.handlecar.basf_pmdb_service.filter.CorsFilter;
  3. import com.handlecar.basf_pmdb_service.filter.JwtAuthenticationTokenFilter;
  4. import com.handlecar.basf_pmdb_service.security.JwtTokenUtil;
  5. import com.handlecar.basf_pmdb_service.security.CustomAuthenticationProvider;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.context.annotation.Configuration;
  9. import org.springframework.http.HttpMethod;
  10. import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
  11. import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
  12. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  13. import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
  14. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  15. import org.springframework.security.config.http.SessionCreationPolicy;
  16. import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
  17. //import com.allcom.security.JwtTokenUtil;
  18. @Configuration
  19. //@EnableWebSecurity is used to enable Spring Security's web security support and provide the Spring MVC integration
  20. @EnableWebSecurity
  21. @EnableGlobalMethodSecurity(prePostEnabled = true)
  22. public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  23. private final CustomAuthenticationProvider customAuthenticationProvider;
  24. @Autowired
  25. public WebSecurityConfig(CustomAuthenticationProvider customAuthenticationProvider) {
  26. this.customAuthenticationProvider = customAuthenticationProvider;
  27. }
  28. @Override
  29. protected void configure(AuthenticationManagerBuilder auth) {
  30. auth.authenticationProvider(customAuthenticationProvider);
  31. }
  32. @Bean
  33. public JwtTokenUtil jwtTokenUtil(){
  34. return new JwtTokenUtil();
  35. }
  36. @Bean
  37. public CorsFilter corsFilter() throws Exception {
  38. return new CorsFilter();
  39. }
  40. @Bean
  41. public JwtAuthenticationTokenFilter authenticationTokenFilterBean() {
  42. return new JwtAuthenticationTokenFilter();
  43. }
  44. @Override
  45. protected void configure(HttpSecurity httpSecurity) throws Exception {
  46. httpSecurity
  47. // 由于使用的是JWT,我们这里不需要csrf,不用担心csrf攻击
  48. .csrf().disable()
  49. // 基于token,所以不需要session
  50. .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
  51. .authorizeRequests()
  52. //.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
  53. // 允许对于网站静态资源的无授权访问
  54. .antMatchers(
  55. HttpMethod.GET,
  56. "/",
  57. "/*.html",
  58. "/favicon.ico",
  59. "/**/*.html",
  60. "/**/*.css",
  61. "/**/*.js",
  62. "/webjars/springfox-swagger-ui/images/**","/swagger-resources/configuration/*","/swagger-resources",//swagger请求
  63. "/v2/api-docs"
  64. ).permitAll()
  65. // 对于获取token的rest api要允许匿名访问
  66. .antMatchers("/pmdbservice/auth/**","/pmdbservice/keywords/export3").permitAll()
  67. .antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
  68. // 除上面外的所有请求全部需要鉴权认证。 .and() 相当于标示一个标签的结束,之前相当于都是一个标签项下的内容
  69. .anyRequest().authenticated().and()
  70. .addFilterBefore(corsFilter(), UsernamePasswordAuthenticationFilter.class)
  71. .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
  72. // 禁用缓存
  73. httpSecurity.headers().cacheControl();
  74. }
  75. }

以上为个人经验,希望能给大家一个参考,也希望大家多多支持w3xue。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号