上一篇:HTTPbasic和表单的认证、基本原理、源码分析
下一篇:https://blog.csdn.net/LawssssCat/article/details/105299270
自定义用户认证逻辑:
- 处理用户信息获取逻辑
- 处理用户校验逻辑
- 处理密码加密解密
处理用户信息获取逻辑
指定从哪里获取用户信息(数据库?缓存?第三方REST API?)
Spring Security 提供了一个接口(UserDetailsService) 处理用户信息获取
这个接口也非常简单,就是传入用户名,返回一个类 UserDetails (封装用户信息),其次会抛出异常 UsernameNotFoundException
下面,我们实现这个接口
创建接口实现 MyUserDetailsService
package cn.vshop.security.browser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import java.util.Collection;
/** * @author alan smith * @version 1.0 * @date 2020/4/3 17:05 */
@Slf4j
// 注入 spring 容器
@Component
public class MyUserDetailsService implements UserDetailsService {
// @Autowired
//private DAO 对象 .... 这里就直接模拟了
/** * 根据用户名查找用户信息,作为登录的认证的依据 * 因为在spring环境中,查找信息的方式只需要注入即可 * * @param username 用户要的认证的用户名 * @return 认证依据的详细信息 * @throws UsernameNotFoundException 没有 username 对应的用户信息 */
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("登录用户名:{}", username);
// 模拟:查出来的用户密码
String password = "123456";
// 授权信息,告诉SpringSecurity,当前用户一旦认证成功,拥有哪些权限
Collection<? extends GrantedAuthority> authorities = AuthorityUtils
// 一个工具,把字符串以空格隔开,分别存储为权限
.commaSeparatedStringToAuthorityList(
// 模拟从数据库读出一下权限
"admin user");
// 返回UserDetails接口
// User是SpringSecurity提供的UserDetails接口实现
return new User(username, password, authorities);
}
}
保存,重启项目
访问登录
重定向,登录界面,登录
<mark>注意,这时,密码已改为我们写死的 123456 (用户名随意)</mark>
ok!
如果这时候,密码写错,结果如下
处理用户校验逻辑
做这个前,需要先了解上面用到 UserDetails , 我们进入到其源码
package org.springframework.security.core.userdetails;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import java.io.Serializable;
import java.util.Collection;
public interface UserDetails extends Serializable {
// 权限信息
Collection<? extends GrantedAuthority> getAuthorities();
// 密码
String getPassword();
// 用户名
String getUsername();
//--------------------- 下面四个布尔值可用来执行用户校验逻辑 --------------
// 账户是否过期
boolean isAccountNonExpired();
// 账户是否被锁定
boolean isAccountNonLocked();
// 用户密码过期
boolean isCredentialsNonExpired();
// 账户是否可用(是否被删除了)
boolean isEnabled();
}
需要说明的是:isAccountNonLocked 和 isEnabled 的对比
Spring Security 并没有明确指出两个的使用场景的不同。
但实际开发中,通常如下指定业务场景
- isAccountNonLocked 用户是否被冻结了(一般可恢复)
- isEnabled 用户是否被删除了(一般不可恢复)
因此,我们如果要进行用户校验逻辑的业务,只需要在代码中加上相关判断即可。
简单使用如下
修改上面代码中,最后返回的User的构造(使用7个参数的构造)【我们这时候把账号设置成了冻结】
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("登录用户名:{}", username);
// 模拟:查出来的用户密码
String password = "123456";
// 授权信息,告诉SpringSecurity,当前用户一旦认证成功,拥有哪些权限
Collection<? extends GrantedAuthority> authorities = AuthorityUtils
// 一个工具,把字符串以空格隔开,分别存储为权限
.commaSeparatedStringToAuthorityList(
// 模拟从数据库读出一下权限
"admin user");
// 返回UserDetails接口
// User是SpringSecurity提供的UserDetails接口实现
return new User(
username,
password,
// 账号是否被删除
true,
// 账号是否过期
true,
// 密码是否过期
true,
// 账号是否被冻结
false,
authorities);
}
启动后,再次登录的结果如下
处理密码加密解密
Spring Security 了专门的加密解密的类 ,进行加密解密非常简单,下面我们只需要介绍相关的 接口 和 工具api 即可
接口名:PasswordEncoder
<mark>org.springframework.security.crypto.password</mark> 包下的
接口 PasswordEncoder 实现
有以下六个实现
具体的区别参考:加密算法:PBEncrypt(hash消息摘要:MD5、SHA;对称加密:DES、AES;非对称加密:RSA)
下面用 BCryptPasswordEncoder
BCrypt 其实就还是 hash 散列编码,可以指定 加密次数、加密盐
在注册类中注册加密器
package cn.vshop.security.browser;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/** * spring security 提供的 web 应用适配器 * * @author alan smith * @version 1.0 * @date 2020/4/3 12:15 */
@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 指定身份认证方式为表单
http.formLogin()
.and()
// 并且认证请求
.authorizeRequests()
// 全部请求,都需要认证
.anyRequest().authenticated();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
<mark>正常这样就可以了,但是由于我们现在认证的密码部分用的是明文,而要求的是密文,因此本例中还需要在 MyUserDetailsService 中对模拟出来的密码明文进行加密</mark>
对模拟的明文进行加密
接下来,正常登陆即可
登陆,查看日志
可以看到加密后的密码
如果说 账号已锁定,把User那里改回来即可