我的实例是Spring Boot项目,随便一个SpringBoot项目都可以,先不启用SpringSecurity。
创建一个demoController,加方法:
@ResponseBody
@RequestMapping("wtf")
public String wtf(){
return "WTF";
}
运行项目,浏览器测试一下:
然后,pom文件加入SpringSecurity的依赖后就启用SpringSecurity了。
org.springframework.boot
spring-boot-starter-security
重新启动项目,访问wtf测试一下:
说明SpringSecurity起作用了,由于Spring Security的默认安全策略是对所有请求都必须通过安全验证,所以请求wtf需要首先通过身份认证才能访问。这个时候他用的是默认的叫InMemoryUserDetailsManager的用户信息管理器来完成我们上篇文章所说的用户管理工作的,他的默认用户为user,密码在启动的过程中打印在了控制台:
用这个用户名、密码试一下:
可以访问:
好的,我们开始干活。
用户对象Userdetails
首先需要有用户对象,用户对象需要有用户名、密码,SpringSecurity要求用户对象实现接口:
org.springframework.security.core.userdetails。
代码如下,简单一点:
@Component
public class User implements UserDetails {
private String name;
private String password;
@Override
public Collection extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return name;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
UserDetailsService
然后需要有我们自己的UserDetailsService。
创建一个用户service类、实现UserDetailsService接口的loadUserByUsername方法即可。
我们创建一个用户名 user,密码:123456的用户,表示当前系统用户只有一个user用户、密码为123456.
@Service
@Slf4j
public class MyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("Here we are in MyUserDetailsService====");
User user=new User();
user.setName("user");
user.setPassword("123456");
return user;
}
}
PasswordEncoder
实现密码解析器PasswordEncoder的encode、matches方法,这两个方法很好理解,encode是对密码明文进行加密的,返回加密后的密码密文,matches方法是用来验密的。
我们不研究加密算法,所以也就不加密了,实际应用的时候,项目中可以替换encode方法为加密算法即可。
@Service
public class MyPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return rawPassword.toString();
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return encode(rawPassword).equals(encodedPassword);
}
}
强调一下,UserDetailsService和PasswordEncoder需要加注解@Service,目的是注入Spring Ioc容器。
重新启动项目,这个时候控制台不会再打印上面的那个随机密码串了。
访问wtf,要求登录:
录入user/123456:
登录成功了!
说明上面我们自己创建的UserDetailsService和PasswordEncoder已经开始工作了!!!
会不会觉得太简单了点?
简单分析一下我们自己实现的这两个类的生效原因。
Spring Security在初始化UsernamePassword...的过程中会通过一个叫 InitializeUserDetailsManagerConfigurer的配置类来初始化我们上一篇文章说过的AuthenticationProvider,通过上一篇文章我们知道绝大部分的用户验证工作都是这个AuthenticationProvider完成的。
我们简单看一下InitializeUserDetailsManagerConfigurer的configure方法:
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
if (auth.isConfigured()) {
return;
}
UserDetailsService userDetailsService = getBeanOrNull(UserDetailsService.class);
if (userDetailsService == null) {
return;
}
PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
if (passwordEncoder != null) {
provider.setPasswordEncoder(passwordEncoder);
}
if (passwordManager != null) {
provider.setUserDetailsPasswordService(passwordManager);
}
provider.afterPropertiesSet();
auth.authenticationProvider(provider);
}
方法不长,可以看到首先要到Spring Ioc容器中去获取UserDetailsService和PasswordEncoder,如果能获取到的话,就赋值给初始化好的AuthenticationProvider。
由于我们创建的UserDetailsService和PasswordEncoder都已经通过注解注入到SpringIoc容器中了,所以,这里就能获取到。后续AuthenticationProvider在用户认证干活的时候能调用到我们创建的这两个对象就不奇怪了!