1、Spring Security登录认证的核心
Spring Security框架完成用户登录认证的核心就在与org.springframework.security.core.userdetails
包下的UserDetailsService
接口
2、UserDetailsService接口详解
2.1 UserDetailsService接口的定义
UserDetailsService接口只有一个抽象方法就是loadUserByUsername(String username),
UserDetailsService接口的返回值是UserDetails
接口, 这又是一个接口,Spring Security框架提供了它的实现类org.springframework.security.core.userdetails
包下的User
类对象
2.2 UserDetailsService接口的已有实现类有哪些
我们再看看UserDetailsService接口提供的默认的实现类
JdbcDaoImpl
(还有一个子类JdbcUserDetailsManager)InMemoryUserDetailsManager
2.2.1 UserDetailsService接口的实现类JdbcDaoImpl的实现
我们在分别看看UserDetailsService的实现类JdbcDaoImpl
和InMemoryUserDetailsManager
都是怎么实现这个loadUserByUsername(String username)方法的
我们在分别看看UserDetailsService的实现类JdbcDaoImpl
中loadUserByUsername(String username)方法的实现过程
下图主要就是验证一下createuserDetails方法的返回值是不是一个User对象,没有什么具体含义。
2.2.2 UserDetailsService接口的实现类InMemoryUserDetailsManager的实现
看看InMemoryUserDetailsManager
类的loadUserByUsername(String username)方法的具体实现
通过以上的源码解读, 我们可以大胆猜测一下一个是不是只要我们在我们自己的项目中定义一个自定义的实现类实现这个UserDetailsService,就可以完成我们自定义的登录认证逻辑, 接下来我们自己写一个实现类来尝试一下。
3、自定义实现类实现UserDetailsService接口
自定义实现类MyUserDetailsServiceImpl
packagecom.yige.service.impl;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.security.core.authority.AuthorityUtils;importorg.springframework.security.core.userdetails.User;importorg.springframework.security.core.userdetails.UserDetails;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.security.core.userdetails.UsernameNotFoundException;importorg.springframework.stereotype.Service;@ServicepublicclassMyUserDetailsServiceImplimplementsUserDetailsService{privatestaticfinalString USERNAME="test";privatestaticfinalString PASSWORD="test";@OverridepublicUserDetailsloadUserByUsername(String userName)throwsUsernameNotFoundException{if(!USERNAME.equals(userName)){thrownewUsernameNotFoundException("用户名不存在");}UserDetails userDetails=newUser(USERNAME,
PASSWORD,AuthorityUtils.commaSeparatedStringToAuthorityList("admin,common"));return userDetails;}}
重启项目看看,能不能生效。
登录输入用户名"test", 密码"test",发现页面刷新了一下又回到登录页面了, 怎么回事?
检查了一下后台程序,发现后台报错了,没有使用任何的PasswordEncoder,我们输入的密码没有用加密工具进行加密
Spring Security其实已经给我们提供了很多的PasswordEncoder, 我们来看一下
在org.springframework.security.crypto.password
包下有一个PasswordEncoder
接口,看看他的实现类
所有这里就很简单了啊,两种方式可以搞定:
- 我们只需要在我们的项目里面整一个Spring Security的配置类,把这个PasswordEncoder的任意一个我们需要用来加密密码的实现类的Bean注入到容器里面,就可以直接拿来使用
- 我们也可以自己自定义密码加密的实现类实现PasswordEncoder接口, 注入Bean到容器
这里我们采用第一种方式,人家已经实现了, 不需要重复造轮子,当然如果你确信你自己造的轮子更好就可以用你自己的。
packagecom.yige.config;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.config.annotation.SecurityBuilder;importorg.springframework.security.config.annotation.web.WebSecurityConfigurer;importorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;@ConfigurationpublicclassSecurityConfigextendsWebSecurityConfigurerAdapter{@BeanpublicBCryptPasswordEncodergetPasswordEncoder(){returnnewBCryptPasswordEncoder();}}
修改MyUserDetailsServiceImpl
类如下:
packagecom.yige.service.impl;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.security.core.authority.AuthorityUtils;importorg.springframework.security.core.userdetails.User;importorg.springframework.security.core.userdetails.UserDetails;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.security.core.userdetails.UsernameNotFoundException;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;importorg.springframework.stereotype.Service;/**
* @Author: karma
* @Date: 2022/3/21 0021 - 03 - 21 - 11:20
* @Description: com.yige.service.impl
* @version: 1.0
*/@ServicepublicclassMyUserDetailsServiceImplimplementsUserDetailsService{privatestaticfinalString USERNAME="test";privatestaticfinalString PASSWORD="test";@AutowiredprivateBCryptPasswordEncoder passwordEncoder;@OverridepublicUserDetailsloadUserByUsername(String userName)throwsUsernameNotFoundException{if(!USERNAME.equals(userName)){thrownewUsernameNotFoundException("用户名不存在");}returnnewUser(userName, passwordEncoder.encode(PASSWORD),AuthorityUtils.commaSeparatedStringToAuthorityList("admin,common"));}}
重启项目再次测试。
登录输入用户名"test", 密码"test",确认登录,
页面跳转到http://localhost:8080,说明我们的登录认证成功了。以上测试说明我们自定义的登录认证逻辑生效了,
4、测试结论
由以上测试我们可以得出结论,当我们自定义实现了UserDetailsService
逻辑并注入bean到容器之后, 项目再次启动之后,在进行登录认证的时候就会使用我们自定义的逻辑来进行处理, 不会在使用Spring Security框架默认的逻辑进行认证处理。
5、下一节内容
当前我们的实现都是把用户名、密码、权限都是硬编码在我们自己的代码中, 这样肯定是不行的, 下一节我们着重说说怎么让我们自定义的登录认证跟我们的数据库搭配使用,从数据库中获取用户的数据信息来完成登录认证的过程。