• SpringSecurity 中的 CORS实现


    1.基础知识##

    1.1 跨域###

    跨域是浏览器的一种同源安全策略,是浏览器单方面限制的,所有仅在客户端运行在浏览器中才需要考虑这个问题。
    跨域分为三种情况,协议跨域(http->https)、端口跨域、主机跨域。
    常用的解决跨域的三种方式,JSONP(只能支持GET跨域),NGINX代理转发(一般前端同学爱这么用),CORS

    1.2 CORS原理###

    因为cors是由浏览器控制的,实际就是在HEADER中新增一些字段,跨域时和服务器端进行一个的握手协议,比如下面的request部分,就是浏览器告诉服务器,我要从origin这里访问你了,要访问你的xxMethod,还会带xxHeader这个参数。你告诉我行不行吧。然后response会带一堆东西回来。告诉你,我这里只能有Access-Control-Allow-Origin这些域名可以访问,你自个儿先看看行不行,然后你的方法支持不支持,header支持不支持。。。等等东西

    比如
    request部分:

        Origin     //浏览器自己设置的,表示请求从哪个域名下发出来的
        //下面两个只有预检请求会带
        Access-Control-Request-Method    //告诉服务器我要用什么方法,服务器会根据配置做下筛选 再返回一个Access-Control-Allow-Methods告诉浏览器哪些可以用
        Access-Control-Request-Headers   //告诉服务器我要带哪些header,服务器会根据配置做下筛选 再返回一个Access-Control-Allow-Headers告诉浏览器哪些可以用
    

    response部分:

        Access-Control-Allow-Origin    //指定哪些客户端的域名允许访问这个资源
        Access-Control-Allow-Credentials  //服务器允许浏览器带cookie上来,不设这个服务器端就无法获得登录信息
        Access-Control-Max-Age   //告诉浏览器多久不需要再发出预检请求
        Access-Control-Allow-Methods //服务器支持的方法 比如POST GET之类的
        Access-Control-Allow-Headers  //需要在正式请求中加入的header值,否则正式请求会被拒绝,也是预检请求的响应中带回去的
        Access-Control-Expose-Headers   //很少用,告诉客户端哪些header可以使用
    

    1.3 CORS的三种场景###

    简单请求

    浏览器来决定请求的种类,比如不带自定义请求头信息的GET请求、HEAD请求以及Content-type为application/x-www-form-urlencoded、multipart/form-data或者text/plain的post请求,都是简单请求。
    简单请求时,浏览器的request只有一个origin字段,后端的返回值也很简单只要加一个Access-Control-Allow-Origin九可以了,浏览器拿到Access-Control-Allow-Origin的值后和自己的进行首部比对,通过就允许跨域了。

    预检请求(preflight)

    预检请求是浏览器发现不是简单请求后,封装一个OPTIONS请求到服务器,根据服务器的返回值来判断是否可以进行跨域,可以的话,后面还有一次真正带数据的请求。上面的response部分:就是预检请求后服务器端的响应

    带凭据的请求

    其实就是当预检请求的返回值中 Access-Control-Allow-Credentials 的值为TRUE,那么后续的正式请求就会携带凭据信息(cookie等)

    2.springsecurit中cors的实现##

    springboot中是通过CorsFilter来实现的,具体干活的是DefaultCorsProcessor,一会看下代码。开启方式也很简单,就是security的配置中通过HttpSecurity中的.cors()方法开启。

    DefaultCorsProcessor.java

        //干活的代码
        public boolean processRequest(@Nullable CorsConfiguration config, HttpServletRequest request,
    			HttpServletResponse response) throws IOException {
                    //回写三个vary属性
    		response.addHeader(HttpHeaders.VARY, HttpHeaders.ORIGIN);
    		response.addHeader(HttpHeaders.VARY, HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD);
    		response.addHeader(HttpHeaders.VARY, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
                    //如果不是跨域请求,直接返回true了
    		if (!CorsUtils.isCorsRequest(request)) {
    			return true;
    		}
                    //response已经有Access-Control-Allow-Origin 就是已经处理过了,就退回,兼容其他的自定义filter
    		if (response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN) != null) {
    			logger.trace("Skip: response already contains "Access-Control-Allow-Origin"");
    			return true;
    		}
                    //判断是否是预检请求
    		boolean preFlightRequest = CorsUtils.isPreFlightRequest(request);
                    //缺少cors的配置
    		if (config == null) {
                            //预检请求直接拒绝
    			if (preFlightRequest) {
    				rejectRequest(new ServletServerHttpResponse(response));
    				return false;
    			}
                            //简单请求直接通过
    			else {
    				return true;
    			}
    		}
                    //具体处理
    		return handleInternal(new ServletServerHttpRequest(request), new ServletServerHttpResponse(response), config, preFlightRequest);
    	}
    
    
        protected boolean handleInternal(ServerHttpRequest request, ServerHttpResponse response,
    			CorsConfiguration config, boolean preFlightRequest) throws IOException {
    
    		String requestOrigin = request.getHeaders().getOrigin();
    		String allowOrigin = checkOrigin(config, requestOrigin);
    		HttpHeaders responseHeaders = response.getHeaders();
                    //确保请求带了origin这个属性
    		if (allowOrigin == null) {
    			logger.debug("Reject: '" + requestOrigin + "' origin is not allowed");
    			rejectRequest(response);
    			return false;
    		}
                    //请求的method,复杂请求从Access-Control-Request-Method里面拿,简单请求直接就是request.getMethod()
    		HttpMethod requestMethod = getMethodToUse(request, preFlightRequest);
    		List<HttpMethod> allowMethods = checkMethods(config, requestMethod);
                    //确保请求的method 确保在服务器端的配置中是允许的
    		if (allowMethods == null) {
    			logger.debug("Reject: HTTP '" + requestMethod + "' is not allowed");
    			rejectRequest(response);
    			return false;
    		}
                    //和方法类似,校验下header
    		List<String> requestHeaders = getHeadersToUse(request, preFlightRequest);
    		List<String> allowHeaders = checkHeaders(config, requestHeaders);
    		if (preFlightRequest && allowHeaders == null) {
    			logger.debug("Reject: headers '" + requestHeaders + "' are not allowed");
    			rejectRequest(response);
    			return false;
    		}
                    //设置Access-Control-Allow-Origin
    		responseHeaders.setAccessControlAllowOrigin(allowOrigin);
                    //如果是预检请求,设置下Access-Control-Allow-Methods
    		if (preFlightRequest) {
    			responseHeaders.setAccessControlAllowMethods(allowMethods);
    		}
                    //如果是预检请求,并且allowHeaders不为空,设置下Access-Control-Allow-Headers
    		if (preFlightRequest && !allowHeaders.isEmpty()) {
    			responseHeaders.setAccessControlAllowHeaders(allowHeaders);
    		}
                    //设置Access-Control-Expose-Headers
    		if (!CollectionUtils.isEmpty(config.getExposedHeaders())) {
    			responseHeaders.setAccessControlExposeHeaders(config.getExposedHeaders());
    		}
                    //设置Access-Control-Allow-Credentials
    		if (Boolean.TRUE.equals(config.getAllowCredentials())) {
    			responseHeaders.setAccessControlAllowCredentials(true);
    		}
                    //设置Access-Control-Max-Age
    		if (preFlightRequest && config.getMaxAge() != null) {
    			responseHeaders.setAccessControlMaxAge(config.getMaxAge());
    		}
    		response.flush();
    		return true;
    	}
    
    

    3. springboot如何开启cors##

    3.1 整体配置###

    写一个CorsConfigurationSource的bean就可以了,CorsConfigurer里面会自动注入这个bean

        @Bean
        public CorsConfigurationSource corsConfigurationSource(){
            CorsConfiguration configuration = new CorsConfiguration();
            configuration.setAllowedOrigins(Arrays.asList("https://www.baidu.com"));
            configuration.setAllowedMethods(Arrays.asList("GET","POST"));
            configuration.setAllowCredentials(true);
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            source.registerCorsConfiguration("/**",configuration);
            return source;
        }
    
    

    3.2 具体配置###

    在controller或者具体方法上面加注释

        @CrossOrigin(origins = Constant.originalHost, maxAge = 3600,allowCredentials = "true") 就可以了
    
  • 相关阅读:
    eclispe安装tomcate没有srver解决
    反射常见方法
    让用户输入一个日期字符串,将其转换成日期格式, 格式是(yyyy/MM/dd,yyyyMMdd,yyyy-MM-dd)中的一种, 任何一种转换成功都可以; 如果所有的都无法转换,输出日期格式非法。
    List 接口中ArrayList Vector LinkedList 比较
    抽象类
    js核心知识
    JQgrid学习网站
    小练习---递归求5!
    map()
    小练习---阶乘
  • 原文地址:https://www.cnblogs.com/june777/p/11996130.html
Copyright © 2020-2023  润新知