ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Day244.Springsecurity解决跨域问题、CSRF跨域攻击防护、JWT集群应用方案 -springsecurity-jwt-oauth2

2021-04-11 22:03:09  阅读:285  来源: 互联网

标签:oauth2 应用 Day244 JWT token CSRF 请求 跨域


1.解决跨域访问的问题

一、CORS简述

要说明CORS(Cross Origin Resourse-Sharing) 跨站资源共享,就必须先说同源策略。长话短说,同源策略就是向服务端发起请求的时候,以下三项必须与当前浏览器应用一致:域名、端口、协议。用白话说:就是你的应用发送请求不能访问别人的资源,否则浏览器就会限制你。

当然也有例外,如:img、srcipt、iframe等资源引用的HTML标签不受同源策略的限制。

image-20210411202354790

但是我们实际开发中又经常会跨站访问,比如前后端分离的应用是分开部署的,在浏览器看来是两个域。所以同源策略是用来禁止跨域访问的,CORS正好相反是根据自己的需求与规则,有限的开放部分资源的共享。

二、Spring-CORS规则基础配置

想在Spring或Spring Boot的web环境下实现跨域资源共享,主要有三种实现方式:

  • @CrossOrigin注解,这个注解是作用于Controller类或者请求方法上的,实现局部接口的跨域资源共享。
  • 实现WebMvcConfigurer接口addCorsMappings方法,实现全局配置的跨域资源共享。
  • 注入CorsFilter过滤器,实现全局配置的跨域资源共享。推荐使用。

这三种实现方式在我的另外一篇文章《SpringBoot解决跨域访问的问题》中已经介绍过,这里就不多做说明了。

三、Spring Security 中的配置CORS

当我们的应用使用了Spring Security之后,我们会发现上面的配置方法全部失效。此时需要在spring security的WebSecurityConfigurerAdapter中的configure(HttpSecurity http)配置方法,加上http.cors()配置,第二小节中的配置才会生效。

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and()
        ...
    }
}

另外Spring Security为我们提供了一种新的CORS规则的配置方法:CorsConfigurationSource 。使用这种方法实现的效果等同于注入一个CorsFilter过滤器。

@Bean
CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(Arrays.asList("http://localhost:8888"));
    configuration.setAllowedMethods(Arrays.asList("GET","POST"));
    configuration.applyPermitDefaultValues();
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}

四、深入了解

Spring 里那么多种 CORS 的配置方式,到底有什么区别

五、前端测试代码

请将如下代码,放入与jwt-server不同域的应用下面,进行测试。

  • jwt-server的端口这里是8889
  • 使用jwt令牌进行访问
window.onload = function () {
    var headers = {};
    headers['JWTHeaderName'] = "<这里替换为jwt令牌>";
    $.ajax({
        url: 'http://localhost:8889/hello',
        type: "POST",
        headers: headers,
        success: function (data) {
            alert("跨域请求配置成功")
        },
        error: function (data) {
            alert("跨域请求配置失败")
        }
    });
}

2.CSRF跨站攻击防护

一、什么是CSRF

他只防御POST/DELETE/PUT的请求方式,针对GET请求不防御

很多朋友在学习Spring Security的时候,会将CORS(跨站资源共享)和CSRF(跨站请求伪造)弄混,以为二者是一回事。其实不是,先解释一下:

  • CORS(跨站资源共享)是局部打破同源策略的限制,使在一定规则下HTTP请求可以突破浏览器限制,实现跨站访问。
  • CSRF是一种网络攻击方式,也可以说是一种安全漏洞,这种安全漏洞在web开发中广泛存在。我们要需要堵上这个漏洞。

当我们使用Spring Security的时候,这种CSRF漏洞默认的被防御掉了。但是你会发现在跨域请求的情况下,我们的POST、DELETE、PUT等HTTP请求方式失效了。所以在笔者之前的文章中,我们使用http.csrf.disable()暂时关闭掉了CSRF的防御功能,但是这样是不安全的,那么怎么样才是正确的做法呢?就是本文需要向大家介绍的内容。


二、CSRF的攻击方式

通常的CSRF攻击方式如下:

  • 你登录了网站A,攻击者向你的网站A账户发送留言、或者伪造嵌入页面,带有危险操作链接。
  • 当你在登录状态下点击了攻击者的连接,因此该链接对你网站A的账户进行了操作。
  • 这个操作是你在网站A中主动发出的,并且也是针对网站A的HTTP链接请求,同源策略无法限制该请求。

如果你不小心点击的连接,是针对网站的数据操作,如:转出货币,你的钱就被转走了。因为点击"链接"的请求是HTTP的GET请求,所以正规的开发人员的做法是不要使用GET方法进行数据操作,只使用GET方法进行数据查询。

三、如何防御CSRF攻击

  • 为系统中的每一个连接请求加上一个token,这个token是随机的,服务端对该token进行验证。破坏者在留言或者伪造嵌入页面的时候,无法预先判断CSRF token的值是什么,所以当服务端校验CSRF token的时候也就无法通过。所以这种方法在一定程度上是靠谱的。
  • 但是如果你的电脑中毒,网络信息被劫持使用token的方法仍然不安全。所以没有绝对的安全,道高一次魔高一丈。作为开发者,我们就做到我们应该做到的。
  • 跳转提示:当用户不小心点击了第三方连接,合格的应用应该提示用户相关的风险!由用户自己确认是否真的要跳转或者执行第三方连接,或者就干脆不让非可信连接在留言区等地方存在。

四、Spring Security的CSRF token攻击防护

首先,我们要先开启防护功能,在用户登陆操作之后,生成的CSRF Token就保存在cookies中。

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //开启csrf防御
        http.csrf()
            //使用cookie去存csrf令牌,允许浏览器当前cookie可以被js脚本读取
            .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
			//忽略登入认证的请求,/authentication 登录认证的请求
            .ignoringAntMatchers("/authentication");
        .and()
        ...
    }
}
  • 使用CookieCsrfTokenRepository生成CSRF Token放入cookie,并设置cookie的HttpOnly=false,允许读取该cookie。这样非浏览器等无法自动维护cookie的客户端可以读取cookie中的CSRF Token,以供后续资源请求中使用。
  • 使用ignoringAntMatchers开放一些不需要进行CSRF防护的访问路径,比如:登录授权。

至此,我们生成了CSRF token保存在了cookies中,浏览器向服务端发送的HTTP请求,都要将CSRF token带上,服务端校验通过才能正确的响应。这个校验的过程并不需要我们自己写代码实现,Spring Security会自动处理。但是我们需要关注前端代码,如何正确的携带CSRF token。


五、前端请求携带CSRF Token的方式

  • 默认情况下,CookieCsrfTokenRepository会向cookies中写入一个key为XSRF-TOKEN的cookie。
  • CookieCsrfTokenRepository在跨站防御验证的过程中,可以从HTTP Header中读取 X-XSRF-TOKEN或者从HTTP参数中读取_csrf,作为跨站防御验证的令牌.

注意:这里是XSRF-TOKENX-XSRF-TOKEN,没有写错。而不是CSRF-TOKENX-CSRF-TOKEN

在thymeleaf模板中可以使用如下方式,在发送HTTP请求的时候携带CSRF Token。如果是前后端分离的应用,或者其他模板引擎,酌情从cookies中获取CSRF Toekn。

5.1.在Header中携带CSRF token

var headers = {};
headers['X-XSRF-TOKEN'] = "${_csrf.token}";
$.ajax({    
    headers: headers,    
});

5.2.直接作为参数提交。

$.ajax({    
    data: {      
       "_csrf": "${_csrf.token}"        
    }
});

5.3.form表单的隐藏字段

<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}">

  • 请求授权认证返回XSRF-TOKEN

image-20210411212841694

  • 再次请求的时候携带时,X-XSRF-TOKEN等于上面的值

image-20210411213100448


3.JWT集群应用方案

一、回顾JWT授权与验证流程

在我们之前实现的JWT应用中,登录认证的Controller和令牌验证的Filter是在同一个应用中的。

image-20210411215023998

要想使用JWT访问资源需要

  • 先使用用户名和密码,去Controller换取JWT令牌
  • 然后才能进行资源的访问,资源接口的前端由一个"JWT验证Filter"负责校验令牌和授权访问。

二、集群应用

那我们可以思考一个问题,如果上面的应用部署两份形成集群应用,也就是“应用A”和“应用B”,代码是同一套代码。如果认证过程是在“应用A”获取的JWT令牌,可以访问“应用B”的接口资源么?(如下图)

image-20210411215037041

答案是:可以。因为两个应用中没有在内存(session)中保存中保存任何的状态信息,所有的信息都是去数据库里面现加载的。所以只要这两个应用,使用同一个数据库、同一套授权数据、同一个用于签名和解签的secret。就可以实现“应用A”的认证、在“应用B”中被承认。
那么另外一个问题来了,对于上面的集群应用,“应用A”和“应用B”实际上是一份代码部署两份。如果“应用A”和“应用B”是真正的两套代码的部署结果呢?答案仍然是可以。前提是你的认证Controller代码和鉴权Filter代码的实现逻辑是一样的、校验规则是一样的。使用同一个数据库、同一套授权数据、同一个用于签名和解签的secret。所以JWT服务端应用可以很容易的扩展。

  • 条件要求

image-20210411215107562

满足以上就可以在A服务器上鉴权授权返回jwt令牌,然后去携带jwt令牌,去B服务器请求访问,通过授权

三、独立的授权服务

基于JWT的这种无状态的灵活性,它很容易实现应用横向扩展。只要具备以下条件任何JWT的应用都可以整合为一个应用集群。

  • 认证Controller代码统一
  • 鉴权Filter代码统一、校验规则是一样的。
  • 使用同一套授权数据
  • 同一个用于签名和解签的secret。

基于这个条件前提,我们完全可以把认证Controller代码单独抽取出来,形成“认证服务器”。如下图所示:

image-20210411215048905

或者我们还可以进一步把所有的Jwt验证鉴权Filter代码单独抽取出来,形成“服务网关”,放在接口资源的前端。当然“服务网关”的功能不只是鉴权、还要有请求转发的功能。

最后剩下的一系列的“接口资源”,实际上就是我们常说的“资源服务器”。


标签:oauth2,应用,Day244,JWT,token,CSRF,请求,跨域
来源: https://blog.csdn.net/qq_43284469/article/details/115607674

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有