Spring Security 从数据库加载用户名和密码
目录
Spring Security 从数据库加载用户名和密码
项目结构
数据库设计
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for ROLE
-- ----------------------------
DROP TABLE IF EXISTS `ROLE`;
CREATE TABLE `ROLE` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of ROLE
-- ----------------------------
BEGIN;
INSERT INTO `ROLE` VALUES (1, 'ROLE_ADMIN');
INSERT INTO `ROLE` VALUES (2, 'ROLE_USER');
COMMIT;
-- ----------------------------
-- Table structure for ROLE_USER
-- ----------------------------
DROP TABLE IF EXISTS `ROLE_USER`;
CREATE TABLE `ROLE_USER` (
`user_id` int DEFAULT NULL,
`role_id` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of ROLE_USER
-- ----------------------------
BEGIN;
INSERT INTO `ROLE_USER` VALUES (1, 1);
INSERT INTO `ROLE_USER` VALUES (2, 2);
COMMIT;
-- ----------------------------
-- Table structure for USER
-- ----------------------------
DROP TABLE IF EXISTS `USER`;
CREATE TABLE `USER` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT NULL,
`password` varchar(150) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of USER
-- ----------------------------
BEGIN;
INSERT INTO `USER` VALUES (1, 'root', '$2a$10$YZeZCBa8GNB3STzcOcLnh.gUTt5zdc8lvSOgubQaONbuJNrox3K16');
INSERT INTO `USER` VALUES (2, 'ming', '$2a$10$qv2hxWT3PsYBA0RwTfttJ.JhhvwBm6dSqZmAEI2gJSGyCo.iXOMG6');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
Mapper
MyUser
public class MyUser {
private Integer id;
private String name;
private String password;
public MyUser(){}
//省略get set方法
}
Role
public class Role {
private Integer id;
private String name;
public Role(){}
//省略get set方法
}
LoginMapper
@Mapper
@Component //把普通pojo实例化到spring容器中,相当于配置文件中的<bean id="" class="">
public interface LoginMapper {
@Select("select * from user where name = #{name}")
MyUser loadUserByUsername(String name);
@Select("SELECT role.`name` FROM role WHERE role.id in (SELECT role_id FROM " +
" role_user as r_s JOIN `user` as u ON r_s.user_id = u.id and u.id = #{id})")
List<Role> findRoleByUserId(int id);
}
自定义 UserDetailsService
UserDetailsService 的主要作用是获取数据库里面的信息,然后封装成user对象。
实现自己的 UserDetailsService ,从数据库中获取用户名和密码信息。
UserService
public interface UserService extends UserDetailsService {
}
UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Autowired
private LoginMapper loginMapper;
@Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
// 根据用户名查询数据库,查到对应的用户
MyUser myUser = loginMapper.loadUserByUsername(name);
// ... 做一些异常处理,没有找到用户之类的
if(myUser == null){
throw new UsernameNotFoundException("用户不存在") ;
}
// 根据用户ID,查询用户的角色
List<Role> roles = loginMapper.findRoleByUserId(myUser.getId());
// 添加角色
List<GrantedAuthority> authorities = new ArrayList<>();
for (int i = 0; i < roles.size(); i++) {
authorities.add(new SimpleGrantedAuthority(roles.get(i).getName()));
}
// 构建 Security 的 User 对象
User user = new User(myUser.getName(), myUser.getPassword(), authorities);
return user;
}
}
自定义登陆校验器 AuthenticationProvider
实现自己的 AuthenticationProvider 类,完成校验逻辑。
@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserService userService;
/**
* 进行身份认证
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 获取用户输入的用户名和密码
String username = authentication.getName();
String password = authentication.getCredentials().toString();
// 获取封装用户信息的对象
UserDetails userDetails = userService.loadUserByUsername(username);
// 进行密码的比对
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
boolean flag = bCryptPasswordEncoder.matches(password, userDetails.getPassword());
// 校验通过
if (flag){
// 将权限信息也封装进去
return new UsernamePasswordAuthenticationToken(userDetails,password,userDetails.getAuthorities());
}
// 验证失败返回 null
return null;
}
/**
* 这个方法 确保返回 true 即可
*/
@Override
public boolean supports(Class<?> aClass) {
return true;
}
配置 security
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// WebSecurityConfigurerAdapter类:通过重载该类的三个configure()方法来制定Web安全的细节。
// 1、configure(WebSecurity):通过重载该方法,可配置Spring Security的Filter链。
// 2、configure(HttpSecurity):通过重载该方法,可配置如何通过拦截器保护请求。
// 3、configure(AuthenticationManagerBuilder):通过重载该方法,可配置user-detail(用户详细信息)服务。
@Autowired
private MyAuthenticationProvider myAuthenticationProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() //开启登录配置
.antMatchers("/", "/home").permitAll() //访问"/"和"/home"路径的请求都允许
.anyRequest().authenticated() //表示剩余的其他接口,登录之后访问
.and()
.formLogin() //表单登陆
//定义登录页面,未登录时,访问一个需要登录之后才能访问的接口,会自动跳转到该页面
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.authenticationProvider(myAuthenticationProvider);
}
}