当前位置: 首页 > news >正文

Springboot +spring security,自定义认证和授权异常处理器

一.简介

在Spring Security中异常分为两种:

  1. AuthenticationException 认证异常
  2. AccessDeniedException 权限异常 我们先给大家演示下如何自定义异常处理器,然后再结合源码帮助大家进行分析

二.创建项目

如何创建一个SpringSecurity项目,前面文章已经有说明了,这里就不重复写了。

三.自定义异常处理器

3.1配置SecurityConfig

这里主要是authenticationEntryPoint和accessDeniedHandler配置,代码如下:

@Beanpublic SecurityFilterChain config(HttpSecurity http) throws Exception {http.authorizeHttpRequests().anyRequest().authenticated().and().formLogin().loginPage("/login.html").loginProcessingUrl("/login").permitAll().and().cors().configurationSource(corsConfigurationSource()).and().exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {Map<String, Object> result = new HashMap<>();result.put("code", -1);result.put("msg", "authenticationEntryPoint");result.put("data", authException.getMessage());System.out.println("调用次数");writeResp(result, response);}}).accessDeniedHandler(new AccessDeniedHandler() {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {Map<String, Object> result = new HashMap<>();result.put("code", -1);result.put("msg", "accessDeniedHandler");result.put("data", accessDeniedException.getMessage());writeResp(result, response);}}).and().csrf().disable();http.headers().cacheControl();return http.build();}

3.2自定义登录页面

为什么要自定义登录页面呢,因为如果我们实现了异常处理端点,security 就不会将生成登录页面的过滤器加入,我们看下源码:“authenticationEntryPoint == null)”才会添加自定义生成登录页面的过滤器。

代码如下:

 public void configure(H http) {AuthenticationEntryPoint authenticationEntryPoint = null;ExceptionHandlingConfigurer<?> exceptionConf = http.getConfigurer(ExceptionHandlingConfigurer.class);if (exceptionConf != null) {authenticationEntryPoint = exceptionConf.getAuthenticationEntryPoint();}if (this.loginPageGeneratingFilter.isEnabled() && authenticationEntryPoint == null) {this.loginPageGeneratingFilter = postProcess(this.loginPageGeneratingFilter);http.addFilter(this.loginPageGeneratingFilter);LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);if (logoutConfigurer != null) {http.addFilter(this.logoutPageGeneratingFilter);}}}

登陆页面代码:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<div th:text="SPRING_SECURITY_LAST_EXCEPTION"></div>
<form action="/login" method="post">用户名:<input name="username" type="text"><br>密码:<input name="password" type="password"><br><button type="submit">登陆</button>
</form>
</body>
</html>

3.3配置用户信息

代码如下:

spring.security.user.password=123456
spring.security.user.roles=admin
spring.security.user.name=lglbc

3.4添加controller

代码如下:

 @RequestMapping("/user")@PreAuthorize("hasRole('user')")public String user() {return "hello";}@RequestMapping("/admin")@PreAuthorize("hasRole('admin')")public String admin() {return "admin";}

3.4验证

验证匿名访问user接口

http://localhost:8080/user,截图如下:
在这里插入图片描述
返回的是自定义异常,被Authentication进行处理,稍后从源码角度分析。

验证登录后访问admin接口
在这里插入图片描述
请求成功,返回接口数据 需要注意的是,如果实现了异常端点,想之前自动跳转到登录页面将不再生效,因为这些逻辑都是在默认的异常端点里面处理

3.5异常过滤器实现原理分析

上面讲到自定义异常端点的回调都是通过异常处理过滤器实现,我们现在就从这块开始看, 首先,我们还是从入口开始找:.exceptionHandling() 点击.exceptionHandling()进入到代码中,我们发现我们熟悉的ExceptionHandlingConfigurer,代码如下:

public ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling() throws Exception {return getOrApply(new ExceptionHandlingConfigurer<>());}

3.5.1init

ExceptionHandlingConfigurer没有重写,所以我们就直接看configure方法

