温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

怎么利用spring-security解决CSRF问题

发布时间:2021-06-17 14:58:53 来源:亿速云 阅读:341 作者:小新 栏目:编程语言

这篇文章主要介绍了怎么利用spring-security解决CSRF问题,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

CSRF介绍

CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

配置步骤

1.依赖jar包

<properties>      <spring.security.version>4.2.2.RELEASE</spring.security.version>    </properties>  <dependency>          <groupId>org.springframework.security</groupId>          <artifactId>spring-security-core</artifactId>          <version>${spring.security.version}</version>        </dependency>          <dependency>          <groupId>org.springframework.security</groupId>          <artifactId>spring-security-web</artifactId>          <version>${spring.security.version}</version>        </dependency>          <dependency>          <groupId>org.springframework.security</groupId>          <artifactId>spring-security-config</artifactId>          <version>${spring.security.version}</version>        </dependency>

2.web.xml配置

<filter>      <filter-name>springSecurityFilterChain</filter-name>      <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>    </filter>      <filter-mapping>      <filter-name>springSecurityFilterChain</filter-name>      <url-pattern>/*</url-pattern>    </filter-mapping>

3.Spring配置文件配置

<bean id="csrfSecurityRequestMatcher" class="com.xxx.CsrfSecurityRequestMatcher"></bean>      <security:http auto-config="true" use-expressions="true">      <security:headers>        <security:frame-options disabled="true"/>      </security:headers>      <security:csrf request-matcher-ref="csrfSecurityRequestMatcher" />    </security:http>

4.自定义RequestMatcher的实现类CsrfSecurityRequestMatcher

这个类被用来自定义哪些请求是不需要进行拦截过滤的。如果配置csrf,所有http请求都被会CsrfFilter拦截,而CsrfFilter中有一个私有类DefaultRequiresCsrfMatcher。

源码1:DefaultRequiresCsrfMatcher类

private static final class DefaultRequiresCsrfMatcher implements RequestMatcher {      private final HashSet<String> allowedMethods;        private DefaultRequiresCsrfMatcher() {        this.allowedMethods = new HashSet(Arrays.asList(new String[]{"GET", "HEAD", "TRACE", "OPTIONS"}));      }        public boolean matches(HttpServletRequest request) {        return !this.allowedMethods.contains(request.getMethod());      }    }

从这段源码可以发现,POST方法被排除在外了,也就是说只有GET|HEAD|TRACE|OPTIONS这4类方法会被放行,其它Method的http请求,都要验证_csrf的token是否正确,而通常post方式调用rest接口服务时,又没有_csrf的token,所以会导致我们的rest接口调用失败,我们需要自定义一个类对该类型接口进行放行。来看下我们自定义的过滤器:

源码2:csrfSecurityRequestMatcher类

public class CsrfSecurityRequestMatcher implements RequestMatcher {    private Pattern allowedMethods = Pattern.compile("^(GET|HEAD|TRACE|OPTIONS)$");    private RegexRequestMatcher unprotectedMatcher = new RegexRequestMatcher("^/rest/.*", null);      @Override    public boolean matches(HttpServletRequest request) {      if(allowedMethods.matcher(request.getMethod()).matches()){        return false;      }        return !unprotectedMatcher.matches(request);    }  }

说明:一般我们定义的rest接口服务,都带上 /rest/ ,所以如果你的项目中不是使用的这种,或者项目中没有rest服务,这个类完全可以省略的。

5.post请求配置

一般我们的项目中都有一个通用的jsp文件,就是每个页面都会引用的,所以我们可以在通用文件中做如下配置:

<meta name="_csrf" content="${_csrf.token}"/>  <meta name="_csrf_header" content="${_csrf.headerName}"/>    <script>      var token = $("meta[name='_csrf']").attr("content");    var header = $("meta[name='_csrf_header']").attr("content");    $.ajaxSetup({      beforeSend: function (xhr) {        if(header && token ){          xhr.setRequestHeader(header, token);        }      }}    );  </script>

$.ajaxSetup的意思就是给我们所有的请求都加上这个header和token,或者放到form表单中。注意,_csrf这个要与spring security的配置文件中的配置相匹配,默认为_csrf。

源码解析

我们知道,既然配置了csrf,所有的http请求都会被CsrfFilter拦截到,所以看下CsrfFilter的源码就对原理一目了然了。这里我们只看具体过滤的方法即可:

源码3:CsrfFilter的doFilterInternal方法

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {      request.setAttribute(HttpServletResponse.class.getName(), response);      CsrfToken csrfToken = this.tokenRepository.loadToken(request);      boolean missingToken = csrfToken == null;      if(missingToken) {//如果token为空,说明第一次访问,生成一个token对象        csrfToken = this.tokenRepository.generateToken(request);        this.tokenRepository.saveToken(csrfToken, request, response);      }        request.setAttribute(CsrfToken.class.getName(), csrfToken);      //把token对象放到request中,注意这里key是csrfToken.getParameterName()= _csrf,所以我们页面上才那么写死。      request.setAttribute(csrfToken.getParameterName(), csrfToken);            //这个macher就是我们在Spring配置文件中自定义的过滤器,也就是GET,HEAD, TRACE, OPTIONS和我们的rest都不处理      if(!this.requireCsrfProtectionMatcher.matches(request)) {        filterChain.doFilter(request, response);      } else {        String actualToken = request.getHeader(csrfToken.getHeaderName());        if(actualToken == null) {          actualToken = request.getParameter(csrfToken.getParameterName());        }          if(!csrfToken.getToken().equals(actualToken)) {          if(this.logger.isDebugEnabled()) {            this.logger.debug("Invalid CSRF token found for " + UrlUtils.buildFullRequestUrl(request));          }            if(missingToken) {            this.accessDeniedHandler.handle(request, response, new MissingCsrfTokenException(actualToken));          } else {            this.accessDeniedHandler.handle(request, response, new InvalidCsrfTokenException(csrfToken, actualToken));          }          } else {          filterChain.doFilter(request, response);        }      }    }

从源码中可以看到,通过我们自定义的过滤器以外的post请求都需要进行token验证。

本来呢,是想截图弄个案例上去的,然后通过断点看看页面和后台的传值情况....不过,我这里没法上传图片抓狂。好吧,就总结这么多吧!如果有写的不对的或者有其他问题可以留言交流。

感谢你能够认真阅读完这篇文章,希望小编分享的“怎么利用spring-security解决CSRF问题”这篇文章对大家有帮助,同时也希望大家多多支持亿速云,关注亿速云行业资讯频道,更多相关知识等着你来学习!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI