Spring Boot | Spring Boot “CSRF 防护功能“ 、Security “管理前端页面“

在这里插入图片描述

作者简介 :一只大皮卡丘,计算机专业学生,正在努力学习、努力敲代码中! 让我们一起继续努力学习!

该文章参考学习教材为:
《Spring Boot企业级开发教程》 黑马程序员 / 编著
文章以课本知识点 + 代码为主线,结合自己看书学习过程中的理解和感悟 ,最终成就了该文章

文章用于本人学习使用 , 同时希望能帮助大家。
欢迎大家点赞👍 收藏⭐ 关注💖哦!!!

(侵权可联系我,进行删除,如果雷同,纯属巧合)


一、SpringBoot 中 自定义 “用户授权管理” ( 总体内容介绍 )

  • 一个系统建立之后,通常需要适当地做一些权限控制,使得 不同用户具有不同的权限 操作系统
    例如一般的项目中都会做一些简单的登录控制,只有特定用户才能登录访问。接下来将 针对 Web 应用中常见自定义用户授权管理 进行 介绍

  • SpringBoot自定义 “用户授权管理”实现方式 :
    创建类 继承(extens) WebSecurityConfigurerAdapter类

    重写 WebSecurityConfigurerAdapter类 中的 configure( HttpSecurity http )方法

    通过 HttpSecurity类中的 Xxx方法 来 实现自定义 “用户授权管理”

    ( 通过 configure( HttpSecurity http ) 方法 中的 HttpSecurity 类 实现/进行 “用户授权管理” )

  • HttpSecurity类主要方法说明 ( 通过 该类 中的方法 来 实现 "用户授权管理"):

方法描述
authorizeRequests( ) :
授权请求
开启基于 “HttpServletRequest” 请求访问限制
ps :
用于实现 “自定义用户访问控制
( 通过configure( HttpSecurity http)方法 中的 HttpSecurity类authorizeRequests( )方法实现 “自定义用户访问控制”其他方法则是以此类推。)
formLogin( )开启基于表单用户登录
ps :
用于实现 “自定义用户登录页面
使用该方法就是 使用 security提供 的" 默认登录"页面 进行" 登录验证" ( 如果没有指定 "自定义的登录页面" 的话 )
httpBasic( )开启基于 HTTP 请求Basic 认证登录
logout( )开启退出登录支持
sessionManagement( )开启 Session 管理配置
rememberMe( )开启 记住我 功能
csrf( )配置 “CSRF” 跨站请求伪造防护功能

补充 :
configure ( HttpSecurity http )方法参数类型HttpSecurity 类HttpSecurity 类提供了 Http请求的限制权限Session 管理配置CSRF跨站请求问题等方法

二、实现 “CSRF” 防护功能 ( 通过 “HttpSecurity类” 的 csrf( )方法来实现 “CSRF” 功能 ) :

  • CSRF ( Cross-site request forgery跨站请求伪造 ),也被称为“One Click Attack”( 一键攻击 )或者 “Session Riding”( 会话控制 ),通常 缩写CSRF 或者 XSRF,是一种 对网站的恶意利用

  • 传统XSS 攻击( Cross-site Scripting跨站脚本攻击) 相比CSRF 攻击更加难以防范,被认为比 XSS 更具危险性CSRF 攻击可以在 受害者毫不知情 的情况下以受害者的名义 伪造请求发送攻击页面,从而 在用户未授权的情况 下 执行在权限保护之下操作

    例如,一个用户 Tom 登录银行站点服务器准备进行转账操作,在 此用户信息有效期内Tom诱导查看了一个 黑客恶意网站,该网站就会 获取Tom 登录后浏览器与银行网站之间未过期的 Session 信息,而 Tom 浏览器Cookie 中含有 Tom 银行账户认证信息,此时黑客就会伪装成 Tom 认证后合法用户银行账户进行非法操作

  • 在讨论 如何抵御 CSRF 攻击 之前,先要 明确 CSRF 攻击对象,也就是 要保护的对象。从上面的例子可知,CSRF 攻击黑客 借助 受害者Cookie 骗取服务器信任但是黑客并不能获取 Cookie,也看不到 Cookie具体内容。另外,对于服务器返回结果由于浏览器同源策略的限制,黑客无法进行解析

    黑客所能做的就是伪造正常用户给服务器发送请求,以执行请求中所描述的命令在服务器端直接改变数据,而 非窃取服务器中的数据。因此,针对 CSRF 攻击要保护对象是那些可以直接产生数据变化服务,而对于读取数据的服务可以不进行CSRF 保护。例如 ,银行转账操作改变账号金额需要进行 CSRF 保护。获取银行卡等级信息是 读取操作不会改变数据可以不需要保护

  • 业界目前防御 CSRF 攻击 ( 防御 “跨站请求伪造” )主要有 以下3种策略
    (1) 验证 HTTP Referer 字段
    (2)请求地址添加 Token 并验证
    (3)HTTP 头自定义属性并验证

  • Spring Security 安全框架提供了 CSRF 防御相关方法具体如下表所示 :

    CSRF防御相关的 **主要方法**及 说明
    disable( )方法“关闭” Security 默认开启CSRF 防御功能
    csrfTokenRepository( CsrfTokenRepositor csrfTokenRepository )方法指定 要使用CsrfTokenRepository ( Token 令牌持久化仓库 )。默认 是由 LazyCsrfTokenRepository 包装HttoSessionCsrfTokenRepository
    requireCsrfProtectionMatcher( RequestMatcher requireCsrfProtectionMatcher )方法指定 针对什么类型请求应用 CSRF 防护功能默认设置忽略 GETHEADTRACEOPTIONS 请求,而 处理并防御 其他所有请求

    接下来我们 结合上表中的方法Spring Boot 中的 CSRF 防护功能进行说明 :

1. “关闭” CSRF 防护功能 :

  • Spring Boot 整合 Spring Security 默认开启CSRF 防御功能,并要求 "数据修改"请求方法 ( 例如 PATCHPOSTPUTDELETE ) 都 需要经过 Security 配置安全认证后 方可正常访问否则无法正常发送请求 。为了演示 SecurityCSRF 实际默认防护效果编写一个页面 进行 演示说明
① 创建好 “基本的项目”
② 创建数据修改页面
  • 打开项目 resources/templates 目录,在该目录下创建一个名为 csrf文件夹,在该文件夹中编写一个模拟修改用户账号信息Thymeleaf 页面 : csrfTest.html 用来 进行 CSRF 测试代码文件如下所示

    csrfTest.html :

    <!DOCTYPE html>
    <html lang="en" xmlns="http://www.w3.org/1999/html">
    <head>
        <meta charset="UTF-8">
        <title>用户修改</title>
    </head>
    <body>
    <!-- 该修改页面将会被Spring Security框架拦截(因为安全框架默认启用了CSRF 安全防护功能)-->
    <!--  该修改页面提交的请求也会被拦截,因为其没有携带Token令牌,Spring Security默认的不安全的请求,将其进行拦截,且后台也不会做出响应-->
    <div align="center">
        <form method="post" action="/updateUser">
            用户名: <input type="text" name="username"/> </br>&nbsp;&nbsp;<input type="password" name="password"/> </br>
            <button type="submit">修改</button>
        </form>
    </div>
    </body>
    </html>
    
③ 编写后台控制层方法
  • 项目controller包 下,创建一个用于 CSRF 页面请求测试控制类 : CSRFController代码文件如下 :

    CSRFController.java :

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import javax.servlet.http.HttpServletRequest;
    
    @Controller
    public class CSRFController { //关于 CSRF页面请求测试的控制层类
    
        /*
          向用户修改页面跳转
         */
        @GetMapping("/toUpdate")
        public String toUpdate() { //返回值为String类型,可用于返回一个视图页面
            return "csrf/csrfTest";
        }
    
        /*
          用户修改处理
         */
        @ResponseBody //将返回值转换为"指定类型"并写入/存入响应体中(响应给前端) , 所以此处返回的不再是是"视图页面"了
        @PostMapping(value = "/updateUser")
        //使用@RequestParam()注解来绑定"前后端参数" -- 该注解能解决"前后端参数名不一致"的问题
        public String updateUser(@RequestParam String username, @RequestParam String password, HttpServletRequest request) {
            System.out.println(username);
            System.out.println(password);
            String csrf_token = request.getParameter("_csrf");
            System.out.println(csrf_token);
            return "ok";
        }
    
    }
    
④ CSRF 默认防护效果测试
  • 重启项目,通过浏览器访问 http://localhost8080/toUpdate用户修改页面,由于前面配置请求拦截 ,会先 被拦截跳转用户登录页面
    用户登录页面输入正确的用户信息后
    ,就会自动跳转到用户修改页面该页面如下图所示 :

    在这里插入图片描述

    在上图所示的用户修改页面中,随意输入修改后用户名密码 ,单击【修改】按钮进行 数据提交,效果如下图所示 :

    在这里插入图片描述

    上图可以看出,在 代码业务逻辑没有错误情况下表单正确提交 POST请求数据被拦截,出现了 403Forbidden(禁止)错误提示信息,而 后台也没有任何响应。这说明整合使用的 Spring Security 安全框架 默认启用CSRF 安全防护功能, 而上述示例 被拦截本质原因就是数据修改请求没有携带 CSRF Token(CSRF 令牌) 相关的参数信息,所以被认为是 不安全的请求

  • 通过上述示例可以看出,在整合 Spring Security 安全框架后,项目 默认启用CSRF 安全防护功能,项目中所有涉及数据修改方式请求都会被拦截如果没有携带CSRF Token信息 将不会放行 。 针对这种情况,可以 有两种处理方式 :
    一种方式直接关闭 Security 默认开启CSRF 防御功能另一种 方式就是 配置 Security需要CSRF Token

⑤ 关闭 Security 的 " CSRF 防御功能"
  • 打开配置类 : SecurityConfig , 在重写configure( HttpSecurity http )方法 中进行 “关闭配置” :

    @EnableWebSecurity  // 开启MVC security安全支持
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
    
            /**
             * 关闭 Spring Security的 CSRF防御功能
             */
            http.csrf() .disable();
        }
    
    }
    

    上述示例中展示了 关闭 CSRF 防御功能配置方式,这种 直接关闭 CSRF防御的方式简单粗暴不太推荐使用,如果 强行关闭后 网站 可能会面临 CSRF攻击 的危险。

2. 为 “数据修改” 操作提供 “CSRF Token 令牌” ( 有CSRF才能执行该"数据操作" )

  • Spring Security 针对 不同类型 的**数据修改请求提供了不同方式**的 CSRF Token 配置 ( 为 “数据修改操作 提供 "CSRF Token" ) ,主要包括有:
    针对 Form 表单数据 "修改"的 CSRF Token 配置
    针对 Ajax 数据 "修改"请求的 CSRF Token配置。下面将分别 对这两种配置方式进行 讲解

(1) 针对 “Form表单” “数据修改” 的 “CSRF Token令牌” 配置

方式一 : Form表单中 “显式配置” “CSRF Token令牌信息” ( 需"手动配置")
  • 针对 Form 表单类型数据修改请求Security 支持在 Form 表单提供一个携带 CSRFToken 信息的 隐藏域与其他修改数据一起提交,这样后台就可以 获取并验证 该请求 是否为安全 的,示例代码如下

    csrfTest.html :

    <!DOCTYPE html>
    <html lang="en" xmlns="http://www.w3.org/1999/html">
    <head>
        <meta charset="UTF-8">
        <title>用户修改</title>
    </head>
    <body>
    <!-- 该修改页面将会被Spring Security框架拦截(因为安全框架默认启用了CSRF 安全防护功能)-->
    <!--  该修改页面提交的请求也会被拦截,因为其没有携带Token令牌,Spring Security默认的不安全的请求,将其进行拦截,且后台也不会做出响应-->
    <div align="center">
        <form method="post" action="/updateUser">
            <!-- 该Form表单中提供了一个携带 CSRF Token信息的 "隐藏域",与其他数据一起提交,以让后端认定该请求是安全的 -->
            <!--
                ${_csrf.getParameterName()} :  获取 "Security默认提供" 的 "CSRF Token令牌"对应的 "key值"
                th:name="${_csrf.getParameterName()}" : 将该 CSRF Token令牌对应的key值,作为name属性的"属性值"
    
                ${_csrf.token} : 获取 "Security随机生成" 的 "CSRF Token令牌" 对应的 "value值"
                th:value="${_csrf.token} : 将给 "CSRF Token令牌值"作为value属性的"属性值"
    
                该Form表单中添加了CSRF Token令牌,后台配置的Security会自动获取并识别请求中的CSRF Token令牌信息并进行用户信息验证,
                从而判断是否是安全的
            -->
            <input type="hidden" th:name="${_csrf.getParameterName()}" th:value="${_csrf.token}">
            用户名: <input type="text" name="username"/> </br>&nbsp;&nbsp;<input type="password" name="password"/> </br>
            <button type="submit">修改</button>
        </form>
    </div>
    </body>
    </html>
    

    Form 表单添加上述 CSRF 配置后,无须其他配置就可以正常实现数据修改请求 ( 因为 给后端提供CSRF Token令牌 ),后台配置Security自动获取并识别请求中的 CSRF Token 信息并进行用户信息验证,从而判断是否安全

方式二 : 使用 “Thymeleaf 模板” 的 “th:action 属性” 配置 “CSRF Token令牌信息” ( 无需"手动配置")
  • 使用 Thymeleaf 模板th:action 属性 配置 CSRF Token 信息示例代码如下

    csrfTest.html :

    <!DOCTYPE html>
    <html lang="en" xmlns="http://www.w3.org/1999/html">
    <head>
        <meta charset="UTF-8">
        <title>用户修改</title>
    </head>
    <body>
    <div align="center">
         <!-- 
          th:action="@{/updateUser} :
          此处使用Thymeleaf的 th:action属性来 "配置请求", 其会默认会携带CSRF Token信息(给后端进行"安全验证"),这样就       无需开发者手动添加 -->
        <form method="post" th:action="@{/updateUser}">
            用户名: <input type="text" name="username"/> </br>&nbsp;&nbsp;<input type="password" name="password"/> </br>
            <button type="submit">修改</button>
        </form>
    </div>
    </body>
    </html>
    

    上述代码中,使用了 Thymeleaf 模板th:action 属性配置了 Form 表单数据修改后请求路径 ( 使用Thymeleafth:action属性配置Url请求时, 其"自动默认携带" CSRF Token令牌信息 ,这个 令牌信息url请求携带一起到后端不再需要隐藏域” 的方式来 传递CSRF Token令牌” 给后端。 ),而在 表单并没有提供携带 CSRFToken 信息隐藏域 ,但仍然可以正常地执行数据修改请求。这是因为使用 Thymeleaf 模板th:action 属性配置请求时,会默认携带 CSRF Token令牌 信息无须开发者手动添加,这也解释了在前面编写login.html 页面进行用户登录时为何可以正常执行的原因

(2) 针对 “Ajax” “数据修改” 的 “CSRF Token令牌” 配置

  • 对于 Ajax 类型数据修改请求 来说,Security 提供了通过添加 HTTP “header头” 信息的方式携带 CSRF Token令牌信息进行请求验证。( 也是 要携带 CSRF Token信息 后端才能进行 “数据修改” 操作 ) 。

  • 首先,在 页面<head>标签中添加<meta>子标签 , 并配置 CSRF Token令牌 信息示例代码如下 :

    <html>
    <head>
        <!--  Ajax的数据修改配置CSRF Token令牌信息  -->
        <!-- 获取 CSRF Token令牌  -->
        <meta name="_csrf" th:content="${_csrf.token}"/>
        <!-- 获取 CSRF头,默认为 X-CSRF-TOKEN  -->
        <meta name="_csrf_header" th:content="${_csrf.headerName}"/>
    </head>
    ......
    

    然后,在 具体的 Ajax 请求中 获取 <meta>子标签设置CSRF Token 信息绑定HTTP 请求头 中进行 请求验证示例代码如下

    $(function() {
         //获取<meta>标签中封装的 CSRF Token令牌信息
        var token =  $("meta[name='_csrf']").attr("content");
        var header = $("meta[name='_csrf_header']").attr("content");
        //将头中的 "CSRF Token令牌信息" 进行发送
        $(document).ajaxSend(function (e,xhr,options) {
            xhr.setRequestHeader(header, token);
        })
    });
    

    上述代码中,首先 获取<meta>标签 设置CSRF Token 信息,然后通过 HTTP 请求 将CSRF Token 信息后台进行验证

