Springboot +spring security,基于多种方式配置登录用户:memory、jdbc、MyBatis

news2024/11/24 15:58:46

一.简介

前面章节所有的用户信息(用户名和密码)都是基于配置文件配置的,这篇文章学习基于多种方式配置登录用户,比如:

  1. memory(内存)
  2. jdbc
  3. MyBatis

二.创建项目

如何创建一个SpringSecurity项目,前面文章已经有说明了,这里就不重复写了。

三.基于memory(内存)方式

其实在配置文件中写的用户信息,最终也是被读到内存中的,截图如下:
在这里插入图片描述
这一块就是基于内存方式构建了用户信息,定义了默认的用户信息来源。

3.1配置内存方式

代码如下:

@Configuration
public class SecurityConfig {
    @Bean
    public InMemoryUserDetailsManager inMemoryUserDetailsManager(){
        UserDetails userDetails1 = User.withUsername("memory1").password("{noop}memory1").roles("memory1").build();
        UserDetails userDetails2 = User.withUsername("memory2").password("{noop}memory2").roles("memory2").build();
        return new InMemoryUserDetailsManager(userDetails1,userDetails2);
    }
}

返回结果中返回了用户信息,权限信息,还有一个details,这个我们后面是需要实现自己的,扩展我们业务中的信息。截图如下:
在这里插入图片描述

3.2验证登录

memory1,截图如下:在这里插入图片描述
memory2,截图如下:
在这里插入图片描述

四.基于JDBC 方式

4.1引入依赖

相应的版本

mysql:mysql-connector-java:8.0.32
org.springframework.boot:spring-boot-starter-jdbc

4.2获取数据库执行脚本

在这个路径下:org/springframework/security/core/userdetails/jdbc/users.ddl 得到脚本后,将_ignorecase 去掉,截图如下:
在这里插入图片描述
执行之后,得到两张表,截图如下:
在这里插入图片描述

4.3配置JDBC Manager

JDBC Manager的配置代码如下:

 @Autowired
    private DataSource dataSource;
    @Bean
    public JdbcUserDetailsManager jdbcUserDetailsManager(){
        JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager(dataSource);
        if(!jdbcUserDetailsManager.userExists("lglbc-jdbc")){
            jdbcUserDetailsManager.createUser(User.withUsername("lglbc-jdbc").username("lglbc-jdbc").password("{noop}lglbc-jdbc").roles("admin").build());
        }
        if(!jdbcUserDetailsManager.userExists("lglbc-jdbc2")){
            jdbcUserDetailsManager.createUser(User.withUsername("lglbc-jdbc2").username("lglbc-jdbc2").password("{noop}lglbc-jdbc2").roles("admin").build());
        }
        return jdbcUserDetailsManager;
    }

4.4验证登录

截图如下:
在这里插入图片描述
在这里插入图片描述

五.基于MyBatis 方式

5.1引入依赖

基于上面的依赖之后,需要再引入MyBatis的依赖

org.mybatis.spring.boot:mybatis-spring-boot-starter:+

5.2定义UserDetails

UserDetails类,代码如下:

public class User implements UserDetails {
    private Integer id;
    private String username;
    private String password;
    private Boolean enabled;
    private Boolean accountNonExpired;
    private Boolean accountNonLocked;
    private Boolean credentialsNonExpired;
    private List<Role> roles = new ArrayList<>();
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        for (Role role : roles) {
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }
        return authorities;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return accountNonExpired;
    }

    @Override
    public boolean isAccountNonLocked() {
        return accountNonLocked;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return credentialsNonExpired;
    }

    @Override
    public boolean isEnabled() {
        return enabled;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }

    public void setAccountNonExpired(Boolean accountNonExpired) {
        this.accountNonExpired = accountNonExpired;
    }

    public void setAccountNonLocked(Boolean accountNonLocked) {
        this.accountNonLocked = accountNonLocked;
    }

    public void setCredentialsNonExpired(Boolean credentialsNonExpired) {
        this.credentialsNonExpired = credentialsNonExpired;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    public Integer getId() {
        return id;
    }

    public static class Role {
        private Integer id;
        private String name;
        private String nameZh;

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getNameZh() {
            return nameZh;
        }

        public void setNameZh(String nameZh) {
            this.nameZh = nameZh;
        }
        //省略getter/setter
    }
}

5.3定义UserDetailService

UserDetailService类,代码如下:

@Service
public class MyUserDetailService implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userMapper.loadUserByUserName(username);
        if (Objects.isNull(user)) {
            throw new UsernameNotFoundException("user is null");
        }
        user.setRoles(userMapper.getRolesByUid(user.getId()));
        return user;
    }
}

5.3创建mybatis mapper

UserMapper 类,代码如下:

@Mapper
public interface UserMapper {
    @Select("select r.* from user_role ur LEFT JOIN role r on ur.rid=r.id where ur.uid=#{id}")
    public List<User.Role> getRolesByUid(@Param("id") Integer id);

    @Select("select * from user where username=#{uname} limit 1")
    public User loadUserByUserName(String uname);
}

