前言
最近在跟着尚硅谷的视频看spring-security框架的教程,视频传送门
在讲认证的时候,老师讲了三种方式
- 在application.yml或application.properties配置文件中配置用户名和密码
- 通过配置类配置用户名和密码
- 通过实现org.springframework.security.core.userdetails.UserDetailsService接口, 从数据库中查询用户,从而确定是否能进入
前两种直接写死的情况,在正常的项目中用得可能比较少,用得比较多的可能还是第三种,从数据库中查询的方式,
视频中老师为了演示这种功能,起先没有从数据库中读取,而是在实现接口的地方直接new了一个org.springframework.security.core.userdetails.User对象,表示这个用户能够登陆到系统
但是问题来了:
我发现,这样写之后,只要密码对了,用户名无论怎么输,都是能进入到系统中,我看视频中好多小伙伴也发弹幕说有这样的情况。老师好像没有发现这个情况,或者是觉得这个问题很简单,所以压根没有提。
分析
我当时就很懵,然后打了个断点调式,恍然大悟,
我用户名输入的1111,密码是正确的
我们在这个loadUserByUsername方法中,做了什么事情?返回了一个设置了用户名、密码、权限集合的org.springframework.security.core.userdetails.User对象,所以我们理所当然的认为:应该用户名和密码都与我们设置的相匹配,才能进入到系统。但我们仔细看这个方法的名称:loadUserByUsername,见名知意,这个方法是通过用户名加载用户,结合我们的断点,方法的参数String,的确是将我们的用户名传过来了。
那这又能说明什么呢?说明spring-security的登录判断逻辑和我们常理思考的不一样,我们常理认为,登录是通过用户名和密码去查询数据库,能查出来,就表示能登录,否则不能。而spring-security是怎么做的呢?他是让我们通过用户名去数据库中查询用户,然后把查询出来的用户信息封装成他要求的对象(rg.springframework.security.core.userdetails.User),返回给他,他再去比对密码。按照正常情况,这样做是不会出问题的,我们做用户名的比对(根据用户名去数据控中查),spring-security做密码的比对。但是现在情况是:我们没有根据用户名去查询数据,而是直接返回给了spring-security一个我们自己new的一个用户。所以就是说我们没有做用户名的比对,spring-security又是只做的密码比对,所以无论用户名是什么,只要密码正确,都能进入到系统。
不知道我描述清楚了没有,如果非要用户名和密码都正确的话,我们可以自己验证一下用户名
这样当我再次输入其他用户名时
总结
其实这个问题简单来说就是:spring-security把用户名判断的工作交给了使用者,他自己只做密码判断,使用者在通过用户名去数据库中查询用户时,就是在判断用户名。恰好我们没有通过数据库查询,而且我们没有意识到,spring-security把用户名判断的工作交给了我们,所以就出现了这个问题。
如果spring-security明确的告诉了我们,要我们自己判断用户名,那我们肯定知道判断用户名。但是他并没有,而我们也没有意识到:让我们通过用户名查询用户,其实就是在让我们判断用户名。
我们经常看视频学东西,跟着视频敲,还是缺少了自己的思考。其实根据loadUserByUsername这个方法名字,我们就应该意识到。