一、前言
前几篇博客在内存配置的基础上简单了解了一下spring security中的一些配置。但是用户不可能是在内存中写死的,这篇博客就配合mybatis学习一下基于数据库的认证如何配置。
二、开整
2.1、准备数据库
首先准备一下数据库
这篇主要用到这几个表
用户表
角色表
用户角色关联表
2.2、准备实体类
User类
在spring security中需要准备一个UserDetails
的实现类用来存储用户的基本信息,这些信息会被封装在Authentication
对象中。随后会多次使用到Authentication
这个类。
(只是这里是做一个简单的例子,所以部分属性没有配置。)
public class User implements UserDetails {
private Integer id;
private String username;
private String password;
private Boolean enabled;
private Boolean locked;
private List<Role> roles;
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Override
public String getUsername() {
return username;
}
@Override
// 账户未过期
public boolean isAccountNonExpired() {
return true;
}
@Override
// 账户未锁定
public boolean isAccountNonLocked() {
return !locked;
}
@Override
// 凭证(密码)未过期过期
public boolean isCredentialsNonExpired() {
return true;
}
@Override
// 用户是否可用
public boolean isEnabled() {
return enabled;
}
public void setUsername(String username) {
this.username = username;
}
@Override
// 用户的角色列表,需要将角色名称封装进SimpleGrantedAuthority对象中
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (Role role : roles) {
authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getName()));
}
return authorities;
}
@Override
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public void setLocked(Boolean locked) {
this.locked = locked;
}
}
Role类
public class Role {
private Integer id;
private String name;
private String nameZh;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNameZh() {
return nameZh;
}
public void setNameZh(String nameZh) {
this.nameZh = nameZh;
}
}
2.3、准备UserDetailsService的实现类
前面我们的User
类实现了UserDetails
接口。在这里我们还要实现一个UserDetailsService
接口,这个接口中只定义了一个方法loadUserByUsername
。这个方法主要是验证用户是否存在,允许抛出UsernameNotFoundException
异常。
@Service("userServiceImpl")
public class UserServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userMapper.loadUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("用户不存在!");
}
user.setRoles(userMapper.getUserRolesById(user.getId()));
return user;
}
}
2.4、准备配置类
@Configuration
public class SecurityDbConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userServiceImpl;
@Bean
// 密码加密方式
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
// 配置身份验证
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userServiceImpl);
}
@Override
// 在内存中配置路径资源权限
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/dba/**").hasRole("dba")
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasRole("user")
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll()
.and()
.csrf().disable();
}
}
2.5、测试
随便准备一个Controller,登录不同的用户访问这些接口,有相应权限的可以正常访问,反之则不能。
@RestController
public class HelloSpringBoot {
@GetMapping("/SpringBoot")
public String hello() {
return "Hello SpringBoot!";
}
@GetMapping("/admin/hello")
public String helloAdmin() {
return "Hello admin!";
}
@GetMapping("/user/hello")
public String helloUser() {
return "Hello user!";
}
@GetMapping("/dba/hello")
public String helloDba() {
return "hello dba!";
}
}