SpringSecurity源码分析以及如何解决前后端分离出现的跨域问题

news2024/11/26 20:49:53

解决Security前后端分离出现的跨域问题

一. Security源码分析

首先在看源码之前我们先来看这张图 , 这张图展示了Security执行的全部流程

在这里插入图片描述

从上图可知Security执行的入口是UsernamePasswordAuthenticationFilter这个抽象类 , 那我们就先从该类进行分析

1. UsernamePasswordAuthenticationFilter

在这里插入图片描述

进入源码我们可以看到该类继承了一个叫做AbstractAuthenticationProcessingFilter的类 , 而相同的是二者的类名都包含Filter , 说明二者的顶级接口都包含Filter , 而Filter中包含一个非常重要的方法doFilter , 但是作为抽象重写该方法不是必须的 , 现在我们就从继承的层级关系寻找哪个类重写了该方法

AbstractAuthenticationProcessingFilter

在这里插入图片描述

该类继承了一个GenericFilterBean抽象类 , 我们继续往上找

GenericFilterBean

在这里插入图片描述

在这里我们终于找到了Filter接口 , 那么谁重写了doFilter呢? ( 这里的是否被重写需要从上层一层一层往下找 )

在这里插入图片描述

点开结构我们发现这里并没有发现doFilter , 那么就看它的下一级嘛 , 也就是AbstractAuthenticationProcessingFilter

AbstractAuthenticationProcessingFilter

在这里插入图片描述

在这里我们也是找到了doFilter方法 , 观察其源码该方法调用了一个叫做attemptAuthentication尝试认证的方法

在这里插入图片描述

而该方法是一个抽象方法 , 必然会被子类重写 , 那么绕来绕去又要回到一开始的UsernamePasswordAuthenticationFilter

2. attemptAuthentication

public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
    if (this.postOnly && !request.getMethod().equals("POST")) {
        throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
    } else {
        String username = this.obtainUsername(request);
        username = username != null ? username : "";
        username = username.trim();
        String password = this.obtainPassword(request);
        password = password != null ? password : "";
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
        this.setDetails(request, authRequest);
        return this.getAuthenticationManager().authenticate(authRequest);
    }
}

如下所示 , 前端传输的账号与密码是通过obtainUsername这个方法进行获取的 , 那么继续来看该方法做了什么在这里插入图片描述

    @Nullable
    protected String obtainUsername(HttpServletRequest request) {
        return request.getParameter(this.usernameParameter);
    }
private String usernameParameter = "username";
    private String passwordParameter = "password";

看完下面的代码是不是很明确了 , 账号密码是通过request.getParameter获取的而该方法 , 而该方法是获取表单数据的 , 在前后端分离的架构中拥有跨域问题 , 所以传统的Security架构无法解决这些问题 , 就需要我们自己来实现

二. 如何重写attemptAuthentication方法 , 实现数据传输

重写attemptAuthentication我们只需要继承AbstractAuthenticationProcessingFilter类并参照UsernamePasswordAuthenticationFilter原有的方法进行重新, 进行适当的修改即可

package com.itheima.security.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;

/**
 * @program: security_demo
 * @description:
 * @author: jixu
 * @create: 2024-10-18 01:40
 **/

