Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,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 删除。

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Day6-李泽平-R包学习
install.packages(“包”)或者BiocManager::install(“包”)
用户11040033
2024/03/28
840
岩酱的生信学习笔记Day6(R包的安装和使用)
用户11074124
2024/04/18
1080
学习小组day6笔记-R包
all_of(): Matches variable names in a character vector. All names must be present, otherwise an out-of-bounds error is thrown.
清南
2023/04/13
3960
Day6 学习R包(今天依旧是干货满满但是要注重理解)
海雀
2024/03/09
1360
生信星球学习小组笔记 Day 6——LIU
其次,在Rstudio→Tools→Global options→Packages里面也能设置。
用户10360377
2023/12/03
1870
Day-6:学习R包
BiocManager::install(“包”)(R包来自Bioconductor)
用户11039713
2024/03/27
1070
Day6——R包的学习
菜单栏-Tools-Packages-Primary CRAN repository-选择国内镜像
掩扉先生
2023/10/25
2160
宏宏的学习笔记Day6 学习R包
mutate(test, new = Sepal.Length * Sepal.Width)
咕咚咕叽
2024/04/19
1610
day6-白雪
在这个过程中你可能会发现问题,例如下次在进到rstudio的话,查看镜像,又不在了,怎么办呢
用户10300557
2023/01/15
9360
DAY6-学习R包
用户10663401
2023/07/20
3170
Day 6_学习R包- CG
在我练习select()时,想选择刚新增的列,发现报错。然后发现运行mutate(test, new = Sepal.Length*Sepal.Width)后,查看test后发现test本身没有变。
Crazy_George
2024/03/28
1640
Day-6 香波🐟
6.简单合并:在相当于base包里的cbind()函数和rbind()函数;注意,bind_rows()函数需要两个表格列数相同,而bind_cols()函数则需要两个数据框有相同的行数
波小囡
2023/12/02
1960
Day-6 香波🐟
Day7-R语言综合运用
lapply(list, FUN, …) :对列表/向量中的每个元素实施相同的操作
用户11008504
2024/05/08
1210
学习R包
R包安装命令是install.packages(“包”)或者BiocManager::install(“包”)。取决于你要安装的包存在于CRAN网站还是Biocductor。
用户10845197
2023/11/29
1920
Learn R 专题1-3
图片 专题1 玩转字符串 图片 检测字符串长度 代码1 str_length(x) if(!require(stringr))install.packages('stringr') library(stringr) x <- "The birch canoe slid on the smooth planks." x [1] "The birch canoe slid on the smooth planks." str_length(x) [1] 42 ### 1.检测字符串长度,包含空格和符号 le
用户10412487
2023/03/28
1.5K0
技能树Day03_直播课05-06_R作图与综合利用
属性设置:映射:根据数据的某一列的内容分配颜色;统一设置:把图形设置为一个颜色,与数据无关
sheldor没耳朵
2024/07/19
1280
技能树Day03_直播课05-06_R作图与综合利用
生信星球学习小组Day6-R包学习 Jerry
(1)在Rstudio程序设置中设置,可以用options()$repos来检验,但有时候还是不能成功,也不能下载Bioconductor的包
jerry早点睡
2023/08/12
2470
生信技能树笔记day7
---title: "生信技能树学习笔记"author: "天空"date: "2023-01-04"output: html_document---R语言综合应用1. 玩转字符串图片(1) str_length()图片library(stringr)x <- "The birch canoe slid on the smooth planks."x## [1] "The birch canoe slid on the smooth planks."### 1.检测字符串长度str_length(x)##
天空UP
2023/01/04
5740
生信学习小组Day6笔记—Chocolate Ice
首先用file.edit('~/.Rprofile')打开.Rprofile文件;然后在.Rprofile文件内添加下列两行代码
Chocolate Ice
2023/01/16
8070
Day6——R包
用户10859034
2023/12/03
2210
相关推荐
Day6-李泽平-R包学习
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验