Spring Boot + Spring Security解决UsernameNotFoundException无法被捕获的问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot + Spring Security解决UsernameNotFoundException无法被捕获的问题相关的知识,希望对你有一定的参考价值。

参考技术A 以下配置基于spring boot版本1.4.2.RELEASE,默认引入的spring security版本为4.1.3.RELEASE,页面模板采用thymeleaf。

在MyUserDetailsService实现了UserDetailsService接口以后,在重写的loadUserByUsername方法里验证用户名不存在时,我们会抛出一个UsernameNotFoundException异常,比如:

但是返回页面以后, 发现并不能捕获这个异常信息,通过[[$session.SPRING_SECURITY_LAST_EXCEPTION.message]]方式获得的异常信息始终是“Bad credentials”。

通过程序调试发现,系统在执行到throw new UsernameNotFoundException("用户名不存在")的时候,会执行DaoAuthenticationProvider类的retrieveUser方法:

在这个方法会捕获UsernameNotFoundException异常,继续往下调试,会执行到父抽象类AbstractUserDetailsAuthenticationProvider的authenticate方法:

在这里会继续捕获到UsernameNotFoundException异常。
由于hideUserNotFoundExceptions的值为true,所以这里会new一个新的BadCredentialsException异常抛出来,那么最后捕获到并放入session中的就是这个BadCredentialsException异常。
所以我们在页面始终无法捕获我们自定义的异常信息。

这里提供两个解决方案,当然可能不是最好的,希望各位同学能够给出更好的解决方案。

第一个方案:
1.既然系统是捕获UsernameNotFoundException类型的异常后再抛出新的BadCredentialsException异常,那么我们干脆就不抛出UsernameNotFoundException异常。
我们模仿UsernameNotFoundException,创建自己的MyUsernameNotFoundException异常类。

2.在MyUserDetailsService类的loadUserByUsername方法抛出我们自己定义的MyUsernameNotFoundException异常。

第二个方案:
在MyUserDetailsService类的loadUserByUsername方法直接抛出BadCredentialsException异常,这样就不需要创建自己的MyUsernameNotFoundException异常类。

好了,这样在页面通过[[$session.SPRING_SECURITY_LAST_EXCEPTION.message]],就能显示我们自定义的异常信息了。

初识Spring Boot

Spring Boot是什么?

Spring Boot 基于Spring,为了解决Spring框架时配置繁多、部署流程复杂、开发效率低等问题。如果说Spring 目标是简化Java开发,那么可以认为Spring Boot 框架的目标是简化Spring的开发。 Spring Boot 可以创建独立程序,内嵌了tomcat、jetty等,可以直接启动应用程序而不需要外部的容器。同时,Spring boot 可以自动配置Spring应用,并且将一些框架的依赖包整合起来,如开发web程序只需要引入web的starter,极大的简化了包引用。从Spring创建以来,Spring Boot大概是Spring领域中最令人兴奋的事情了。它在Spring之上构建了全新的开发模型,移除了开发Spring应用中乏味的内容。

Spring Boot提供了Spring Boot Starter、自动配置、命令行接口、Actuator四个主要特性,改变了开发Spring 应用程式的方式。

Spring Boot Starter:它将常用的依赖分组进行了整合,将其合并到一个依赖中,这样就可以一次性添加到项目的Maven或Gradle的构建中;

自动配置:Spring Boot的自动配置特性利用了Spring 4 对条件化配置的支持,合理的推测应用所需的bean自动化的配置它们。

命令行接口(Commond-line interface,CLI):Spring 的CLI发挥了Groovy编程语言的优势,并结合自动配置进一步简化Spring应用的开发;

Actuator:它为Spring Boot应用添加了一定的管理特性。

Spring Boot Starter(依赖的传递性)

Spring boot Satarter将应用所需的各种依赖聚合成一项依赖。它的工作方式使用了Maven和Gradle的依赖传递方案,Starter在自己的Pom.xml文件中声明了多个依赖。当我们将某一个Starter依赖添加到Maven或Gradle构建中的时候,Starter的依赖将会自动地传递性解析。这些依赖可能会也有其他依赖。一个Starter可能会传递性地引入几十个依赖。

自动配置

自动配置功能消减了Spring配置的数量,使用了约定优于配置的理念,大大的减少了Spring中的一些配置。它在实现时会考虑应用中的其他因素并推断出你所需要的Spring配置。

场景1:将Thymeleaf模板作为Spring MVC的视图,至少需要三个bean:ThymeleafView-Resolver,  SpringTemplateEngine,TemplateResolver。但是使用Spring Boot自动配置的话,我们需要做的仅仅是将Thymeleaf添加项目的类路径中。如果Spring Boot探测Thymeleaf位于类路径中,它将会推断出我们需要使用Thyemeleaf实现Spring Mvc的视图功能,并自动配置这些bean.

场景2:Spring boot Starter也会触发自动配置。在Spring Boot应用中,如果我们想要使用Spring MVC的话,所需要做的仅仅是将Web Starter作为依赖放到构建之中。将Web Starter作为依赖放到构建中以后,它会自动添加Spring MVC依赖(前面提到的依赖传递性)。如果Spring Boot 的Web 自动配置探测到Spring MVC 位于类路径下,它将会自动配置支持Spring MVC的多个bean,包括视图解析器、资源处理器以及消息 转换器等等。我们接下来需要做的就是编写处理请求的控制器。如果你之前从头配置过一个Spring MVC 项目,你会理解这带来的效率。

Spring Boot CLI

spring Boot CLI充分使用了Spring Boot Starter和自动配置的魔力,并添加一些Groovy的功能,它简化了Spring 的开发流程,通过CLI,我们能运行一个或多个Groovy脚本,并查看它是如何运行的。在应用运行过程中,CLI能够自动导入Spring类型并解析依赖。

有一种实践,单元测试代码通过Groovy编写,带来效率的提升。

Actuator

Spring Boot Actuator 为Spring Boot项目带来了很多有用的特性,包括

  • 管理端点

  • 合理的异常处理以及默认的“/error"映射端点

  • 获取应用的”/info"端点

  • 当启用Spring Security时会有一个审计事件框架

这些特性很有用的,但Actuator最有用和最有意思的是管理端点,它开启了一扇窗,能够让我们洞悉内部的应用运行状况。

以上是Spring Boot的主要四个特性介绍,Spring Boot 的Starter减少了依赖列表的长度,自动配置功能则消减了Spring配置的数量。Spring Boot CLI 通过与Groovy结合带来了开发测试效率的提升。

Spring Boot Actuator 则提供了一个我们能监控应用内部的运行状况的窗口。内嵌tomcat、jetty等容器,可直接启动应用程序而不需要外部的容器这些特性又为开发、调试运行和项目部署时带来巨大的便利和效率上的提升,Spring Boot 为开发,测试,部署,运维等层面带来了巨大变化,大大的简化了这些方面的复杂性,带来了效率的大幅提升,也为后面的微服务提供了基础。

以上是关于Spring Boot + Spring Security解决UsernameNotFoundException无法被捕获的问题的主要内容,如果未能解决你的问题,请参考以下文章

UnsatisfiedDependencyException:创建名为“securityConfig”的 bean 时出错

SpringSecurity基于数据库认证

spring security 1

Spring securiuty 过滤器

Spring Boot 学习例子

Spring Boot 2Spring Boot CLI