接着上篇,我们继续配置WebSecurityConfig。上一篇我们配置的是登录相关的参数,接下来再配置一下其它参数。
这里我们得先确定密码的加密方式,默认会使用bcrypt对我们输入的密码进行加密,然后才会比较输入的密码和存放的密码是否一致。
我们需要在WebSecurityConfig文件中,指定加密的方式,后面使用这个加密对象,对我们的密码进行加密。
@Bean
public PasswordEncoder passwordEncoder() {
// 使用默认bcrypt的方式对密码进行加密
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
// 不进行密码加密
// return NoOpPasswordEncoder.getInstance();
}
用户密码除了第一篇使用application.yml文件配置的方式外,还可以使用代码的方式
在WebSecurityConfig文件中,使用InMemory的方式配置了两个用户,此处使用了上面注入的加密对象,对密码进行了加密。
@Autowired
PasswordEncoder passwordEncoder;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("tester")
.password(passwordEncoder.encode("123456"))
.authorities("tester")
.and()
.withUser("user")
.password(passwordEncoder.encode("123456"))
.authorities("user");
}
访问http://locahost:8080/login就可以使用上面我们配置的两个用户登录访问了。
这次我们使用JDBC方式来读取数据库中的用户信息,进行身份认证。
数据库使用的mysql5.6版本
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.13version>
dependency>
Spring Security为我们准备了数据库脚本,jar包中的org/springframework/security/core/userdetails/jdbc/users.ddl文件。
在项目启动时就会运行初始化脚本,但是我使用的数据库mysql5.6,不支持varchar_ignorecase类型。因此需要自己准备好数据库,并运行下面的sql脚本。
create table users(username varchar(50) not null primary key,password varchar(500) not null,enabled boolean not null);
create table authorities (username varchar(50) not null,authority varchar(50) not null,constraint fk_authorities_users foreign key(username) references users(username));
create unique index ix_auth_username on authorities (username,authority);
withDefaultSchema方法会运行数据表初始化脚本,前提是你的数据库支持varchar_ignorecase字段类型。这里我同样添加了两个用户。
@Autowired
DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
// 下面的方法会运行数据表初始化脚本,前提是你的数据库支持varchar_ignorecase字段类型
// .withDefaultSchema()
//使用自定义sql查询用户信息
.usersByUsernameQuery("select username,password,enabled from users " + "where username = ?")
.withUser("tester")
.password(passwordEncoder.encode("123456"))
.authorities("tester")
.and()
.withUser("user")
.password(passwordEncoder.encode("123456"))
.authorities("tester");
}
在项目启动的时候,我们会发现刚刚创建的数据表中已经有两条用户记录了。加密后的密码有{bcrypt}的前缀,说明是使用bcrypt方式进行加密
访问http://locahost:8080/login就可以使用上面我们配置的两个用户登录访问了。
下面是写了一个继承AuthenticationProvider的匿名类。support方法用来判断是否验证,authenticate方法用来验证。如果用户名密码正确,就返回一个UsernamePasswordAuthenticationToken带有用户的认证信息。此处的用户密码是写死的,如果是正式项目,就可以从数据库中获取用户信息。
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new AuthenticationProvider() {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
if (authentication.getCredentials() == null)
throw new BadCredentialsException("Bad credentials");
String password = authentication.getCredentials().toString();
if( "user".equals(username) && "123456".equals(password)) {
UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
authentication.getPrincipal(), authentication.getCredentials(),
authentication.getAuthorities());
result.setDetails(authentication.getDetails());
return result;
}
throw new UsernameNotFoundException("用户或密码错误");
}
@Override
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class
.isAssignableFrom(authentication));
}
});
}
和上面的AuthenticationProvider方式不同,这里使用返回的是一个UserDetails。这个UserDetails是给DaoAuthenticationProvider类用的,它会去比较登录时填写的User和这里UserDetails返回的User,进行用户名和密码的比较。如果相同,那么就通过验证。
DaoAuthenticationProvider从名字上看出,它是继承自AuthenticationProvider,有兴趣的同学可以看看它的源代码。
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(new UserDetailsService() {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if ("user".equals(username)) {
return new User(
"user",
passwordEncoder.encode("123456"),
Collections.singletonList(new SimpleGrantedAuthority("user")
));
}
return null;
}
});
}
本篇使用了多种不同的方式来判断用户登录时的身份认证,至于LDAP方式,我这边没有使用和测试,基本原理都差不多。对于正式项目,大部分情况会使用自定义AuthenticationProvider或者UserDetailsService的方式,搭配数据库和redis来进行身份认证,可以更加灵活的面对变化的需求。
github项目地址,https://github.com/camellibby/security-demo