详解Spring Security怎么从数据库加载我们的用户
作者:bangiao 发布时间:2024-01-21 18:35:37
本章内容
如何从数据库中读取用户对象
源码分析
如何从数据库中读取用户对象?
1前面我们分析认证的时候就会发现他在DaoAuthenticationProvider
中就已经有从数据库中获取用户名和密码的。
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
意思是说我们只要重写这个类,就可以从我们所想要的地方获取用户信息。
那我们重写吧
public class MybatisUserDetailsService implements UserDetailsService, UserDetailsPasswordService {
@Resource
private UsersMapper usersMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Users users = usersMapper.loadUserByUsername(username);
if (null == users) {
throw new UsernameNotFoundException(username);
}
return users;
}
@Override
public UserDetails updatePassword(UserDetails user, String newPassword) {
if (user instanceof Users users) {
users.setPassword(newPassword);
usersMapper.updateByPrimaryKeySelective(users);
}
return user;
}
}
发现这里需要把spring security的users
对象转换成我们所需要的users
对象。比较麻烦。
小白: "为什么不直接使用spring security那里面的users对象呢?"
小黑: "我们自己实现users对象的话就可以在自定义。特别是权限这边,我们肯定是需要自定义users对象的,不能直接使用其内部的对象。"
然后我们直接继承UserDetails
接口,然后自定义一些我们所需要的属性。说白了就是从内置users
对象中拷贝一些代码出来。
小黑: "这里我故意留下了一个坑,如果你真的从内置的users
对象里的话,你会发现一个问题。"
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Users implements Serializable, UserDetails {
private Long id;
private String username;
private String password;
private Boolean enabled;
private Boolean accountnonexpired;
private Boolean accountnonlocked;
private Boolean credentialsnonexpired;
private List<Roles> rolesList;
private static final long serialVersionUID = 1L;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
ArrayList<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (Roles roles : getRolesList()) {
authorities.add(new SimpleGrantedAuthority(roles.getRole()));
}
return authorities;
}
@Override
public boolean isAccountNonExpired() {
return accountnonexpired;
}
@Override
public boolean isAccountNonLocked() {
return accountnonlocked;
}
@Override
public boolean isCredentialsNonExpired() {
return credentialsnonexpired;
}
@Override
public boolean isEnabled() {
return enabled;
}
}
小白: "代码定义好了,但现在你要怎么加入到spring security中呢?让spring security主动调用我们自定义的类呢?"
小黑: "我们要使用自定义的类的话,无非是在DaoAuthenticationProvider
里面设置。所以我们只要找到setUserDetailsService
这个函数就行了。看一下有哪些方法调用了这个函数?"
看了一下发现这边调用了setUserDetailsService
方法的。前面的UserDetailsService是对象是通过spring bean上下文面拿出来的。
那我们也可以效仿他的方式,在spring bean上添加我们的UserDetailsService
Bean。
@Bean
public UserDetailsService userDetailsService() {
return new MybatisUserDetailsService();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity.authorizeHttpRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.defaultSuccessUrl("/hello", true) // 认证成功后访问
.permitAll() // 白名单
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login") // 注销成功后访问
.clearAuthentication(true)
.invalidateHttpSession(true)
.and()
.build();
}
@GetMapping("hello")
public HashMap<String, Object> hello(Authentication authentication) {
HashMap<String, Object> map = new HashMap<>();
Object principal = authentication.getPrincipal();
String name = authentication.getName();
map.put("principal", principal);
map.put("name", name);
return map;
}
小白: "停一下, 你数据库表结构呢? "
小黑: "我忘了, 我找找在哪可以抄"
在分析UserDetailsService
的时候, 我们一般都要看看他的类族是怎样的, 结果发现可以偷懒的地方, 也就是表结构的位置
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);
这段sql
要改改, 否则mysql
无法执行
结果发现, 就这...
我还不如自己设计呢
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for authorities
-- ----------------------------
DROP TABLE IF EXISTS `authorities`;
CREATE TABLE `authorities` (
`username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`authority` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
UNIQUE INDEX `ix_auth_username`(`username` ASC, `authority` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`enabled` bit(1) NULL DEFAULT NULL,
`accountNonExpired` bit(1) NULL DEFAULT b'1',
`accountNonLocked` bit(1) NULL DEFAULT b'1',
`credentialsNonExpired` bit(1) NULL DEFAULT b'1',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
注意, 这里仅仅只是为了玩耍, 而非企业, 在企业中, 肯定不是这么设计的, 一般根据 RBAC
设计
小白: "这样就完成了?"
小黑: "完成了, 其他代码都是mybatis
生成的, 简单"
启动, 访问 崩了
等下, spring security脱敏呢? 为什么这里可以输出密码?
分析源码看看
脱敏为什么没有生效?
通过源码分析,脱敏应该是认证成功之后的事情
大概看了下源码, ProviderManager
有脱敏, 但是为什么不生效呢?
看这代码的意思, 是要我们在 Users
类上多添加一个接口CredentialsContainer
public class Users implements Serializable, UserDetails, CredentialsContainer {
@Override
public void eraseCredentials() {
// 设置 password 为 null
this.password = null;
}
}
行, 前面的坑补上了。再试试
完美~~~
来源:https://juejin.cn/post/7182826437617909817


猜你喜欢
- PyCharm 2020.1版安装破解教程,亲测好用 一、安装第一步下载版本一定得是:2020.1其他版本下载地址:https://www.
- 使用Opencv打开笔记本电脑摄像头报错近期要做一个下位机上发图像数据给上位机的任务,调试时自己写了一个客户端获取笔记本电脑的摄像头视频数据
- 1. sys 模块Python 中的 sys 模块具有 argv 功能。当通过终端触发 main.py 的执行时,此功能将返回提供给 mai
- 查看某一列中有多少中取值:数据集名.drop_duplicates(['列名'])#实际为删除重复项,删除后对原数据集不修改
- 在学习django的时候,想要实现登录失败后,进行用户锁定,切记录锁定时间,在网上找了很多资料,但是都感觉不是那么靠谱,于是乎,我开始了我的
- 遇到一个很奇怪的现象,在给页面添加“打印”按钮时,发现网页在IE6下居然不能打印,弹出一个对话框,遇到脚本错误。查看错误详细:定位到 url
- Microsoft? SQL Server? 2000 提供了两种主要机制来强制业务规则和数据完整性:约束和触发器。触发器是一种特殊类型的存
- 大家好,我是启航。本文将给大家分享一个实用的Python办公自动化脚本 「利用Python批量翻译英文Word文档并保留格式」,最终效果甚至
- 为cd2sc.com网站功能而开发,代码为本人原创,生成速度一般。 (出于众所周知的原因,涉及到数据库的数据字段名称做了改动,并且为了代码明
- 一、节点的定义dom节点树图中可见节点HTML文档中的每个成分都是一个节点:整个文档是一个文档节点每个HTML标签是一个元素节点包含在HTM
- 提示:以下操作均在root权限下进行。# 查看有没有安装MySQL:dpkg -l | grep mysql# 安装MySQL:apt in
- 前言这篇文章主要是就在公司实习的时候,对SQL优化工作作出的一些整理。在公司实习的时候,导师分配了SQL慢查询优化的任务,任务是这样的:每周
- 什么是闭包闭包(Closure)是一种函数,它被定义在另一个函数的内部,并且可以访问该函数作用域中的变量,即使该函数已经执行完毕并被销毁。换
- 七夕节简介每年农历七月初七这一天是我国汉族的传统节日七夕节。因为此日活动的主要参与者是少女,而节日活动的内容又是以乞巧为主,故而人们称这天为
- 本文实例为大家分享了JavaScript缓动动画函数的封装代码,供大家参考,具体内容如下本文将从封装缓动动画的以下几个部分进行封装(1、单个
- 1. 开始Python 中可以进行网页解析的库有很多,常见的有 BeautifulSoup 和 lxml 等。在网上玩爬虫的文章通常都是介绍
- 博主本地环境:VMwareCentos7.6django2.22python3.6出现这个的原因分析:第一:您在VMware中的虚拟主机地址
- 使用一个简单的 XSL 样式表就可以将 XML 数据转换成 HTML。随着 XML 规范的不断演进,在新的版本中满足每个人的需要似乎已经成为
- 1.用管理员打开cmd2.首先通过pip命令安装wheelpip install wheel如果提示'pip'不是内部或外部
- 今天看一个水友说他的MySQL现在变的很慢。问什么情况时。说单表超过2个G的一个MyISAM。真垃圾的回答方式。 &n