身份认证 - 2
之前我们的用户名密码配置在配置文件中的,而且密码也用的是明文,这明显不符合我们的要求,我们的用户必须是存储在数据库中,密码也是得经过加密的。所以我们先来解决这个问题,然后再去弄授权。
首先来插入一条用户数据,但这里有个问题,就是我们的密码怎么生成?密码怎么来的?这里我们使用Security内置了的BCryptPasswordEncoder,里面就有生成和匹配密码是否正确的方法,也就是加密和验证策略。因此我们再SecurityConfig中进行配置:
@Bean
BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
这样系统就会使用我们找个新的密码策略进行匹配密码是否正常了。之前我们配置文件配置的用户名密码去掉:
- application.yml
# security:
# user:
# name: user
# password: 111111
ok,我们先使用BCryptPasswordEncoder给我们生成一个密码,给数据库添加一条数据先,我们再TestController中注入BCryptPasswordEncoder,然后使用encode进行密码加密,对了,记得在SecurityConfig中吧/test/**添加白名单哈,不然访问会提示你登录!!
@Autowired
BCryptPasswordEncoder bCryptPasswordEncoder;
@GetMapping("/test/pass")
public Result passEncode() {
// 密码加密
String pass = bCryptPasswordEncoder.encode("111111");
// 密码验证
boolean matches = bCryptPasswordEncoder.matches("111111", pass);
return Result.succ(MapUtil.builder()
.put("pass", pass)
.put("marches", matches)
.build()
);
}
可以看到我密码是111111,加密以及验证的结果如下:$2a$10$R7zegeWzOXPw871CmNuJ6upC0v8D373GuLuTw8jn6NET4BkPRZfgK
data中的那一串字符串就是我们的密码了,可以看到marches也是true,说明密码验证也是正确的,我们添加到我们数据库sys_user表中:
INSERT INTO `vueadmin`.`sys_user` (`id`, `username`, `password`, `avatar`, `email`, `city`, `created`, `updated`, `last_login`, `statu`) VALUES ('1', 'admin', '$2a$10$R7zegeWzOXPw871CmNuJ6upC0v8D373GuLuTw8jn6NET4BkPRZfgK', 'https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg', '123@qq.com', '广州', '2021-01-12 22:13:53', '2021-01-16 16:57:32', '2020-12-30 08:38:37', '1');
后面我们就可以使用admin/111111登录我们的系统哈。
但是先我们登录过程系统不是从我们数据库中获取数据的,因此,我们需要重新定义这个查用户数据的过程,我们需要重写UserDetailsService接口。
@Slf4j
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
SysUserService sysUserService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser sysUser = sysUserService.getByUsername(username);
if (sysUser == null) {
throw new UsernameNotFoundException("用户名或密码不正确!");
}
return new AccountUser(sysUser.getId(), sysUser.getUsername(), sysUser.getPassword(), new TreeSet<>());
}
}
因为security在认证用户身份的时候会调用UserDetailsService.loadUserByUsername()方法,因此我们重写了之后security就可以根据我们的流程去查库获取用户了。然后我们把UserDetailsServiceImpl配置到SecurityConfig中:
@Autowired
UserDetailsServiceImpl userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
然后上面UserDetailsService.loadUserByUsername()默认返回的UserDetails,我们自定义了AccountUser去重写了UserDetails,这也是为了后面我们可能会调整用户的一些数据等。
public class AccountUser implements UserDetails {
private Long userId;
private String password;
private final String username;
private final Collection<? extends GrantedAuthority> authorities;
private final boolean accountNonExpired;
private final boolean accountNonLocked;
private final boolean credentialsNonExpired;
private final boolean enabled;
public AccountUser(Long userId, String username, String password, Collection<? extends GrantedAuthority> authorities) {
this(userId, username, password, true, true, true, true, authorities);
}
public AccountUser(Long userId, String username, String password, boolean enabled,
boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked,
Collection<? extends GrantedAuthority> authorities) {
Assert.isTrue(username != null && !"".equals(username) && password != null, "Cannot pass null or empty values to constructor");
this.userId = userId;
this.username = username;
this.password = password;
this.enabled = enabled;
this.accountNonExpired = accountNonExpired;
this.credentialsNonExpired = credentialsNonExpired;
this.accountNonLocked = accountNonLocked;
this.authorities = authorities;
}
public Long getUserId() {
return userId;
}
...
}
其实数据基本没变,我就添加多了一个用户的id而已。
ok,万事俱备,我们再次尝试去登录,看能不能登录成功。
1、获取验证码:
2、从控制台获取到对应的验证码
3、提交登录表单
4、登录成功,并在请求头中获取到了Authorization,也就是JWT。完美!!