3.5.2configure

 public void configure(H http) {AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http);ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(entryPoint,getRequestCache(http));AccessDeniedHandler deniedHandler = getAccessDeniedHandler(http);exceptionTranslationFilter.setAccessDeniedHandler(deniedHandler);exceptionTranslationFilter = postProcess(exceptionTranslationFilter);http.addFilter(exceptionTranslationFilter);}
  1. 创建过滤 ExceptionTranslationFilter
  2. 获取两种异常的处理端点,如果我们配置了就会使用我们自己的,否则使用默认的 *
    在这里插入图片描述

在这里插入图片描述

  1. 将端点配置到ExceptionTranslationFilter中
  2. 将ExceptionTranslationFilter 放到IOC容器中,并且放到过滤器链中 *
    在这里插入图片描述
    加入的时候,会获取过滤器的优先级,ExceptionTranslationFilter放在了AuthorizationFilter之前,这个后面讲到为什么这么做

在这里插入图片描述
看configure方法就是为了看它到底使用什么过滤器,现在我们直接看ExceptionTranslationFilter。

3.5.3ExceptionTranslationFilter

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws IOException, ServletException {try {chain.doFilter(request, response);}catch (IOException ex) {throw ex;}catch (Exception ex) {// Try to extract a SpringSecurityException from the stacktraceThrowable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);RuntimeException securityException = (AuthenticationException) this.throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);if (securityException == null) {securityException = (AccessDeniedException) this.throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);}if (securityException == null) {rethrow(ex);}if (response.isCommitted()) {throw new ServletException("Unable to handle the Spring Security Exception "+ "because the response is already committed.", ex);}handleSpringSecurityException(request, response, chain, securityException);}}

在doFilter方法中,异常过滤器直接调用了下个过滤器,并没有做什么

  1. 捕获执行下个过滤器的异常
  2. 将异常丢给handleSpringSecurityException()方法进行处理

handleSpringSecurityException类的代码如下:

private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response,FilterChain chain, RuntimeException exception) throws IOException, ServletException {if (exception instanceof AuthenticationException) {handleAuthenticationException(request, response, chain, (AuthenticationException) exception);}else if (exception instanceof AccessDeniedException) {handleAccessDeniedException(request, response, chain, (AccessDeniedException) exception);}}

根据异常类型不同,分别调用不同的处理方法

handleAuthenticationException类代码如下:

private void handleAuthenticationException(HttpServletRequest request, HttpServletResponse response,FilterChain chain, AuthenticationException exception) throws ServletException, IOException {this.logger.trace("Sending to authentication entry point since authentication failed", exception);sendStartAuthentication(request, response, chain, exception);}protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,AuthenticationException reason) throws ServletException, IOException {// SEC-112: Clear the SecurityContextHolder's Authentication, as the// existing Authentication is no longer considered validSecurityContext context = SecurityContextHolder.createEmptyContext();SecurityContextHolder.setContext(context);this.requestCache.saveRequest(request, response);this.authenticationEntryPoint.commence(request, response, reason);}

这个是直接调用了this.authenticationEntryPoint.commence方法,authenticationEntryPoint讲过,如果我们配置了就使用配置的,否则使用默认的端点处理

handleAccessDeniedException类代码如下:

private void handleAccessDeniedException(HttpServletRequest request, HttpServletResponse response,FilterChain chain, AccessDeniedException exception) throws ServletException, IOException {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();boolean isAnonymous = this.authenticationTrustResolver.isAnonymous(authentication);if (isAnonymous || this.authenticationTrustResolver.isRememberMe(authentication)) {sendStartAuthentication(request, response, chain,new InsufficientAuthenticationException(this.messages.getMessage("ExceptionTranslationFilter.insufficientAuthentication","Full authentication is required to access this resource")));}else {this.accessDeniedHandler.handle(request, response, exception);}}

这个处理逻辑和前面有点不同

  1. 如果未登录或者是rememberMe,则还是调用sendStartAuthentication
  2. 否则调用this.accessDeniedHandler.handle(),这个和前面的逻辑一致

四.最后用一张图总结

在这里插入图片描述

相关文章:

Springboot +spring security,自定义认证和授权异常处理器

一.简介 在Spring Security中异常分为两种&#xff1a; AuthenticationException 认证异常AccessDeniedException 权限异常 我们先给大家演示下如何自定义异常处理器&#xff0c;然后再结合源码帮助大家进行分析 二.创建项目 如何创建一个SpringSecurity项目&#xff0c;前…...

Dockerfile(1) - FROM 指令详解

FROM 指明当前的镜像基于哪个镜像构建dockerfile 必须以 FROM 开头&#xff0c;除了 ARG 命令可以在 FROM 前面 FROM [--platform<platform>] <image> [AS <name>]FROM [--platform<platform>] <image>[:<tag>] [AS <name>]FROM […...

【嵌入式Linux】源码菜单配置 | 编译 | 菜单配置的实现 | 源码编译的实现

源码配置编译 源码配置编译,要把中间各个环节都理清楚 厂商把自己增加的东西专门放了个文件独立&#xff0c;方便开发者发现变化 1.菜单配置 移植的第一步&#xff0c;就是选配&#xff0c;通过make menuconfig图形化界面选配 //载入配置 $ make ARCHarm64 tegra_defconfi…...

python自动化爬虫实战

python自动化爬虫实战 偶然的一次机会再次用到爬虫&#xff0c;借此机会记录一下爬虫的学习经历&#xff0c;方便后续复用。 需求&#xff1a;爬取网站数据并存入的csv文件中&#xff0c;总体分为两步 爬取网站数据存到到csv文件中 1、配置爬虫环境 1.1、下载自动化测试驱动 …...

LVGL-最新版本及其版本定义标准

lvgl的最新版本是9.0.0&#xff0c;处于开发分支中。 稳定版本是8.3.0. 建议一般开发使用稳定版8.3.0. .\lvgl.h定义了当前版本 /*************************** CURRENT VERSION OF LVGL ***************************/ #define LVGL_VERSION_MAJOR 8 #define LVGL_VERSION_MINO…...

ORB_SLAM2算法中如何计算右目和左目两个特征点的是否匹配?

文章目录 if(kpR.octave<levelL-1 || kpR.octave>levelL+1)const int &levelL = kpL.octave;if(uR>=minU && uR<=maxU)const cv::Mat &dR = mDescriptorsRight.row(iR);const int dist = ORBmatcher::DescriptorDistance(dL,dR);筛选最佳匹配特征点…...

Android 12.0系统Settings主页去掉搜索框

1.概述 在12.0定制化开发中,在系统原生设置中主页的搜索框是要求去掉的,不需要搜索功能,所以首选看下布局文件 看下搜索框是哪个布局,然后隐藏到布局,达到实现功能的目的 2.系统Settings主页去掉搜索框的主要代码 packages/apps/Settings/src/com/android/settings/home…...

电脑数据丢失如何恢复

随着电脑使用的日益普及&#xff0c;数据丢失成为了很多用户不得不面对的问题。数据丢失的原因有很多&#xff0c;例如误删除文件、磁盘格式化、电脑病毒等等。一旦发生数据丢失的情况&#xff0c;我们就需要利用专业的数据恢复工具来尽快找回被丢失的数据。下面我们就来详细介…...

大数据分析案例-基于决策树算法构建世界杯比赛预测模型

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…...

Python 图形界面框架 PyQt5 使用指南

Python 图形界面框架 PyQt5 使用指南 使用Python开发图形界面的软件其实并不多&#xff0c;相对于GUI界面&#xff0c;可能Web方式的应用更受人欢迎。但对于像我一样对其他编程语言比如C#或WPF并不熟悉的人来说&#xff0c;未必不是一个好的工具。 常见GUI框架 PyQt5[1]&#…...

代码随想录算法训练营第四十二天 | 二维dp数组01背包, 力扣 416. 分割等和子集

背包 解析 1.确定dp数组以及下标的含义 对于背包问题&#xff0c;有一种写法&#xff0c; 是使用二维数组&#xff0c;即dp[i][j] 表示从下标为[0-i]的物品里任意取&#xff0c;放进容量为j的背包&#xff0c;价值总和最大是多少。 2.确定递推公式 有两个方向推出来dp[i][…...

【1110. 删点成林】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给出二叉树的根节点 root&#xff0c;树上每个节点都有一个不同的值。 如果节点值在 to_delete 中出现&#xff0c;我们就把该节点从树上删去&#xff0c;最后得到一个森林&#xff08;一些不相交的…...

第三章 JVM内存概述

附录&#xff1a;精选面试题 Q&#xff1a;为什么虚拟机必须保证一个类的Clinit( )方法在多线程的情况下被同步加锁 &#xff1f; A: 因为虚拟机在加载完一个类之后直接把这个类放到本地内存的方法区&#xff08;也叫原空间&#xff09;中了&#xff0c;当其他程序再来调这个类…...

基于SpringBoot的企业客户信息反馈平台的设计与实现

背景 企业客户信息反馈平台能够通过互联网得到广泛的、全面的宣传&#xff0c;让尽可能多的用户了解和熟知企业客户信息反馈平台的便捷高效&#xff0c;不仅为客户提供了服务&#xff0c;而且也推广了自己&#xff0c;让更多的客户了解自己。对于企业客户信息反馈而言&#xf…...

【SA8295P 源码分析】01 - SA8295P 芯片介绍

【SA8295P 源码分析】01 - SA8295P 芯片介绍 一、Processors 处理器介绍二、Memory 内存介绍三、Multimedia 多媒体介绍3.1 DPU 显示处理器:Adreno DPU 11993.2 摄像头ISP:Spectra 395 ISP3.3 视频处理器:Adreno video processing unit (VPU)3.4 图像处理器:Adreno graphic…...

扩展1:Ray Core详细介绍

扩展1:Ray Core详细介绍 导航 1. 简介和背景2. Ray的基本概念和核心组件3. 分布式任务调度和依赖管理4. 对象存储和数据共享5. Actor模型和并发编程6. Ray的高级功能和扩展性7. 使用Ray构建分布式应用程序的案例研究8. Ray社区和资源9. 核心框架介绍...

day08 Spring MVC

spring MVC相当于Servlet mvc解释:模型,视图,控制器 **使用该思想的作用:**减少耦合性,提高可维护性 Spring MVC前端控制器 方式1 1.在web.xml中配置前端控制器方式2 ​ 要是用前端控制器,必须在web.xml中配置DidpatcherServlet类 <!--前端控制器--> <servlet&g…...

c++中的extern “C“

在一些c语言的library库中&#xff0c;我们经常可以还看下面这样的结构 #ifndef __TEST_H #define __TEST_H#ifdef _cplusplus extern "C" { #endif/*...*/#ifdef _cplusplus } #endif #endif#ifndef __TEST_H这样的宏定义应该是非常常见了&#xff0c;其作用是为了…...

python异常处理名称整理

Python 异常处理 python提供了两个非常重要的功能来处理python程序在运行中出现的异常和错误。你可以使用该功能来调试python程序。BaseException所有异常的基类UnboundLocalError访问未初始化的本地变量SystemExit...

SpringMVC拦截器

SpringMVC拦截器 介绍 拦截器&#xff08;interceptor&#xff09;的作用 SpringMVC的拦截器类似于Servlet开发中的过滤器Filter&#xff0c;用于对处理器 进行预处理和后处理 将拦截器按一定的顺序连接成一条链&#xff0c;这条链称为拦截器链&#xff08;Interception Ch…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

wpf在image控件上快速显示内存图像

wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像&#xff08;比如分辨率3000*3000的图像&#xff09;的办法&#xff0c;尤其是想把内存中的裸数据&#xff08;只有图像的数据&#xff0c;不包…...

针对药品仓库的效期管理问题,如何利用WMS系统“破局”

案例&#xff1a; 某医药分销企业&#xff0c;主要经营各类药品的批发与零售。由于药品的特殊性&#xff0c;效期管理至关重要&#xff0c;但该企业一直面临效期问题的困扰。在未使用WMS系统之前&#xff0c;其药品入库、存储、出库等环节的效期管理主要依赖人工记录与检查。库…...