5.4创建需要的表

表的sql脚本如下:

CREATE TABLE `role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) DEFAULT NULL,
`nameZh` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`enabled` tinyint(1) DEFAULT NULL,
`accountNonExpired` tinyint(1) DEFAULT NULL,
`accountNonLocked` tinyint(1) DEFAULT NULL,
`credentialsNonExpired` tinyint(1) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` int(11) DEFAULT NULL,
`rid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `uid` (`uid`),
KEY `rid` (`rid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


INSERT INTO `role` (`id`, `name`, `nameZh`)
VALUES
(1,'ROLE_dba','数据库管理员'),
(2,'ROLE_admin','系统管理员'),
(3,'ROLE_user','用户');
INSERT INTO `user` (`id`, `username`, `password`, `enabled`,
`accountNonExpired`, `accountNonLocked`, `credentialsNonExpired`)
VALUES
(1,'root','{noop}123',1,1,1,1),
(2,'admin','{noop}123',1,1,1,1),
(3,'sang','{noop}123',1,1,1,1);
INSERT INTO `user_role` (`id`, `uid`, `rid`)
VALUES
(1,1,1),
(2,1,2),
(3,2,2),
(4,3,3);

5.5验证登录

在启动之前,记住要把之前配置的jdbc和内存方式获取用户信息的配置给注销,否则会报如下错误:
在这里插入图片描述
至于为什么,请看原理分析
在这里插入图片描述
我的密码里面都加上了{noop},为什么呢?因为加上这个代码就等于告诉security 我的密码没加密,因为security默认给密码使用加密,如果它看到{noop},就不会对密码进行加密匹配。

六.为什么定义了两个UserDetailService,就抛如下异常

在这里插入图片描述
因为security在初始化http的时候,会初始化全局的认证处理器,如果发现有不等于1个UserDetails实现,则不会设置默认的AuthenticationProvider:InitializeUserDetailsManagerConfigurer#configure

在这里插入图片描述
在这里插入图片描述
所以当我们定义两个UserDetailService(验证多种用户),那就需要自己定义一个AuthenticationManager,重写他获取provider和UserDetailService的逻辑,这个后面会给大家介绍:如何配置多个数据源,验证不同用户表

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/552829.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python入门【序列、列表简介、列表的创建 、列表元素的增加、列表元素的删除 】(四)-全面详解(学习总结---从入门到深化)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小王&#xff0c;CSDN博客博主,Python小白 &#x1f4d5;系列专栏&#xff1a;python入门到实战、Python爬虫开发、Python办公自动化、Python数据分析、Python前后端开发 &#x1f4e7;如果文章知识点有错误…

微服务架构初探

大家好&#xff0c;我是易安&#xff01;我们今天来谈一谈微服务架构的前世今生。 我们先来看看维基百科是如何定义微服务的。微服务的概念最早是在2014年由Martin Fowler和James Lewis共同提出&#xff0c;他们定义了微服务是由单一应用程序构成的小服务&#xff0c;拥有自己的…

chatgpt赋能Python-pythonfalse

PythonFalse&#xff1a; Python中的False值 在Python编程语言中&#xff0c;布尔(Boolean)是一种基本数据类型&#xff0c;它只有两个值&#xff1a;True和False。这篇文章将讨论Python中的False值&#xff0c;并提供有关如何使用它的指南。 什么是PythonFalse PythonFalse…

chatgpt赋能Python-pythongil

Python GIL&#xff08;全局解释器锁&#xff09;介绍 Python GIL 是全局解释器锁&#xff08;Global Interpreter Lock&#xff09;的简称&#xff0c;它是 Python 解释器中的一个重要概念。GIL 的作用是确保任何时间只有一个线程在执行 Python 指令&#xff0c;以防止多个线…

jsonmodels.model.base

欢迎来到猫子酱的学习之旅 jsonmodels.model创建模型用法验证 validate()验证器 Validators自定义验证器&#xff08;**&#xff09;默认值转换为Python结构&#xff08;和JSON&#xff09;为您的模型创建JSON模式(***) &#xff08;结合Draft7Validator&#xff09;结构和对象…

蓝桥杯单片机串口通信学习提升笔记

今日得以继续蓝桥杯国赛备赛之旅&#xff1a; 有道是 “不知何事萦怀抱&#xff0c;醒也无聊&#xff0c;醉也无聊&#xff0c;梦也何曾到谢桥。” 那我们该如何 让这位诗人纳兰 “再听乐府曲 &#xff0c;畅解相思苦”呢&#xff1f; 那就建立起串口通信吧&#xff01; 我…

论文阅读_音频表示_W2V-BERT

信息 number headings: auto, first-level 2, max 4, _.1.1 name_en: w2v-BERT: Combining Contrastive Learning and Masked Language Modeling for Self-Supervised Speech Pre-Training name_ch: W2V-BERT&#xff1a;结合对比学习和Mask语言建模进行自监督语音预训练 pape…

Redis实现全局唯一Id

