项目1在线交流平台-7.构建安全高效的企业服务-2.使用Security自定义社区网页认证与授权

2023-05-16

文章目录

    • 功能需求
    • 一、 废弃登录检查的拦截器
    • 二、授权配置
      • 1. 导包
      • 2. Security配置
        • 2.1 `WebSecurity`
        • 2.2`HttpSecurity`
          • ` http.authorizeRequests()`
            • `.antMatchers()`-要验证的请求路径
            • `.hasAnyAuthority( )`-需要的权限
            • `.anyRequest().permitAll()`-请他请求无需权限访问
          • `http.exceptionHandling()`
            • `authenticationEntryPoint`-认证没通过
            • `.accessDeniedHandler`-权限不够
        • `http.logout()`-退出设置
    • 三、使用自定义认证系统
      • 1. 处理思路
      • 2. 在Service业务层增加查询权限的方法
      • 3.认证信息进token和Context时机
        • `new UsernamePasswordAuthenticationToken()`-存入token
        • `SecurityContextHolder.setContext()`-存入context
      • 4. 请求之后和退出登录后要清理容器的认证信息
        • `SecurityContextHolder.clearContext()`-清理容器
    • 四、处理CSRF攻击配置
      • 1. 防止CFRS攻击原理
      • 2. Security自动配置了普通请求的CSRF攻击
      • 3. 异步请求自己配置csrf的tocken
        • HTML `` 标签
          • 标签定义及使用说明
          • 提示和注释
        • 自定义`` 标签,传入csdf元数据
        • 在发布请求的js文件中设置-将csrf令牌发给浏览器携带
      • 4. 关闭csrf
          • `.and().csrf().disable()`- 关闭csrf功能
      • 5. 测试结果显示
      • 6. 其他异步请求配置

参考牛客网高级项目教程
尚硅谷SpringSecurity教程笔记

社区 Spring Security 从入门到进阶系列教程

功能需求

在这里插入图片描述

  • 1.有些网页请求需要登录者才有权限访问,采用security权限空值,废弃之前的登录检查拦截器控制
  • 2.对社区网页中的不同请求分配不同的访问权限
  • 3.登录用户密码验证认证,之前系统中认证逻辑已经很完善了,可以绕过Security默认认证流程,采用自定义的认证逻辑
  • 4.CSRF配置更新,原配置只能处理普通的表单请求,对于异步的AJAX表单请求,需要自定义CSRF配置

一、 废弃登录检查的拦截器

  • 拦截器配置中,将此拦截器注释掉即可,即不会将拦截器类添加到系统拦截器中去管理

在这里插入图片描述

二、授权配置

1. 导包

<!--       整合security-->
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-security</artifactId>
      </dependency>

2. Security配置

  • 因使用自定义的认证逻辑,故,无需重写Security的认证方法AuthenticationManagerBuilder=

2.1 WebSecurity

  • 忽略静态资源的访问
@Override
public void configure(WebSecurity web) throws Exception {
    // 忽略静态资源的访问
    web
        .ignoring()
        .antMatchers("/resources/**");
}

2.2HttpSecurity

http.authorizeRequests()
  • 处理要验证权限的请求-即访问这些请求,需要有一定的权限
.antMatchers()-要验证的请求路径
.hasAnyAuthority( )-需要的权限
.anyRequest().permitAll()-请他请求无需权限访问
  • 注意,用户头像的请求,不能拦截,否则首页中普通访客看不到用户的头像
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers(   // 要验证权限的请求
                    "/user/setting",
                    "/user/upload",
                    "/discuss/add",
                    "/comment/add/**",
                    "/letter/**",
                    "/notice/**",
                    "/like",
                    "/follow",
                    "/unfollow"
            )
            .hasAnyAuthority( // 需要的权限
                    AUTHORITY_ADMIN,
                    AUTHORITY_MODERATOR,
                    AUTHORITY_USER
            )
            .anyRequest().permitAll();  // 请他请求无需权限访问
}
http.exceptionHandling()
  • 当访问某些请求,权限不足而出现异常后处理的逻辑