public class MyUserNamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    protected MyUserNamePasswordAuthenticationFilter(String defaultFilterProcessesUrl) {
        super(defaultFilterProcessesUrl);
    }

    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";

    @Override
    public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
        if (!httpServletRequest.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + httpServletRequest.getMethod());
        } else {
            // 在这里就是出现问题的地方只需要重新修改获取账号密码的方式就行了
            // 这里由于我使用的是Json格式的数据进行传参 , 所以只需要获取数据, 反序列化即可
            ServletInputStream inputStream = httpServletRequest.getInputStream();
            HashMap<String,String> info = new ObjectMapper().readValue(inputStream, HashMap.class);
            String username = info.get(SPRING_SECURITY_FORM_USERNAME_KEY);
            username = username != null ? username : "";
            username = username.trim();
            String password = info.get(SPRING_SECURITY_FORM_PASSWORD_KEY);
            password = password != null ? password : "";
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            //this.setDetails(httpServletRequest, authRequest);
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }

    /**
     *
     * @param request
     * @param response
     * @param chain
     * @param authResult: 用户认证信息
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        User principal = (User) authResult.getPrincipal();
        String username = principal.getUsername();
        Collection<GrantedAuthority> authorities = principal.getAuthorities();
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        HashMap<String, String> info = new HashMap<>();
        info.put("code","1");
        info.put("msg","响应成功");
        // 将数据Map数据序列化成Json数据并发送
        response.getWriter().write(new ObjectMapper().writeValueAsString(info));


    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        super.unsuccessfulAuthentication(request, response, failed);
    }
}

在这里我们已经将自定义的配置信息完善了 , 那么接下来再来想一个问题 , 该配置信息如何被Security识别并使用?

在Security中先来的过滤器会覆盖后来的 , 也就是当识别到一个过滤器被使用了 , 那么它后面与之相同的过滤器就会失效

所以只需要提前声明我们自定义的过滤器就行了

package com.itheima.security.config;

import com.itheima.security.filter.MyUserNamePasswordAuthenticationFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @program: security_demo
 * @description:
 * @author: jixu
 * @create: 2024-10-17 21:01
 **/

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }


    /**
     * 定义用户认证和授权的信息
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin().and().logout().permitAll().and().csrf().disable().authorizeRequests();
        http.addFilterBefore(myUserNamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public MyUserNamePasswordAuthenticationFilter myUserNamePasswordAuthenticationFilter() throws Exception {
        // 构造认证过滤器对象 , 传入默认登录路径
        MyUserNamePasswordAuthenticationFilter myUserNamePasswordAuthenticationFilter = new MyUserNamePasswordAuthenticationFilter("/mylogin");
        // 认证该过滤器Bean
        myUserNamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManagerBean());

        return myUserNamePasswordAuthenticationFilter;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2220891.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

029_基于nodejs外卖网站设计和实现

目录 系统展示 开发背景 代码实现 项目案例 获取源码 博主介绍&#xff1a;CodeMentor毕业设计领航者、全网关注者30W群落&#xff0c;InfoQ特邀专栏作家、技术博客领航者、InfoQ新星培育计划导师、Web开发领域杰出贡献者&#xff0c;博客领航之星、开发者头条/腾讯云/AW…

Java应用程序的测试覆盖率之设计与实现(一)-- 总体设计

一、背景 作为测试&#xff0c;如何保证开发人员提交上来的代码都被测试覆盖到&#xff0c;是衡量测试质量的一个重要指标。 本系列文章将要说一说&#xff0c;如何搭建一套测试覆盖率的系统。 包括以下内容&#xff1a; jacoco agent采集执行覆盖率数据jacoco climaven集成…

基于Multisim的模拟拔河游戏比赛设计与仿真

1.设计一个模拟拔河游戏比赛的逻辑电路 2.使用15个发光二极管表示绳子&#xff0c;开机后只有最中间的发光二极管亮。 3.比赛双方各持一个按钮&#xff0c;快速不断地按动按钮&#xff0c;产生脉冲&#xff0c;谁按的快&#xff0c;发光的二极管就向谁的方向移动&#xff0c;每…

越狱你的 iPhone 安全吗?

越狱 iPhone 并不安全&#xff0c;可能会导致您的个人信息被盗、手机感染恶意软件以及软件出现故障。越狱 iPhone 会增加网络犯罪分子可利用来访问您的私人信息的安全漏洞数量。 继续阅读&#xff0c;了解什么是越狱以及为什么你永远不应该越狱你的 iPhone。 什么是越狱&…

K8S系列-Kubernetes网络

一、Kubernetes网络模型 ​ Kubernetes网络模型设计的一个基础原则是&#xff1a;每个Pod都拥有一个独立的IP地址&#xff0c;并假定所有Pod都在一个可以直接连通的、扁平的网络空间中&#xff0c;不管它们是否运行在同一个Node&#xff08;宿主机&#xff09;中&#xff0c;都…

鸿蒙网络编程系列31-使用RCP调用OpenAI接口实现智能助手

简介 在OpenAI推出GPT系列大模型以后&#xff0c;市场上各种类似的大模型也层出不穷&#xff0c;这些大模型也基本都会兼容OpenAI的接口&#xff0c;在开发基于大模型的应用时&#xff0c;选择使用OpenAI接口作为和后端大模型通讯的标准&#xff0c;可以更好的适配不同厂家的模…

Scala 内部类

一. scala的内部类的定义 它是指定义在类或对象内部的类。 idea实例 二.内部类的基本使用 idea实例 三.内部类的使用案例 四.内部对象 idea实例 五.匿名类 idea实例

Bluetooth Channel Sounding中关于CS Step及Phase Based Ranging相应Mode介绍

目录 BLE CS中Step定义 BLE CS中交互的数据包/波形格式 BLE CS中Step的不同Mode BLE CS中Step的执行过程 Mode0介绍 Mode0 步骤的作用 Mode0步骤的执行过程 Mode0步骤的执行时间 Mode0步骤的时间精度要求 Mode2介绍 Mode2步骤的作用和执行过程 Mode2步骤的执行时间 B…

13.4 Linux_网络编程_套接字属性

概述 什么是选项的级别&#xff1a; socket中可以设置的属性种类很多&#xff0c;比如socke的选项、传输层TCP/UDP的选项、数据链路层的选项。这些选项在不同的层级&#xff0c;这就是选项的级别。常用级别及含义如下&#xff1a; 级别含义SOL_SOCKET作用于套接字本身IPPROT…

MySQL中的优先规则

在图片的例子中&#xff0c;有两个条件&#xff1a; 第一个条件是job_id是AD_PRES并且薪水高于15,000。 第二个条件是job_id是SA_REP。 在图片中的例子有两个条件&#xff1a; 第一个条件是job_id是AD_PRES或者SA_REP。 第二个条件是薪水高于$15,000。

React Componet类组件详解(老项目)

React类组件是通过创建class继承React.Component来创建的&#xff0c;是React中用于构建用户界面的重要部分。以下是对React类组件的详细解释&#xff1a; 一、定义与基本结构 类组件使用ES6的class语法定义&#xff0c;并继承自React.Component。它们具有更复杂的功能&#…

Vscode连接WSL2(Ubuntu20.04)

一.安装WSL插件 在扩展里面搜索WSL,选择安装即可 二.连接到wsl 安装完毕后在左下角看到一个按钮&#xff08;一个>和一个<组成&#xff09;&#xff0c;点击在中间选择"连接到wsl",然后Vscode会弹出一个新窗口&#xff0c;左下角显示WSL子系统名称&#xff0…

vue中如何检测数组变化(vue基础,面试,源码级讲解)

大家有什么不明白的地方可以分享在评论区&#xff0c;大家一起探讨哦~~ &#xff08;如果对数据劫持还有所不明白的小伙伴&#xff0c;可以去看看上一篇文章哦&#xff09; 在vue2中&#xff0c;是如何对数组进行劫持的呢&#xff1f; 简单代码实现&#xff1a; 在vue2中&…

学习中,师傅b站泷羽sec——xss挖掘过程

某职业技术学院网站xss挖掘&#xff1a; 资产归纳 例如&#xff1a;先把功能点都看一遍&#xff0c;大部分都是文章 根据信息搜集第一课学习到一般主站的防御力是比较强的&#xff0c;出现漏洞的点不是对新手不友好。 在资产验证过程中还是把主站看了一遍 没有发现有攻击的机会…

G1 GAN生成MNIST手写数字图像

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 G1 GAN生成MNIST手写数字图像 1. 生成对抗网络 (GAN) 简介 生成对抗网络 (GAN) 是一种通过“对抗性”学习生成数据的深度学习模型&#xff0c;通常用于生成…

如何调试浏览器中的内存泄漏?

聚沙成塔每天进步一点点 本文回顾 ⭐ 专栏简介⭐ 如何调试浏览器中的内存泄漏&#xff1f;1. 什么是内存泄漏&#xff1f;2. 调试内存泄漏的工具3. 如何使用 Memory 面板进行内存调试3.1 获取内存快照&#xff08;Heap Snapshot&#xff09;获取内存快照的步骤&#xff1a;快照…

即时通讯增加Redis渠道

情况说明 在本地和服务器分别启动im服务&#xff0c;当本地发送消息时&#xff0c;会发现服务器上并没有收到消息 初版im只支持单机版&#xff0c;不支持分布式的情况。此次针对该情况对项目进行优化,文档中贴出的代码非完整代码&#xff0c;可自行查看参考资料[2] 代码结构调…

C Primer Plus 第9章——第一篇

你该逆袭了 文章目录 一、复习函数1、定义带形式参数的函数2、声明带形式参数函数的原型3、使用 return 从函数中返回值&#xff08;1&#xff09;、返回值不仅可以赋给变量&#xff0c;也可以被用作表达式的一部分。&#xff08;2&#xff09;、返回值不一定是变量的值&#x…

springboot redisTemplate hash 序列化探讨

前提提要&#xff1a;这个是个人小白总结&#xff0c;写完博客后开始厌蠢。 redisTemplate 有两种插入hash的方式 redisTemplate.opsForHash().putAll(key, map);redisTemplate.opsForHash().put(key, field, value);在使用的过程中&#xff0c;难免会疑问为什么 key field v…

Windows下部署autMan

一、安装autMan 下载autMan压缩包 https://github.com/hdbjlizhe/fanli/releases 解压安装包 二、运行&#xff08;注意&#xff0c;无论是交互运行还是静默运行&#xff0c;终端均不可关闭&#xff09; 基本运行 双击autMan.exe运行。 高级运行 在autMan文件夹&#xff0…