Spring Security-2-自定义用户登录

上一个案例中,我们简单的实现了用户的登录,用的是Spring Security的默认的用户名和密码, 实际情况中,我们肯定是要从数据库里面去获取用户信息,然后接下来就看一下具体的要如何实现

大致流程

登录就是用户名,密码还有一些其他的校验, 上文中对认证源码有了一些大致的了解,我们自定义登录主要就是实现从数据库里面去,回顾一下这个部分的源码:
image

这里调用了一个 retrieveUser(...) 这个方法, 返回的是一个UserDetails, 这个需要我们自己去实现 UserDetailsService, 在这个里面去查询数据库

首先,看一下 UserDetailsService,这个类的源码, 如下:
image

很简单,就是一个接口,里面有一个根据用户名去获取用户信息的方法,然后我们新建一个类,实现这个接口,然后重写方法就可以,这里返回的是一个 UserDetails 对象,这里直接用了security提供的一个user对象,当然也可以自己实现,来看一下他里面的几个属性

image

首先是个权限的集合,然后是密码,用户名,是否非过期, 是否非锁定,凭证是否非过期,是否启用.

这下面四个boolean值,只要有一个设置为false,就不能登录

手动实现用户验证

然后再看一下我们的代码,新建类,实现 UserDetailsService ,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Component
public class MyUserDetailsService implements UserDetailsService {

@Autowired
private UserService userService;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 这里是模拟从数据库查询用户
User user = userService.findUserByName(username);

if (user == null) {
throw new UsernameNotFoundException("用户不存在");
}

// 查询出来,返回去
return new org.springframework.security.core.userdetails.User(
username,
user.getPassword(),
user.getEnable(),
true,
true,
!user.getLocked(),
AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
}
}

先是根据用户名从数据库里面查询出用户信息来,然后根据自己的情况把4个Boolean值赋值,然后最后一个集合是权限,这里先随便给个值

PasswordEncoder

在Spring Security中, 验证密码是否正确, 这个事情是交给Spring Security去完成的, 我们只需要按照用户名去查询这个用户就ok了,最后查询出来的信息给到 UserDetails 里面,交给 Spring Security 去验证

我们在数据库中存储的密码都是经过加密的,所以我们还需要告诉 Spring Security 我们的加密方式 ,就是 PasswordEncoder, 先看一下他的源码

image

这个也是一个接口,里面就两个方法, 一个是加密的方法,这个可以在用户注册的时候给密码加密, 然后是一个匹配的方法, 需要传入原始密码,和加密后的密码, 然后返回是否匹配, 这个东西我们是可以自己实现的,比如常用的MD5等, 这里他还提供了很多默认的实现,这里就先用他提供的 BCryptPasswordEncoder

在前面案例创建的 BrowserSecurityConfig 中,加入下面的配置

1
2
3
4
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}

这样密码验证器就生效了

测试

最后看一下我在 UserServiceImpl 中的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Service
public class UserServiceImpl implements UserService {

private static Map<String, User> userMap = new HashMap<>(3);

/**
* 初始化数据,模拟从数据库获取数据
*/
static{
// 密码是经过加密的 都是 123456 通过 BCryptPasswordEncoder 加密
User user1 = new User(1L, "zhangsan", "$2a$10$W17jDdV96PUBlVx/PL6zDuaK3rv9uH.DKLwkFaD6lhOPkv3GD5sEu", false, true);
User user2 = new User(2L, "lisi", "$2a$10$W17jDdV96PUBlVx/PL6zDuaK3rv9uH.DKLwkFaD6lhOPkv3GD5sEu", false, true);
User user3 = new User(3L, "lockedtest", "$2a$10$W17jDdV96PUBlVx/PL6zDuaK3rv9uH.DKLwkFaD6lhOPkv3GD5sEu", true, true);
User user4 = new User(4L, "enabletest", "$2a$10$W17jDdV96PUBlVx/PL6zDuaK3rv9uH.DKLwkFaD6lhOPkv3GD5sEu", false, false);

userMap.put(user1.getName(), user1);
userMap.put(user2.getName(), user2);
userMap.put(user3.getName(), user3);
userMap.put(user4.getName(), user4);

}

@Override
public User findUserByName(String username) {
User user = userMap.get(username);
return user;
}

}

这里应该是要从数据库查询,我这里就模拟了几个用户,密码都是通过 BCryptPasswordEncoder 加密好的,加密这步应该是放到注册里面的

然后启动项目,看一下效果, 访问随便一个接口,我们上面可以试试锁定的用户,还有失效用户登录,还有密码错误的时候,返回分别如下:

image

image

image

然后输入正确的密码,就可以访问到接口了

总结

关于自定义认证主要做了以下两件事:

  • 手动实现 UserDetailsService, 跟数据库交互做登录验证
  • 然后就是 PasswordEncoder 自定义密码加密解密的方法,然后配置到项目里面