authenticationEntryPoint-认证没通过

参考博客

  • 他所建模的概念是:“认证入口点”。处理用户请求处理过程中遇到认证异常的接口

  • ExceptionTranslationFilter用于开启特定认证方案(authentication schema)的认证流程。

  • 该接口只定义了一个方法 :

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, 
           			AuthenticationException e) throws IOException, ServletException { 
    }
    
    • 这里参数request是遇到了认证异常authException用户请求,
    • response是将要返回给客户的相应,
    • 方法commence实现,也就是相应的认证方案逻辑会修改response并返回给用户引导用户进入认证流程。

处理逻辑:参考之前统一处理异常的逻辑

http.exceptionHandling()  // 权限不够去访问出现问题后的处理
        // 没有登录的情况
        .authenticationEntryPoint(new AuthenticationEntryPoint() {
            @Override
            public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
                String xRequestedWith = request.getHeader("x-requested-with"); // 获取请求头参数
                if ("XMLHttpRequest".equals(xRequestedWith)) {  // 异步请求,向前端用JSON写入错误提示信息
                    response.setContentType("application/plain;charset=utf-8"); // 设置格式:普通字符串
                    PrintWriter writer = response.getWriter();
                    writer.write(CommunityUtil.getJSONString(403, "您还没有登录哦!"));
                } else {    // 普通请求-直接重定向到登录页面
                    response.sendRedirect(request.getContextPath() + "/login");
                }
            }
        })
.accessDeniedHandler-权限不够
  • 处理权限不够的异常接口,接口中也定义了一个处理的逻辑方法
  • 在方法中进行处理即可
    • 异步请求
    • 普通请求
.accessDeniedHandler(new AccessDeniedHandler() {
    // 权限不足的情况
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
        String xRequestedWith = request.getHeader("x-requested-with");
        if ("XMLHttpRequest".equals(xRequestedWith)) {	// 异步请求
            response.setContentType("application/plain;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.write(CommunityUtil.getJSONString(403, "您没有访问此功能的权限!"));
        } else {	// 普通请求-重定向到指定提示页面
            response.sendRedirect(request.getContextPath() + "/denied");
        }
    }
});
  • 定义提示没有权限访问的网页请求

    @RequestMapping(path = "/denied", method = RequestMethod.GET)
    public String getDeniedPage() {
        return "/error/404";
    }
    

http.logout()-退出设置

  • 因为Security是过滤器,会在Controller之前拦截默认的/logout/请求,就不会走自定义的/logout/请求

  • 故,为了处理自定义的退出逻辑,在配置中,将默认的请求路径改了,就不会去拦截/logout/请求

    在这里插入图片描述

// 退出设置-不走security自定义的退出逻辑,走自定义的退出逻辑
http.logout()
        .logoutUrl("/securitylogout");// 覆盖默认设置的/logout路径,走自己定义的退出逻辑

三、使用自定义认证系统

1. 处理思路

  • 前面配置中,没有对认证方法进行重写处理,是因为项目中前期的认证逻辑已经很完善,可以直接使用
  • 为了使用自定义的认证逻辑,需要将自己认证逻辑与Security结合
  • Security认证处理后,都会将认证信息放入token容器里,每次认证都会从token中取出主体信息进行验证
  • 因此,可以将之前自定义的认证处理的结果也设法放进Security的token容器内,再将token放入SecurityContext上下文中
    • 就可以让Security走之前定义的认证处理逻辑
    • 因为Context上下文对所有请求都是共享的,这样逻辑上可以对所有请求进行认证

在这里插入图片描述

2. 在Service业务层增加查询权限的方法

  • 自定义的User实体类和Service类可以不用继承UserDetailUserDetailsService 接口,不需要Security管理这个主体
  • 但是,需要在自定义的业务层中,添加根据用户id查询指定用户权限的方法,这样才能构建认证信息交给token
/**
 * 查询指定用户的权限
 * @param userId    指定用户id
 * @return          权限集合-有可能一个用户有多个权限
 */