三、Security “管理前端页面” ( Security 与 Thymeleaf “整合实现前端页面” 的 “管理” )

  • 前面的内容中,我们只是通过 Spring Security后台增加了 权限控制前端页面没有做任何处理前端页面显示还是对应的链接等内容用户体验较差。接下来我们在前面案例的基础上,讲解如何使用 Security 与 Thymeleaf 整合 实现前端页面管理

① 添加 thymeleaf-extras-springsecurity5 “依赖启动器”

  • 在项目 pom.xml添加 thymeleaf-extras-springsecurity5 依赖启动器

    <!-- Security与Thymeleaf整合实现前端页面安全访问控制 -->
    <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-springsecurity5</artifactId>
    </dependency>
    

    需要注意的是,上述添加的 thymeleaf-extras-springsecurity5 依赖启动器中,其版本号同样是由 Spring Boot 统一整合并管理的。如果引用thymeleaf-extras-springsecurity4 依赖启动器,那么还 需要添加<version>标签手动进行版本管理

② 修改前端页面,使用 “Security相关标签” 进行 “页面控制”

  • 打开 项目中项目首页 : index.html,引入 Security 安全标签,并在页面根据需要使用Security 标签进行 显示控制修改后项目首页内容 如下所示 :

    index.html :

    <!DOCTYPE html>
    <!-- 配置开启thymeleaf模板引擎页面配置 -->
    <!--
       xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5" :
       通过 xmlns:sec 引入 " Security安全标签"
      -->
    <html lang="en" xmlns:th="http://www.thymeleaf.org"
    	  xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
    <head>
    	<meta charset="UTF-8">m
    	<title>影视直播厅</title>
    </head>
    <body>
    <h1 align="center">欢迎进入电影网站首页</h1>
    <!--
        div1:
        sec:authorize="isAnonymous()" : 判断用户是否 "未登录",只有匿名用户( 未登录用户 )才会显示 "请登录" 链接提示 -->
    <div sec:authorize="isAnonymous()">
        <h2 align="center">游客你好,如果想查看电影<a th:href="@{/userLogin}">请登录</a></h2>
    </div>
    
    <!--
        div2:
        sec:authorize="isAuthenticated()" : 判断用户是否 "已登录",只有认证用户( 登录用户 )才会显示 "用户信息" 和 "链接提示"
    
        sec:authentication="name" : 显示 "登录用户名"
        sec:authentication="principal.authorities" : 显示权限"authority"
        -->
    <div sec:authorize="isAuthenticated()">
      <h2 align="center"><span sec:authentication="name" style="color: #007bff"></span>
    	  您好,您的用户权限为<span sec:authentication="principal.authorities" style="color: darkkhaki"></span>,你有权观看以下电影
      </h2>
      <form th:action="@{/mylogout}" method="post">
    	  <input th:type="submit" th:value="注销">
      </form>
    </div>
    
    <!--
        div3:
        sec:authorize="hasAnyRole('common','vip')" : 只有角色有common 或 vip (对应权限Authority为 ROLE_common 和 ROLE_vip)且登录的用户
        才会显示普通电影列表信息
         -->
    <div sec:authorize="hasAnyRole('common','vip')">
    	<h3>普通电影</h3>
    	<ul>
    		<li><a th:href="@{/detail/common/1}">飞驰人生</a></li>
    		<li><a th:href="@{/detail/common/2}">夏洛特烦恼</a></li>
    	</ul>
    </div>
    
    <!--
        div4:
        sec:authorize="hasRole('common')" : 只有角色有 vip (对应权限Authority为  ROLE_vip)且登录的用户
        才会显示vip电影列表信息
         -->
    <div sec:authorize="hasRole('vip')">
    	<h3>VIP专享</h3>
    	<ul>
    		<li><a th:href="@{/detail/vip/1}">速度与激情</a></li>
    		<li><a th:href="@{/detail/vip/2}">猩球崛起</a></li>
    	</ul>
    </div>
    
    </body>
    </html>
    
  • 13
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值