实现登录认证功能
SpringBoot项目引入SpringSecurity安全框架后,访问接口会自动跳转到登录页面,这个页面的账号是默认固定为user,密码会在控制台打印。
实际开发中我们肯定会到数据库中进行账号密码比对,因此我们需要实现SpringSecurity给我们提供的方法。但是这个User对象是我们自己定义的,SpringSecurity是肯定不认识的,因此我们需要对User对象进行简单的处理。
# 处理User对象
User对象一般是和数据库相关联的,为了减少集成SpringSecurity对我们代码的影响,我们新建一个LoginUser类。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginUser implements UserDetails {
private User user;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
// 帐户是否过期
return true;
}
@Override
public boolean isAccountNonLocked() {
// 是帐户是否锁定
return true;
}
@Override
public boolean isCredentialsNonExpired() {
// 凭证是否过期
return true;
}
@Override
public boolean isEnabled() {
// 账号是否启用
return true;
}
}
代码分析:
实现UserDetails相当于让SpringSecurity认识了这个User对象,getAuthorities是保存权限码的,这个我们到鉴权相关的篇章再说。
getUsername
和getPassword
分别代表账号和密码剩下的四个都为true才代表当前用户可用,只要有一个为false都代表登录失败,可以根据自己的项目映射相关字段。
# 让SpringSecurity实现从数据库查询对象
我用ArrayList模拟用户数据,findUserByUsername方法相当于从数据库查询对象,loadUserByUsername是实现SpringSecurity接口提供的方法,实现自定义用户信息,而不是框架默认的user用户。
@Service
public class UserDetailServiceImpl implements UserDetailsService {
public static List<User> userList = new ArrayList<User>() {{
add(new User("1", "user1", "123456", "用户一"));
add(new User("2", "user2", "123456", "用户二"));
add(new User("3", "user3", "123456", "用户三"));
add(new User("4", "user4", "123456", "用户四"));
add(new User("5", "user5", "123456", "用户五"));
}};
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 查询用户信息
User user = findUserByUsername(username);
// 如果找不到用户则抛出异常
if (user == null) {
throw new RuntimeException("用户名或密码错误");
}
//TODO 根据用户查询权限信息 添加到LoginUser中
return new LoginUser(user);
}
/**
* 根据用户名查询用户
* @param username 用户名
*/
public static User findUserByUsername(String username) {
for (User user : userList) {
if (user.getUsername().equals(username)) {
return user;
}
}
return null;
}
}
SpringSecurity默认会对密码进行加解密进行比对,所以在登录页面输入密码为123456发现提示用户或密码错误。
# 配置密码加密存储
实际项目中我们不会把密码明文存储在数据库中,默认使用的PasswordEncoder要求数据库中的密码格式为:{id}password 。它会根据id去判断密码的加密方式。但是我们一般不会采用这种方式。所以就需要替换PasswordEncoder。
我们一般使用SpringSecurity为我们提供的BCryptPasswordEncoder,只需要使用把BCryptPasswordEncoder对象注入Spring容器中,SpringSecurity就会使用该PasswordEncoder来进行密码校验。
我们可以定义一个SpringSecurity的配置类,SpringSecurity要求这个配置类要继承WebSecurityConfigurerAdapter。
@Configuration
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
加密解密测试类,encode获取密码加密,matches进行密码比对。
@Slf4j
@SpringBootTest
class JenkinsDemoApplicationTests {
@Autowired
private PasswordEncoder passwordEncoder;
@Test
void contextLoads() {
log.info("密码加密:{}", passwordEncoder.encode("123456"));
log.info("比对结果:{}", passwordEncoder.matches("123456", "$2a$10$7B9Ie1iy.50PecAACzE………………"));
}
}
我们修改模拟的"数据库",然后重启一下项目,访问页面输入账号密码,发现已经能够正常访问了。
public static List<User> userList = new ArrayList<User>() {{
add(new User("1", "user1", "$2a$10$7B9Ie1iy.50PecAACzEAEOJ3m5WlkAJ7S0MHjOiLiTuKRvTdMYYYW", "用户一"));
add(new User("2", "user2", "$2a$10$7B9Ie1iy.50PecAACzEAEOJ3m5WlkAJ7S0MHjOiLiTuKRvTdMYYYW", "用户二"));
add(new User("3", "user3", "$2a$10$7B9Ie1iy.50PecAACzEAEOJ3m5WlkAJ7S0MHjOiLiTuKRvTdMYYYW", "用户三"));
add(new User("4", "user4", "$2a$10$7B9Ie1iy.50PecAACzEAEOJ3m5WlkAJ7S0MHjOiLiTuKRvTdMYYYW", "用户四"));
add(new User("5", "user5", "$2a$10$7B9Ie1iy.50PecAACzEAEOJ3m5WlkAJ7S0MHjOiLiTuKRvTdMYYYW", "用户五"));
}};