public Collection<? extends GrantedAuthority> getAuthorities(int userId) {
    User user = this.findUserById(userId);  // 用service包装的方法-从redis中查
    List<GrantedAuthority> list = new ArrayList<>();
    list.add(new GrantedAuthority() {
        @Override
        public String getAuthority() {
            switch (user.getType()) {
                case 1:
                    return AUTHORITY_ADMIN;        // 管理员
                case 2:
                    return AUTHORITY_MODERATOR; // 版主
                default:
                    return AUTHORITY_USER;     // 普通用户
            }
        }
    });
    return list;
}

3.认证信息进token和Context时机

  • 之前通过拦截器进行认证登录状态,判断是否登录,
    • 是给用户一个ticket,用户访问时,拿到这个ticket,去数据库中查找凭证
    • 根据凭证信息认证用户是否是登录状态,如果认证通过,
    • 将用户信息存入到当前请求线程容器中,方便每个请求获取使用
  • 同样逻辑,认证信息要再存一份放入token里,方便Security进行权限管理

new UsernamePasswordAuthenticationToken()-存入token

SecurityContextHolder.setContext()-存入context

// 在Controller之前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    logger.debug("preHandle " + handler.toString());
    // 1.获取ticket
    String ticket = CookieUtil.getValue(request, "ticket");
    // 2.根据ticket查询user
    if(ticket != null) {
        // 向数据库中查询凭证
        LoginTicket loginTicket = userService.selectByTicket(ticket);
        if(loginTicket != null &&
            loginTicket.getStatus() == 0 &&
            loginTicket.getExpired().after(new Date())) {
            // 有效,就根据凭证找到用户信息
            User user = userService.findUserById(loginTicket.getUserId());
            
            // 在本次请求中持有用户信息,在请求结束前一直保存在请求的当前线程容器中
            hostHolder.setUsers(user);
            
            // 将认证信息-认证主体及主体的权限在存一份到Security的token中
            Authentication authentication = new UsernamePasswordAuthenticationToken
                    (user, user.getPassword(), userService.getAuthorities(user.getId()));
            // 再将认证信息存入SecurityContext中
            SecurityContextHolder.setContext(new SecurityContextImpl(authentication));
        }
    }
    return true;    // true,表示拦截处理之后继续执行
}

4. 请求之后和退出登录后要清理容器的认证信息

SecurityContextHolder.clearContext()-清理容器

请求之后清理

// 在模板视图渲染后处理,一般做清理工作
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    logger.debug("afterHandle " + handler.toString());
    hostHolder.clear();
    SecurityContextHolder.clearContext();
}

退出登录后清理

/**
 * 退出登录请求
 * @param ticket    登录凭证t票
 * @return
 */
@RequestMapping(path = "/logout", method = RequestMethod.GET)
public String logout(@CookieValue("ticket") String ticket) {
    userService.logout(ticket);
    SecurityContextHolder.clearContext();
    return "redirect:/login";
}

四、处理CSRF攻击配置

Web安全攻击XSS与CSRF攻击简述

  • 配置CFRS(盗取cooike中的凭证,模仿你的身份提交表单,盗取你的数据)

  • 普通请求SpringSecurity已经自定义配置好了,异步的需要自己处理

1. 防止CFRS攻击原理

  • CSRF俗称跨站协议伪造(跨站请求攻击)

  • 过程模拟

    img

    • 即A访问表单,在提交表单前,访问了B网站
    • B窃取了A的cookie,伪造成A的身份向服务器提交了表单form,窃取数据
  • 防止攻击策略

    在这里插入图片描述
    • 服务器向信任的浏览器发放一个随机的tocken放在提交的表单form中
    • 浏览器提交表单时,会连同tocken一起提交给服务器去验证
    • 其他网站就算盗用了ticket,但无法知道表单中的tocken,故,会被服务器拒绝访问

2. Security自动配置了普通请求的CSRF攻击

  • 点开帖子详情页面,里面有很多提交表单请求
  • 查看源代码,会发现隐藏的标签里传入了CSRF的tocken随机值

在这里插入图片描述

