【Spring Security系列】Spring Security基于默认数据库模型的认证与授权

除了InMemoryUserDetailsManager,Spring Security还提供另一个UserDetailsService实现类: JdbcUserDetailsManagerJdbcUserDetailsManager帮助我们以JDBC的方式对接数据库和Spring Security,它设定了一个默认的数据库模型,只要遵从这个模型,在简便性上,JdbcUserDetailsManager甚至可以媲美InMemoryUserDetailsManager

1.配置数据库

引入JDBC和MySQL两个必要依赖。

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>

<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<scope>runtime</scope>
</dependency>

其他依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

在application.yml中配置数据库连接参数。

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springDemo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&verifyServerCertificate=false&useSSL=false
    username: root
    password: root

 

JdbcUserDetailsManager设定了一个默认的数据库模型,Spring Security将该模型定义在/org/springframework/security/core/userdetails/jdbc/users.ddl

create table users(username varchar_ignorecase(50) not null primary key,password varchar_ignorecase(500) not null,enabled boolean not null);

create table authorities (username varchar_ignorecase(50) not null,authority varchar_ignorecase(50) not null,constraint fk_authorities_users foreign key(username) references users(username));

create unique index ix_auth_username on authorities (username,authority);

JdbcUserDetailsManager需要两个表,其中users表用来存放用户名、密码和是否可用三个信息,authorities表用来存放用户名及其权限的对应关系。 将其复制到MySQL命令窗口执行时,会报错,因为该语句是用hsqldb创建的,而MySQL不支持varchar_ignorecase这种类型。怎么办呢?很简单,将varchar_ignorecase改为MySQL支持的varchar即可。

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);

运行建表语句,得到authoritiesusers两个表。

2.编码实现

下面构建一个JdbcUserDetailsManager实例,让Spring Security使用数据库来管理用户。

@Bean
public UserDetailsService userDetailsService() {
	JdbcUserDetailsManager manager = new JdbcUserDetailsManager();
	manager.setDataSource(dataSource);

	manager.createUser(User.withUsername("user").password("123").roles("USER").build());
	manager.createUser(User.withUsername("admin").password("123").roles("ADMIN").build());

	return manager;
}

JdbcUserDetailsManagerInMemoryUserDetailsManager在用法上没有太大区别,只是多了设置DataSource的环节。Spring Security 通过 DataSource 执行设定好的命令。例如,此处的createUser函数实际上就是执行了下面的SQL语句。

insert into users(username, password, enabled) values (?,?,?)

查看 JdbcUserDetailsManager的源代码可以看到更多定义好的 SQL 语句,诸如deleteUserSqlupdateUserSql等,这些都是JdbcUserDetailsManager与数据库实际交互的形式。当然,JdbcUserDetailsManager 也允许我们在特殊情况下自定义这些 SQL 语句,如有必要,调用对应的 setXxxSql方法即可。现在重启服务,看看在数据库中Spring Security生成了哪些数据

authorities表的authority字段存放的是前面设定的角色,只是会被添上“ROLE_”前缀。下面尝试通过SQL命令创建一个测试账号。

insert into users values ("test", "123", 1);
insert into authorities values("test", "ROLE_USER");

 

清空缓存并使用测试账号访问系统,发现可以访问user路由,但不能访问admin路由,与预期的行为一致。到目前为止,一切都工作得很好,但是只要我们重启服务,应用就会报错。这是因为users表在创建语句时,username字段为主键,主键是唯一不重复的,但重启服务后会再次创建admin和user,导致数据库报错(在内存数据源上不会出现这种问题,因为重启服务后会清空username字段中的内容)。所以如果需要在服务启动时便生成部分用户,那么建议先判断用户名是否存在。

@Bean
public UserDetailsService userDetailsService() {
	JdbcUserDetailsManager manager = new JdbcUserDetailsManager();
	manager.setDataSource(dataSource);

	if (!manager.userExists("user")) {
		manager.createUser(User.withUsername("user").password("123").roles("USER").build());

	}
	if (!manager.userExists("admin")) {
		manager.createUser(User.withUsername("admin").password("123").roles("ADMIN").build());
	}
	return manager;
}

 WebSecurityConfigurerAdapter定义了三个configure

我们只用到了一个参数,用来接收 HttpSecurity对象的配置方法。另外两个参数也有各自的用途,其中,AuthenticationManagerBuilderconfigure同样允许我们配置认证用户。使用方法大同小异,这里不再赘述。

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	
}
    
    
@Override
public void configure(WebSecurity web) throws Exception {
	
}

当使用Spring Security默认数据库模型应对各种用户系统时,难免灵活性欠佳。尤其是在对现有的系统做Spring Security嵌入时,原本的用户数据已经固定,为了适配Spring Security而在数据库层面进行修改显然得不偿失。强大而灵活的Spring Security对这方面进行了改进。

 

 


版权声明:本文为qq_28248897原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。