Redis实现全局唯一Id 全局唯一Id简介二、Redis实现全局唯一Id实践2.1添加RedisIdWorker配置类2.2测试类 全局唯一Id简介 系统当中有些场景如果使用数据库自增ID就存在一些问题&#xff1a; id的规律性太明显受单表数据量的限制 场景分析&#xff1a;如果我们的id具有太明显的…

基于UDP和TCP套接字实现简单的回显客户端服务器程序

目录 1. 套接字 2. 基于UDP 套接字实现的简单客户端 服务器程序 3. 基于TCP套接字实现的简单客户端 服务器程序 1. 套接字 之前我们有分享到协议分层这个概念,其中就讲到上层协议调用下层协议,下层协议给上层协议提供支持,这里支持指的是就是socket套接字,它是操作系统给应用…

宁波市天一杯 --- Crypto wp

文章目录 secretrsa secret 题目&#xff1a; p134261118796789547851478407090640074022214132682000430136383795981942884853000826171189906102866323044078348933419038543719361923320694974970600426450755845839235949167391987970330836004768360774676424958554946…

坦克大战进阶--发射子弹

坦克大战进阶–发射子弹 1. 坦克大战0.3 1.1 分析 利用线程基础的知识&#xff0c;把坦克大战再次进阶一下&#xff1a;当我们按下J键&#xff0c;坦克就能够发射一颗子弹。 1.2 思路 当发射一颗子弹后&#xff0c;就相当于启动一个线程Mytank 有子弹的对象&#xff0c;当…

MSP432笔记5——外部中断

所用单片机型号&#xff1a;MSP432P401r 今日继续我的MSP432电赛速通之路。 外部中断是个很有用的配置 STM32几乎每个I/O口都能配置复用为外部中断 但MSP432并不是这样。 我经过查阅数据手册发现支持中断的引脚为&#xff1a; P1^0~ P1^7 P3^0~ P3^7 P5^0~ P5^…

Gateway服务网关入门

Gateway服务网关 Spring Cloud Gateway 是 Spring Cloud 的一个全新项目&#xff0c;该项目是基于 Spring 5.0&#xff0c;Spring Boot 2.0 和 Project Reactor 等响应式编程和事件流技术开发的网关&#xff0c;它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。…

【网络字节序】

网络字节序 我们已经知道&#xff0c;内存中的多字节数据相对于内存地址有大端和小端之分&#xff0c;磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分。网络数据流同样有大端小端之分&#xff0c;那么如何定义网络数据流的地址呢&#xff1f;发送主机通常将发送…

【C++】21年精通C++之泛型编程和模板初阶知识

❤️前言 大家好&#xff01;今天和大家一起学习关于C泛型编程和模板初阶的相关知识。 正文 我们之前已经学习了C中非常重要的一个特性——函数重载&#xff0c;函数重载很好地提高了我们代码的可读性。但是对于适配多种参数的某种函数来说&#xff0c;我们如果使用函数重载就…

感知程序从ros切换到cyber_rt框架下,pcl相关问题

1.在ubuntu20.04下&#xff0c;原感知程序需要的是pcl1.8.1&#xff0c;车上其他程序使用的是pcl.1.10.0或者pcl1.10.0&#xff0c;在编译pcl1.10.0时会编译通不过&#xff0c;而pcl1.10.1可以顺利编译通过&#xff0c;安装pcl1.8.1时遇到的问题可能如下&#xff0c;及对应的修…

CTF必看~ PHP反序列化漏洞6:绝妙_wakeup绕过技巧

作者&#xff1a;Eason_LYC 悲观者预言失败&#xff0c;十言九中。 乐观者创造奇迹&#xff0c;一次即可。 一个人的价值&#xff0c;在于他所拥有的。可以不学无术&#xff0c;但不能一无所有&#xff01; 技术领域&#xff1a;WEB安全、网络攻防 关注WEB安全、网络攻防。我的…

iptables防火墙2

iptables防火墙 一&#xff1a;SNAT原理与应用 SNAT 应用环境&#xff1a;局域网主机共享单个公网IP地址接入Internet&#xff08;私有不能早Internet中正常路由&#xff09;SNAT原理&#xff1a;修改数据包的源地址。 SNAT转换前提条件&#xff1a; 1.局域网各主机已正确设…

新星计划 Electron+vue2 桌面应用 2 搭建及运行

基础内容&#xff1a;新星计划 Electronvue2 桌面应用 1 基础_lsswear的博客-CSDN博客 根据使用过的经验和官网的描述&#xff0c;大概可以有四种方式&#xff1a; 自己创建项目&#xff08;仅使用npm&#xff09;用Electron脚手架HBuilder编译为web&#xff0c;再用Electron…

MSP432笔记4:时钟与滴答计时器

所用单片机型号&#xff1a;MSP432P401r 今日继续更新我的MSP432电赛速通笔记&#xff1a; 提示&#xff1a; 本节内容相当于讲述delay_ms&#xff08;&#xff09; 和delay_us&#xff08;&#xff09; 俩延时函数的由来&#xff0c; 所以不需要花费过多时间斟酌 MSP432单…