3. 异步请求自己配置csrf的tocken

HTML <meta> 标签

标签定义及使用说明
  • 元数据(Metadata)是数据的数据信息。
  • 标签提供了 HTML 文档的元数据。元数据不会显示在客户端,但是会被浏览器解析
提示和注释

注意:<meta> 标签通常位于<head>区域内

注意: 元数据通常以 名称/值 对出现。

注意: 如果没有提供 name 属性,那么名称/值对中的名称会采用 http-equiv 属性的值。

自定义<meta> 标签,传入csdf元数据

  • 访问该页面时,在此生成CSRF令牌,将Security中的元数据的传给页面,以key,value形式
<!--    访问该页面时,在此生成CSRF令牌,将Security中的元数据的传给页面,以key,value形式-->
   <!--csrf的key-->
   <meta name="_csrf" th:content="${_csrf.headerName}">
   <!--csrf的value-->
   <meta name="_csrf_header" th:content="${_csrf.token}">

在发布请求的js文件中设置-将csrf令牌发给浏览器携带

// 发送AJAX请求之前,将CSRF令牌设置到请求的消息头中.
// 使用JQuery选择器,取指定meta标签中的content属性的值,赋值给变量key,value
var header = $("meta[name='_csrf_header']").attr("content");
var token = $("meta[name='_csrf']").attr("content");
// 在发布异步请求前,先对请求进行设置
$(document).ajaxSend(function (e, xhr, options) {
    // xhr为发布异步请求的核心对象,设置请求头的key和value-设置完后可以放到浏览器中
    // 发送请求时,会携带这个数据
    xhr.setRequestHeader(header, token);
});

4. 关闭csrf

.and().csrf().disable()- 关闭csrf功能

5. 测试结果显示

  • 访问首页,mata标签携带数据

    在这里插入图片描述

  • 发布异步请求

    在这里插入图片描述

6. 其他异步请求配置

  • 同理,点赞、关注、取关、发送私信这些异步请求也要配置csrf,否则无法访问

点赞

  • 帖子详情页面
  • discuss.js

关注与取关

  • 个人主页页面
  • 关注和取关页面
  • profile.js

发送私信

  • 私信页面letter.html
  • 和私信详情页面
  • letter.js
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

项目1在线交流平台-7.构建安全高效的企业服务-2.使用Security自定义社区网页认证与授权 的相关文章

  • 构造内核源码树

    编写驱动程序时 xff0c 需要内核源码树的支持 内核源码树时从内核源代码编译得到的 下面开始构造内核源代码的步骤 以Ubuntu为例子 1 下载内源代码 xff0c 位置www kernel org 注意 xff1a 源码树内核的版本要和
  • 裁剪图像中感兴趣区域python

    题外话 xff1a 比较全面的缩略图及相应源码 http matplotlib org gallery html http www cnblogs com wei li archive 2012 05 23 2506940 html 题外外
  • Linux设备驱动程序(LDD)中snull的编译问题

    对LDD中snull程序 xff0c 编译的时候会有许多问题 xff0c 鉴于网上还没有合适的解决办法 xff0c 做此总结 xff0c 整理知识 本文在debian6 0上运行通过 xff0c 内核版本为2 6 32 学习LDD中网络驱动
  • 认识(大端--小端)端模式

    span style color 000000 端模式 xff08 Endian xff09 的这个词出自Jonathan Swift书写的 格列佛游记 这本书根据将鸡蛋敲开的方法不同将所有的人分为两类 xff0c 从圆头开始将鸡蛋敲开的人
  • HOW TO install nam for ns2 on debian

    Debian is convinent to install software packages for the tool aptl Like many other packages we can use apt get install n
  • c++ #pragma once和 #ifndef 优缺点对比分析

    pragma once ifndef方式为了避免同一个头文件被包含 xff08 include xff09 多次 pragma once 声明 定义语句 ifndef SOMEFILE H define SOMEFILE H 声明 定义语句
  • roslaunch找不到packge

    roslaunch找不到packge 尝试下面几种做法 1 source bashrc 2 source catkin ws devel setup bash 3 rospack profile 为确保ROS能找到新包 xff0c 常常在发
  • DSP:TMS320C6657 之 UART波特率问题

    6657 设置串口波特率 以614400为例 xff08 1 xff09 根据公式计算分频系数 xff08 2 xff09 1GHz 主频下 UART 输入频率 166666666Hz xff08 1 6 xff09 xff08 3 xff
  • 手写httpServer Demo案例

    相信每一个java程序猿在学习javaWeb的时候 xff0c 或多或少接触了Servlet 或者说通过Servlet来完成页面发送的请求 今天 xff0c 模仿Servlet接受和处理请求实现一个简单的httpServer 该Server
  • ubuntu18.04 查看在用串口

    1 终端输入cutecom 打开串口助手 xff0c 可能没有下载 xff0c 可根据提示下载安装 sudo cutecom 2 点击device旁边的下拉按钮即可查询当前在用的串口
  • Linux解决未定义的引用过程记录

    Linux解决未定义的引用过程记录 在摸索vscode使用的过程中 xff0c 编写的代码出现了为定义的引用错误 csdn上搜索了很多 xff0c 代码小白看完觉得写的非常的简略 xff0c 完全无从下手 xff08 应该是我太菜了 xff
  • 十一种室内定位传感器方案汇总介绍与对比(机器人、物联网领域)

    室内定位传感器方案汇总 目录 室内定位传感器方案汇总 1 定位方案概述 1 1 内定位系统有最基本的5种算法 xff1a 1 2 常用的室内定位技术主要包括以下几种 xff1a 1 3 定位理论 1 4 不同的定位方案对比 2 各种定位方案
  • C++中的unique函数

    STL中的unique函数的头文件 xff1a span class hljs preprocessor include lt iostream gt span unique 的作用是 去掉 容器中相邻元素的重复元素 xff0c 这里所说的
  • 单片机开发入门---从零开始玩转FRDM-KL25Z

    一 背景介绍 最近需要开发一个程序 xff0c 使用飞思卡尔的开发板FRDM KL25Z xff0c 来设计一款 西蒙游戏 的改进版 xff0c 下面我们先来了解一下西蒙游戏 西蒙游戏 是一款益智休闲类小游戏 xff0c 它的游戏规则是 x
  • SSD---系统架构

    SSD主要由两大模块构成 主控和闪存介质 另外可选的还有Cache缓存单元 主控是SSD的大脑 xff0c 承担着指挥 运算和协调的作用 xff0c 具体表现在 xff1a 前端实现标准主机接口与主机通信 xff0c 接口包括SATA SA
  • SSD核心技术---FTL

    FTL算法的优劣与否 xff0c 直接决定了SSD在性能 xff08 Performance xff09 可靠性 xff08 Reliability xff09 耐用性 xff08 Endurance xff09 等方面的好坏 xff0c
  • SSD---PCIe介绍

    SSD已经大跨步迈入PCIe时代 作为SSD的一项重要技术 xff0c 我们有必要对PCIe有个基本的了解
  • SSD---NVMe介绍

    何为NVMe xff1f NVMe即Non Volatile Memory Express xff0c 是非易失性存储器标准 xff0c 是跑在PCIe接口上的协议标准 NVMe的设计之初就有充分利用了PCIe SSD的低延时以及并行性 x
  • SSD---ECC原理

    我们知道 xff0c 所有型号的闪存都无法保证存储的数据会永久稳定 xff0c 这时候就需要ECC xff08 纠错码 xff09 去给闪存纠错 ECC能力的强弱直接影响到SSD的使用寿命和可靠性 本章将简单介绍ECC的基本原理和目前最主流
  • 音响发烧友---HiFi音频功放

    最近一直想做个开源的电子项目 xff0c 思考许久还是选择做个HiFi音频功放 作为一个音响发烧友 xff0c 带大家DIY一台属于自己的功放 聆听一下 xff0c 纯正的音乐之美 首选需要了解一下功放的类型 xff1a 纯甲类功率放大器乙

随机推荐