security默认使用的就是内存方式定义数据源
一、基于内存的数据源
1、配置文件方式
默认的用户定义在SecurityProperties里边,是一个静态内部类,如果要定义自己的用户名密码,必然是要去覆盖默认配置,在配置文件中配置
spring.security.user.name=admin spring.security.user.password=admin spring.security.user.roles=ADMIN
此时重启项目,就可以使用自己定义的用户名/密码登录了。
2、配置类中重写configure方式
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Bean PasswordEncoder passwordEncoder() { //暂时先不给密码进行加密,所以返回 NoOpPasswordEncoder 的实例 return NoOpPasswordEncoder.getInstance(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() //如果需要配置多个用户,用 and 相连 .withUser("123") .password("123").roles("admin"); } }
通过重写 WebSecurityConfigurerAdapter 中的 userDetailsService 方法来提供一个 UserDetailService 实例进而配置多个用户:
@Configuration public class SecurityConfig1 extends WebSecurityConfigurerAdapter { @Bean PasswordEncoder passwordEncoder(){ return NoOpPasswordEncoder.getInstance(); } @Override @Bean public UserDetailsService userDetailsServiceBean() throws Exception { InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); manager.createUser(User.withUsername("admin").password("admin").roles("admin").build()); manager.createUser(User.withUsername("123").password("123").roles("user").build()); return manager; } }
4、自定义实现类方式
重写UserDetailsService接口中的loadUserByUsername方法,并在配置类型申明
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import java.util.List; /** * 自定义认证服务实现security中的方法 */ @Service public class MyUserDetailsServiceImpl implements UserDetailsService { //实现security接口类userdetails唯一方法 @Override public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { //这个设置一个角色 List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("role"); return new User("123",getPass(),authorities); } //对配置的用户密码123进行加密 public String getPass(){ return new BCryptPasswordEncoder().encode("123"); } }
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import javax.annotation.Resource; @Configuration public class SecurityConfig1 extends WebSecurityConfigurerAdapter { //注入自己的实现类 @Resource private MyUserDetailsServiceImpl userDetailsService; @Bean PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } }
总结:以上四种基于内存的方式认证推荐第二种
二、基于数据库数据源
1、基于默认的数据库模型认证
UserDetailsService 都有哪些实现类
可以看到,在几个能直接使用的实现类中,除了 InMemoryUserDetailsManager 之外,还有一个 JdbcUserDetailsManager,使用 JdbcUserDetailsManager 可以让我们通过 JDBC 的方式将数据库和 Spring Security 连接起来。
JdbcUserDetailsManager 自己提供了一个数据库模型,这个数据库模型保存在如下位置:
org/springframework/security/core/userdetails/jdbc/users.ddl
这里面sql语句如下
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);
可以看到,脚本中有一种数据类型 varchar_ignorecase,这个其实是针对 HSQLDB 数据库创建的,而我们使用的 MySQL 并不支持这种数据类型,所以这里需要大家手动调整一下数据类型,将 varchar_ignorecase 改为 varchar 即可。
修改完成后,创建数据库,执行完成后的脚本。
执行完 SQL 脚本后,我们可以看到一共创建了两张表:users 和 authorities。
- users 表中保存用户的基本信息,包括用户名、用户密码以及账户是否可用。
- authorities 中保存了用户的角色。
- authorities 和 users 通过 username 关联起来。
配置完成后,接下来,我们将 InMemoryUserDetailsManager 提供的用户数据用 JdbcUserDetailsManager 代替掉,如下:
@Autowired DataSource dataSource; @Override @Bean protected UserDetailsService userDetailsService() { JdbcUserDetailsManager manager = new JdbcUserDetailsManager(); manager.setDataSource(dataSource); if (!manager.userExists("admin")) { manager.createUser(User.withUsername("admin").password("123").roles("admin").build()); } if (!manager.userExists("123")) { manager.createUser(User.withUsername("123").password("123").roles("user").build()); } return manager; }
- 首先构建一个 JdbcUserDetailsManager 实例。
- 给 JdbcUserDetailsManager 实例添加一个 DataSource 对象。
- 调用 userExists 方法判断用户是否存在,如果不存在,就创建一个新的用户出来(因为每次项目启动时这段代码都会执行,所以加一个判断,避免重复创建用户)。
- 用户的创建方法和我们之前 InMemoryUserDetailsManager 中的创建方法基本一致
因为要连接数据库,所以还需导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
配置文件配置
spring.datasource.username=root spring.datasource.password=root spring.datasource.url=jdbc:mysql:///security?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
2、自定义数据库认证
Spring Security适应系统,而非让系统适应Spring Security,是Spring Security框架开发者和使用者的共识,上面使用了 InMemoryUserDetailsManager 和 JdbcUserDetailsManager 两个UserDetailsService 实现类。生效方式也很简单,只需加入 Spring 的 IoC 容器,就会被 Spring Security自动发现并使用。自定义数据库结构实际上也仅需实现一个自定义的UserDetailsService。
UserDetailsService仅定义了一个loadUserByUsername方法,用于获取一个UserDetails对象。UserDetails对象包含了一系列在验证时会用到的信息,包括用户名、密码、权限以及其他信息
这里结合mybatis认证,需要导入的依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
自定义类
@Service public class MyUserDetailsServiceImpl implements UserDetailsService { @Resource private UserDao userDao; //实现security接口类userdetails唯一方法 @Override public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { SysUser user = userDao.getUserByUserName(userName); //这里查出结果如:userName = "admin" passWord="$2a$10$FsNmqBMoxqRQAzcjvF8YD.Sqh3SaSkO40FfuC.VraGuKTcTeC3wDm";密码是经过BCryptPasswordEncoder加密的 if(user==null){ throw new UsernameNotFoundException("用户不存在"); } //这个设置一个角色 List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("role"); return new User(user.getUserName(),user.getPassWord(),authorities); } }
配置类
@Configuration public class SecurityConfig1 extends WebSecurityConfigurerAdapter { //注入自己的实现类 @Resource private MyUserDetailsServiceImpl userDetailsService; @Bean PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } }