前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >SpringBoot集成SpringSecurity - 异常处理(三)

SpringBoot集成SpringSecurity - 异常处理(三)

作者头像
用户1212940
发布于 2022-04-13 09:05:54
发布于 2022-04-13 09:05:54
1.2K00
代码可运行
举报
文章被收录于专栏:LambdaLambda
运行总次数:0
代码可运行

源码地址:https://github.com/springsecuritydemo/microservice-auth-center03

当我们登录失败的时候,SpringSecurity 帮我们跳转到了 /login?error URL,奇怪的是不管是控制台还是网页上都没有打印错误信息。

这是因为首先 /login?error 是SpringSecurity 默认的失败 URL,其次如果你不自己处理这个异常,这个异常时不会被处理的。

一、常见异常

我们先来列举下一些 SpringSecurity 中常见的异常:

  • UsernameNotFoundException (用户不存在)
  • DisableException(用户已被禁用)
  • BadCredentialsException(坏的凭据)
  • LockedException(账号锁定)
  • CerdentialsExpiredException(证书过期)
  • ... 以上列出的这些异常都是 AuthenticationException 的子类,然后我们看 SpringSecurity 是如何处理 AuthenticationException 异常的。

二、源码分析

SpringSecurity的异常处理是在过滤器中进行的,我们在 AbastrctAuthenticationProcessingFilter 中找到了对 Authentication 的处理:

  • 在 doFilter() 中,捕获 AuthenticationException 异常,并交给 unsuccessfulAuthentication() 处理。
  • unsuccessfulAuthentication() 中,转交给了 SimpleUrlAuthenticationFailureHandler 类的 onAuthencicationFailure() 处理。
  • 在 onAuthenticationFailure() 中,首先判断有没有设置 defaultFailureUrl。 a. 如果没有设置,直接返回 401 错误,即 HttpStatus.UNAUTHORIZED 的值。 b. 如果设置了,首先执行 saveException() 方法。然后判断 forwardToDestination 是否为服务器调整,默认使用重定向即客户端跳转。
  • 在 saveException() 方法中,首先判断 forwardToDestination,如果使用服务器跳转则写入Request,客户端跳转则写入 Session。写入名为 WebAttributes.AUTHENTICATION_EXCEPTION 常量对应值SPRING_SECURITY_LAST_EXCEPTION,值为 AuthenticationException 对象。

至此 SpringSecurity 完成了异常处理,总结下流程:

–> AbstractAuthenticationProcessingFilter.doFilter() –> AbstractAuthenticationProcessingFilter.unsuccessfulAuthentication() –> SimpleUrlAuthenticationFailureHandler.onAuthenticationFailure() –> SimpleUrlAuthenticationFailureHandler.saveException()

三、处理异常

上面通过源码看着挺复杂,但真正处理起来SpringSecurity为我们提供了方便的方式,我们只需要指定错误的url,然后在该方法中对异常进行处理即可。

  • 指定错误url ,在WebSecurityConfig 中添加 .failureUrl("/login/error")
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                // 如果有允许匿名的url,填在下面
//                .antMatchers().permitAll()
                .anyRequest().authenticated()
                .and()
                // 设置登陆页
                .formLogin().loginPage("/login")
                // 设置登陆成功url
                .defaultSuccessUrl("/").permitAll()
                // 设置登录失败url
                .failureUrl("/login/error")
                // 自定义登陆用户名和密码参数,默认为username和password
//                .usernameParameter("username")
//                .passwordParameter("password")
                .and()
                .logout().permitAll()
                // 自动登录
                .and().rememberMe()
                .tokenRepository(persistentTokenRepository())
                // 有效时间,单位:s
                .tokenValiditySeconds(60)
                .userDetailsService(userDetailsService);

        // 关闭CSRF跨域
        http.csrf().disable();
    }
  • 在 Controller 中编写 loginError方法完成异常处理操作:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 @GetMapping("/login/error")
    @ResponseBody
    public Result loginError(HttpServletRequest request) {
        AuthenticationException authenticationException = (AuthenticationException) request.getSession().getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
        log.info("authenticationException={}", authenticationException);
        Result result = new Result();
        result.setCode(201);

        if (authenticationException instanceof UsernameNotFoundException || authenticationException instanceof BadCredentialsException) {
            result.setMsg("用户名或密码错误");
        } else if (authenticationException instanceof DisabledException) {
            result.setMsg("用户已被禁用");
        } else if (authenticationException instanceof LockedException) {
            result.setMsg("账户被锁定");
        } else if (authenticationException instanceof AccountExpiredException) {
            result.setMsg("账户过期");
        } else if (authenticationException instanceof CredentialsExpiredException) {
            result.setMsg("证书过期");
        } else {
            result.setMsg("登录失败");
        }
        return result;
    }

