自定义用户认证
通过自定义WebSecurityConfigurerAdapter类型的Bean组件,并重写configure(Authentication ManagerBuilder auth)方法,
可以实现自定义用户认证。
一、内存身份认证
In-Memory Authentication(内存身份认证)是最简单的身份认证方式,主要用于Security安全认证体验和测试
1.自定义WebSecurityConfigurerAdapter配置类

2.使用内存进行身份认证

//开启MVC Security安全支持
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
// 密码需要设置编码器
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
// 1、使用内存用户信息,作为测试使用
auth.inMemoryAuthentication().passwordEncoder(encoder)
.withUser("czy").password(encoder.encode("123")).roles("common")
.and()
.withUser("tom").password(encoder.encode("456")).roles("vip");
}
}
3.效果测试

输入正确的用户名和密码后进入首页

输入错误的用户名或者密码,将会显示错误

二、JDBC身份认证
1.数据准备
创建t_customer、t_authority和t_customer_authority三张表


# 选择使用数据库
USE springbootdata;
# 创建表t_customer并插入相关数据
DROP TABLE IF EXISTS `t_customer`;
CREATE TABLE `t_customer` (
`id` int(20) NOT NULL AUTO_INCREMENT,
`username` varchar(200) DEFAULT NULL,
`password` varchar(200) DEFAULT NULL,
`valid` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
INSERT INTO `t_customer` VALUES ('1', 'shitou', '$2a$10$5ooQI8dir8jv0/gCa1Six.GpzAdIPf6pMqdminZ/3ijYzivCyPlfK', '1');
INSERT INTO `t_customer` VALUES ('2', '李四', '$2a$10$5ooQI8dir8jv0/gCa1Six.GpzAdIPf6pMqdminZ/3ijYzivCyPlfK', '1');
# 创建表t_authority并插入相关数据
DROP TABLE IF EXISTS `t_authority`;
CREATE TABLE `t_authority` (
`id` int(20) NOT NULL AUTO_INCREMENT,
`authority` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO `t_authority` VALUES ('1', 'ROLE_common');
INSERT INTO `t_authority` VALUES ('2', 'ROLE_vip');
# 创建表t_customer_authority并插入相关数据
DROP TABLE IF EXISTS `t_customer_authority`;
CREATE TABLE `t_customer_authority` (
`id` int(20) NOT NULL AUTO_INCREMENT,
`customer_id` int(20) DEFAULT NULL,
`authority_id` int(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
INSERT INTO `t_customer_authority` VALUES ('1', '1', '1');
INSERT INTO `t_customer_authority` VALUES ('2', '2', '2');
SQL
2.添加JDBC连接数据库的依赖启动器

3、数据库连接配置
#MySQL相关配置
spring.datasource.url=jdbc:mysql://192.168.152.120:3306/springbootdata?serverTimeZone=UTC
spring.datasource.username=root
spring.datasource.password=1
#Redis相关配置
spring.redis.host=192.168.152.120
spring.redis.port=6379
spring.redis.password=1
4、使用JDBC进行身份认证

// 2.使用JDBC进行身份验证
String userSQL ="select username,password,valid from t_customer "+ "where username = ?";
String authoritySQL="select c.username,a.authority from t_customer c, "+"t_authority a,t_customer_authority ca where "+"ca.customer_id=c.id and ca.authority_id=a.id and c.username =?";
auth.jdbcAuthentication().passwordEncoder(encoder).dataSource(dataSource)
.usersByUsernameQuery(userSQL).authoritiesByUsernameQuery(authoritySQL);
5、效果测试:同理
三、UserDetailsService身份认证
1、定义查询用户及角色信息的服务接口



package com.uos.safe.service;
import com.uos.safe.domain.Authority;
import com.uos.safe.domain.Customer;
import com.uos.safe.repository.AuthorityRepository;
import com.uos.safe.repository.CustomerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
//对用户数据结合Redis缓存进行业务处理
@Service
public class CustomerService {
@Autowired
private CustomerRepository customerRepository;
@Autowired
private AuthorityRepository authorityRepository;
@Autowired
private RedisTemplate redisTemplate;
//业务控制:使用唯一用户名查询用户信息
public Customer getCustomer(String username){
Customer customer = null;
Object o = redisTemplate.opsForValue().get("customer_"+username);
if (o != null){
customer = (Customer) o;
}else {
customer = customerRepository.findByUsername(username);
if (customer != null){
redisTemplate.opsForValue().set("customer_"+username,customer);
}
}
return customer;
}
// 业务控制:使用唯一用户名查询用户权限
public List<Authority> getCustomerAuthority(String username){
List<Authority> authorities = null;
Object o = redisTemplate.opsForValue().get("authorities_"+username);
if (o != null){
authorities = (List<Authority>) o;
}else {
authorities = authorityRepository.findAuthoritiesByUsername(username);
if (authorities.size() > 0){
redisTemplate.opsForValue().set("authorities_"+username,authorities);
}
}
return authorities;
}
}
CustomerService
2、定义UserDetailsService用于封装认证用户信息



package com.uos.safe.service;
import com.uos.safe.domain.Authority;
import com.uos.safe.domain.Customer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
/*自定义一个 UserDetailsService实现类进行用户认证信息封装*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
CustomerService customerService;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
// 通过业务方法获取用户权限信息
Customer customer = customerService.getCustomer(s);
List<Authority> authorities = customerService.getCustomerAuthority(s);
// 对用户权限进行封装
List<SimpleGrantedAuthority> list = authorities.stream()
.map(authority -> new SimpleGrantedAuthority(authority.getAuthority()))
.collect(Collectors.toList());
// 返回封装的UserDetails用户详情类
if (customer != null) {
UserDetails userDetails = new User(customer.getUsername(),customer.getPassword(),list);
return userDetails;
} else {
// 如果查询的用户不存在(用户名不存在),必须抛出异常
throw new UsernameNotFoundException("当前用户不存在!");
}
}
}
UserDetailsServiceImpl
3、使用UserDetailsService进行身份认证

4、效果测试:同理