一.简介
前面章节所有的用户信息(用户名和密码)都是基于配置文件配置的,这篇文章学习基于多种方式配置登录用户,比如:
- memory(内存)
- jdbc
- MyBatis
二.创建项目
如何创建一个SpringSecurity项目,前面文章已经有说明了,这里就不重复写了。
三.基于memory(内存)方式
其实在配置文件中写的用户信息,最终也是被读到内存中的,截图如下:
这一块就是基于内存方式构建了用户信息,定义了默认的用户信息来源。
3.1配置内存方式
代码如下:
@Configuration
public class SecurityConfig {
@Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager(){
UserDetails userDetails1 = User.withUsername("memory1").password("{noop}memory1").roles("memory1").build();
UserDetails userDetails2 = User.withUsername("memory2").password("{noop}memory2").roles("memory2").build();
return new InMemoryUserDetailsManager(userDetails1,userDetails2);
}
}
返回结果中返回了用户信息,权限信息,还有一个details,这个我们后面是需要实现自己的,扩展我们业务中的信息。截图如下:
3.2验证登录
memory1,截图如下:
memory2,截图如下:
四.基于JDBC 方式
4.1引入依赖
相应的版本
mysql:mysql-connector-java:8.0.32
org.springframework.boot:spring-boot-starter-jdbc
4.2获取数据库执行脚本
在这个路径下:org/springframework/security/core/userdetails/jdbc/users.ddl 得到脚本后,将_ignorecase 去掉,截图如下:
执行之后,得到两张表,截图如下:
4.3配置JDBC Manager
JDBC Manager的配置代码如下:
@Autowired
private DataSource dataSource;
@Bean
public JdbcUserDetailsManager jdbcUserDetailsManager(){
JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager(dataSource);
if(!jdbcUserDetailsManager.userExists("lglbc-jdbc")){
jdbcUserDetailsManager.createUser(User.withUsername("lglbc-jdbc").username("lglbc-jdbc").password("{noop}lglbc-jdbc").roles("admin").build());
}
if(!jdbcUserDetailsManager.userExists("lglbc-jdbc2")){
jdbcUserDetailsManager.createUser(User.withUsername("lglbc-jdbc2").username("lglbc-jdbc2").password("{noop}lglbc-jdbc2").roles("admin").build());
}
return jdbcUserDetailsManager;
}
4.4验证登录
截图如下:
五.基于MyBatis 方式
5.1引入依赖
基于上面的依赖之后,需要再引入MyBatis的依赖
org.mybatis.spring.boot:mybatis-spring-boot-starter:+
5.2定义UserDetails
UserDetails类,代码如下:
public class User implements UserDetails {
private Integer id;
private String username;
private String password;
private Boolean enabled;
private Boolean accountNonExpired;
private Boolean accountNonLocked;
private Boolean credentialsNonExpired;
private List<Role> roles = new ArrayList<>();
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (Role role : roles) {
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return accountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
return accountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return credentialsNonExpired;
}
@Override
public boolean isEnabled() {
return enabled;
}
public void setId(Integer id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public void setAccountNonExpired(Boolean accountNonExpired) {
this.accountNonExpired = accountNonExpired;
}
public void setAccountNonLocked(Boolean accountNonLocked) {
this.accountNonLocked = accountNonLocked;
}
public void setCredentialsNonExpired(Boolean credentialsNonExpired) {
this.credentialsNonExpired = credentialsNonExpired;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public Integer getId() {
return id;
}
public static 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;
}
//省略getter/setter
}
}
5.3定义UserDetailService
UserDetailService类,代码如下:
@Service
public class MyUserDetailService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userMapper.loadUserByUserName(username);
if (Objects.isNull(user)) {
throw new UsernameNotFoundException("user is null");
}
user.setRoles(userMapper.getRolesByUid(user.getId()));
return user;
}
}
5.3创建mybatis mapper
UserMapper 类,代码如下:
@Mapper
public interface UserMapper {
@Select("select r.* from user_role ur LEFT JOIN role r on ur.rid=r.id where ur.uid=#{id}")
public List<User.Role> getRolesByUid(@Param("id") Integer id);
@Select("select * from user where username=#{uname} limit 1")
public User loadUserByUserName(String uname);
}
5.4创建需要的表
表的sql脚本如下:
CREATE TABLE `role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) DEFAULT NULL,
`nameZh` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`enabled` tinyint(1) DEFAULT NULL,
`accountNonExpired` tinyint(1) DEFAULT NULL,
`accountNonLocked` tinyint(1) DEFAULT NULL,
`credentialsNonExpired` tinyint(1) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` int(11) DEFAULT NULL,
`rid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `uid` (`uid`),
KEY `rid` (`rid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `role` (`id`, `name`, `nameZh`)
VALUES
(1,'ROLE_dba','数据库管理员'),
(2,'ROLE_admin','系统管理员'),
(3,'ROLE_user','用户');
INSERT INTO `user` (`id`, `username`, `password`, `enabled`,
`accountNonExpired`, `accountNonLocked`, `credentialsNonExpired`)
VALUES
(1,'root','{noop}123',1,1,1,1),
(2,'admin','{noop}123',1,1,1,1),
(3,'sang','{noop}123',1,1,1,1);
INSERT INTO `user_role` (`id`, `uid`, `rid`)
VALUES
(1,1,1),
(2,1,2),
(3,2,2),
(4,3,3);
5.5验证登录
在启动之前,记住要把之前配置的jdbc和内存方式获取用户信息的配置给注销,否则会报如下错误:
至于为什么,请看原理分析
我的密码里面都加上了{noop},为什么呢?因为加上这个代码就等于告诉security 我的密码没加密,因为security默认给密码使用加密,如果它看到{noop},就不会对密码进行加密匹配。
六.为什么定义了两个UserDetailService,就抛如下异常
因为security在初始化http的时候,会初始化全局的认证处理器,如果发现有不等于1个UserDetails实现,则不会设置默认的AuthenticationProvider:InitializeUserDetailsManagerConfigurer#configure
所以当我们定义两个UserDetailService(验证多种用户),那就需要自己定义一个AuthenticationManager,重写他获取provider和UserDetailService的逻辑,这个后面会给大家介绍:如何配置多个数据源,验证不同用户表