四、运行项目

首先我们修改 CustomUserDetailsService loadUserByUsername() 方法的返回值:

  1. 输入错误的用户名或密码:
  1. 修改返回值:enable 为 false
  1. 修改返回值:accountNonExpired 为 false
  1. 修改返回值:credentialsNonExpired 为 false
  1. 修改返回值:accountNonLocked 为 false

五、存在问题

细心的同学再完成上面功能是会发现,当我们输入的用户名不存在时,不会抛出UserNameNotFoundException,而是抛出 BadCredentialsException这个异常,如果有需要区分 用户名不存在和密码错误的,可参考https://blog.csdn.net/wzl19870309/article/details/70314085

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019/07/24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
WPF 使用 Silk.NET 进行 DirectX 渲染入门
本文告诉大家如何使用 dotnet 基金会新开源的 Silk.NET 库调用 DirectX 进行渲染的方法。此库是对 DirectX 的底层基础封装,用上了 dotnet 和 C# 的各个新特性,相对来说基础性能较好,也许后续可以考虑作为 SharpDx 的代替
林德熙
2021/12/27
3.1K0
WPF 使用 Silk.NET 进行 DirectX 渲染入门
WPF 模拟 WPFMediaKit 的 D3D 配置用来测试4k性能
本文告诉大家我在测试 WPFMediaKit 的 D3D 配置性能影响在 4k 分辨率设备下采用高清摄像头的性能
林德熙
2021/12/23
1.1K0
ImGui基于DX11加载/显示图片教程
关于Imgui显示图片,在网上搜到的教程都不符合我的DX11下显示图片的要求故写此文
晨星成焰
2024/08/15
7180
ImGui基于DX11加载/显示图片教程
WPF 渲染原理
在 WPF 最主要的就是渲染,因为 WPF 是一个界面框架。想用一篇博客就能告诉大家完整的 WPF 渲染原理是不可能的。本文告诉大家 WPF 从开发者告诉如何画图像到在屏幕显示的过程。本文是从一个很高的地方来看渲染的过程,在本文之后会添加很多博客来告诉大家渲染的细节。
林德熙
2018/09/19
3K0
WPF 渲染原理
Nebula3竟然秘密更新了
下载地址: http://213.191.63.156/n3_sdks/ 之前所有更新都会在floh的blog上有一篇changelog, 这回一点也没, 连个更新的消息都没. 看了下变化挺大的, 不
逍遥剑客
2018/05/23
4380
原创Paper | DirectX Hook - 优雅的实现游戏辅助窗口
最近看到了一个github的项目,分析过后觉得里面无论是代码还是界面都很好看,然后开始研究其代码。
Seebug漏洞平台
2023/01/08
4.6K2
原创Paper | DirectX Hook - 优雅的实现游戏辅助窗口
WPF 使用 SharpDx 异步渲染
本文告诉大家如何通过 SharpDx 进行异步渲染,但是因为在 WPF 是需要使用 D3DImage 画出来,所以渲染只是画出图片,最后的显示还是需要 WPF 在他自己的主线程渲染。
林德熙
2022/08/04
1.2K0
WPF 使用 SharpDX
先介绍一下 SharpDx ,一个底层封装的 DirectX 库,支持 AnyCpu ,支持 Direct3D9, Direct3D11, Direct3D12,Direct2D1。支持 win32 程序和商店程序。
林德熙
2018/09/19
1.4K0
WPF 使用 SharpDX
Windows下ImGui的快速使用介绍至项目建立(VS,DX11)
本文建立在基于DX11下API的ImGui使用,如果是别的图形API我还未尝试,故其他API相关请参考阅读,并不一定适用
晨星成焰
2024/06/20
1.9K0
Windows下ImGui的快速使用介绍至项目建立(VS,DX11)
WPF 对接 Vortice 调用 D2D 使用 IWICBitmap 离屏渲染
通过 Vortice 库可以使用非常底层的方式调用到 Direct2D1 进行渲染,本文将使用 D2D 离屏渲染到 IWICBitmap 上,再使用一点点反射黑科技,直接将此 IWICBitmap 对接到 WPF 框架里。本文提供的这个方法可以实现极高性能且只有很少的转换损耗的离屏渲染方式,唯一的一个缺点是需要进行一点反射调用,适合用来静态画面渲染上
林德熙
2023/04/07
5370
C/C++ ImGUI劫持Dx9绘制窗体
ImGUI 是一个无任何第三方依赖的图形化界面组件,其支持多种绘图引擎,ImGUI可用于绘制辅助菜单功能,注入游戏内部方便快捷。
王瑞MVP
2022/12/28
9990
C/C++ ImGUI劫持Dx9绘制窗体
dotnet 读 WPF 源代码笔记 为什么加上 BooleanBoxes 类
在 WPF 框架,为什么需要定义一个 BooleanBoxes 类。为什么在 D3DImage 的 Callback 方法里面,传入的是 object 对象,却能被转换为布尔。本文将告诉大家为什么需要这样设计
林德熙
2022/08/12
6820
dotnet 读 WPF 源代码笔记 为什么加上 BooleanBoxes 类
WPF 使用封装的 SharpDx 控件
上一篇告诉大家如何在 WPF 使用 SharpDx ,看起来代码比较复杂,所以本文告诉大家如何使用我封装的控件。
林德熙
2018/09/18
1.7K0
WPF 使用 Composition API 做高性能渲染
在 WPF 中很多小伙伴都会遇到渲染性能的问题,虽然 WPF 的渲染可以甩浏览器渲染几条街,但是还是支持不了游戏级的渲染。在 WPF 使用的 DX 只是优化等级为 9 和 DX 9 差不多的性能,微软在很多开发者的提议开放了现代渲染方法 Composition API 这是 UI 应用的里程碑的技术
林德熙
2019/04/22
2.1K2
WPF 使用 Composition API 做高性能渲染
WPF 同一窗口内的多线程/多进程 UI(使用 SetParent 嵌入另一个窗口)
发布于 2018-07-11 13:35 更新于 2018-07-12 11:44
walterlv
2018/09/18
4.4K0
WPF 同一窗口内的多线程/多进程 UI(使用 SetParent 嵌入另一个窗口)
关于WPF空域的问题
1.Microsoft.DwayneNeed 怎么说呢 ,这个库我个人没觉得有多好用,因为视频小窗口特别多,用这个巨卡无比
tangmanger
2021/09/02
1.7K0
关于WPF空域的问题
WPF中WindowFormsHost始终置顶的有效解决方案
WPF的原生控件并不具备自身的句柄,即使使用偏门的方式获取的结果也都是控件所在窗体的句柄,并不代表该控件本身的资源,这是由WPF的自身的机制决定的。
zls365
2020/12/15
2.6K0
解决 WPF 嵌套的子窗口在改变窗口大小的时候闪烁的问题
因为 Win32 的窗口句柄是可以跨进程传递的,所以可以用来实现跨进程 UI。不过,本文不会谈论跨进程 UI 的具体实现,只会提及其实现中的一个重要缓解,使用子窗口的方式。
walterlv
2023/10/22
8920
解决 WPF 嵌套的子窗口在改变窗口大小的时候闪烁的问题
WPF 自定义文本框输入法 IME 跟随光标
本文非小白向,本文适合想开发自定义的文本框,从底层开始开发的文本库的伙伴。在开始之前,期望了解了文本库开发的基础知识
林德熙
2022/03/15
1.9K0
浅谈window桌面GUI技术及图像渲染性能测试实践
从Windows Vista之后,desktop composition的部分就由Desktop Window Manager完成了(当然是启用Aero的情况下,Windows 8起DWM是必须开启的)
高楼Zee
2019/07/17
4K0
浅谈window桌面GUI技术及图像渲染性能测试实践
推荐阅读
相关推荐
WPF 使用 Silk.NET 进行 DirectX 渲染入门
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验