Spring Security实现用户认证三:结合MySql数据库对用户进行认证

Spring Security实现用户认证三:结合MySql数据库对用户进行认证

  • 1 原理
  • 2 基于内存的认证(默认方式)
    • 2.1 依赖
    • 2.2 WebSecurityConfig配置类添加配置
  • 3 为下一步准备数据源
    • 3.1 依赖
    • 3.2 创建表users和authorities
    • 3.3 配置DruidDataSource数据源
    • 3.4 创建实体类User
  • 4 基于JDBC的认证
    • 4.1 使用系统自带的JdbcUserDetailsManager
      • 依赖
      • 对WebSecurityConfig添加配置UserDetailsService
      • 定义UserController
      • 使用swagger3测试
    • 4.2 自定义MyJdbcUserDetailsManager

1 原理

在Spring Security实现用户认证一中说到,请求被过滤器UsernamePasswordAuthenticationFilter处理生成UsernamePasswordAuthenticationToken,实际上这里的token只是临时的,并没有进行认证,需要一个AuthenticationProvider提供认证方式。

如下官方所提供的一张原图清楚说明了后续的认证过程。
在这里插入图片描述
从上图可以看出,UsernamePasswordAuthenticationTokenProviderManager类对在Token匹配AuthenticationProvider

对于采用用户名和密码的认证方式,匹配到的是DaoAuthenticationProvider。进入到DaoAuthenticationProvider,这个类需要UserDetailsServicePasswordEncoder

UserDetailsService里面存储着用户的细节UserDetails,包括用户名、密文密码、权限等信息。PasswordEncoder是用来对密码进行加密的,默认的加密算法是BCryptPasswordEncoder

DaoAuthenticationProvider拿到用户请求中的username和明文password,也就是包裹在UsernamePasswordAuthenticationToken中的字段principalcredentials。如下图所示:
在这里插入图片描述
UserDetailsService里面是密文密码,所以需要应用对应的加密算法将用户的明文密码映射成密文密码。

UserDetailsService首先通过username查找UserDetails,将找到的UserDetails取出密文密码,再对比两个密文密码的一致性去认证用户信息。

下图展示了认证成功后的UsernamePasswordAuthenticationToken,认证成功的Token将会拿到用户的角色信息和授权信息。最终,返回的 UsernamePasswordAuthenticationToken 被设置在 SecurityContextHolder 上。
在这里插入图片描述
讲到这里我们大致知道怎么修改了。我们需要一个读取UserDetails的一个UserDetailsService,以及一个密码编码方法PasswordEncoder

2 基于内存的认证(默认方式)

其他配置请参考往期内容。

在不进行任何配置条件下,系统会默认生成一个username为user,密码随机的用户(在控制台打印)。
如果想要修改该默认用户的用户名和密码。可以在application.yml文件中添加如下内容:

spring:security:user:name: userpassword: 1234

这是就可以用user和1234进行登录。

下面请看如何添加新的用户到内存中。

2.1 依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>

2.2 WebSecurityConfig配置类添加配置

下面配置了一个基于内存的UserDetailsService,并且向其中添加了一个用户(root,root),该用户角色定义为USER。重新启动服务器可以使用该用户登录。这时系统默认的用户将会失效。

这里拥有我们所需要的两个条件UserDetailsServicePasswordEncoderwithDefaultPasswordEncoder会采用系统默认的密码编码器。

package com.song.cloud.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;@Configuration
@EnableWebSecurity  //开启SpringSecurity自动配置(springboot中可以省略)
public class WebSecurityConfig {//为存储在内存中的基于用户名/密码的认证提供支持。@Beanpublic UserDetailsService userDetailsService() {InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();manager.createUser(User.withDefaultPasswordEncoder().username("root").password("root").roles("USER").build());return manager;}@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {// ...}
}

上面的配置变可以启动服务器测试,输入(root,root)可以进行登录。

3 为下一步准备数据源

MySql 8.0.35,druid, mybatis

3.1 依赖

<!-- 数据库依赖 -->
<dependency><groupId>javax.persistence</groupId><artifactId>persistence-api</artifactId>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency><groupId>tk.mybatis</groupId><artifactId>mapper</artifactId>
</dependency>
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId>
</dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency>

3.2 创建表users和authorities

分别用于存放用户和权限。

create table users
(username varchar(50) not null primary key,password varchar(500) not null,enabled boolean not null
);create table authorities
(username varchar(50) not null,authority varchar(50) not null,constraint fk_authorities_users foreign key (username) references users (username)
);
create unique index ix_auth_username on authorities (username, authority);

3.3 配置DruidDataSource数据源

application.yml配置

spring:application:name: spring-securitysecurity:user:name: userpassword: 1234datasource:driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourceusername: root  # 修改成自己的数据库username和passwordpassword: root# 请把test_db修改成自己数据库名字url: jdbc:mysql://localhost:3306/test_db?characterEncoding=utf8&useSSL=false&serverTimeZone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=trueserver:port: 4555logging:level:web: debugmybatis:mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.song.cloud.entitiesconfiguration:map-underscore-to-camel-case: true

下面请根据实际情况修改,会用mybatis-generator插件的请自行生成。不会请查阅其他教程。

3.4 创建实体类User

package com.song.cloud.entities;import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;/*** 表名:t_users_test
*/
@Table(name = "t_users_test")
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {/*** id*/@Id@GeneratedValue(generator = "JDBC")private Long id;/*** 用户名*/private String username;/*** 密码hash*/@Column(name = "password_hash")private String passwordHash;/*** 是否启用*/private Boolean enable;
}

4 基于JDBC的认证

4.1 使用系统自带的JdbcUserDetailsManager

实现的JdbcUserDetailsManager类已经封装了默认的增删改查的sql语句,但是事实上这样做非常麻烦,也不符合编程习惯,虽然官方提供了对语句的修改功能。

依赖

<!-- spring security -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>

对WebSecurityConfig添加配置UserDetailsService

import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;import javax.sql.DataSource;@Configuration
@EnableWebSecurity  //开启SpringSecurity自动配置(springboot中可以省略)
public class WebSecurityConfig {@Beanpublic JdbcUserDetailsManager jdbcUserDetailsManager(DataSource dataSource){JdbcUserDetailsManager manager = new JdbcUserDetailsManager(dataSource);// ----------------------- 向数据库添加用户和权限 ----------------UserDetails user = User.withDefaultPasswordEncoder().username("user").password("user").roles("USER").build();UserDetails admin = User.withDefaultPasswordEncoder().username("admin").password("admin").roles("ADMIN", "USER").build();manager.createUser(user);manager.createUser(admin);// -----------------------结束向数据库添加用户和权限 ----------------return manager;}// ...
}

现在,可以使用这两个用户进行登录。

在服务器启动时,这两个用户便被写进数据库中。下次启动需要注释向数据库添加用户和权限的这些,或者将数据库中的users和authorities两个表数据删掉,否则会重复键值而报错, 先删authorities的数据

定义UserController

@PostMapping("/user/add")
public UserDetails addUser(@RequestBody User user) {System.out.println(user);PasswordEncoder delegatingPasswordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();String pd_encode = delegatingPasswordEncoder.encode(user.getPasswordHash());UserDetails userDetails = org.springframework.security.core.userdetails.User.withUsername(user.getUsername()).password(pd_encode).roles("USER") //自己定义.disabled(!user.getEnable()).build();jdbcUserDetailsManager.createUser(userDetails);return userDetails;
}

使用swagger3测试

<dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>

测试地址:http://localhost:port/swagger-ui/index.html 改成项目端口号

进入测试地址前,需要使用之前添加进入db的账户登录。
在这里插入图片描述
测试数据:

{"username": "test","passwordHash": "test","enable": true
}

在这里插入图片描述

在这里插入图片描述
测试登录:
在这里插入图片描述

4.2 自定义MyJdbcUserDetailsManager

怎么更加随心所欲的定制一下。

建议使用@Service 注解形式注册,由于需要使用dao层的服务,采用自动注入的形式必须使用@Service 标识为bean,交给spring IoC管理,才能实现自动注入dao层服务。

import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.provisioning.UserDetailsManager;
import tk.mybatis.mapper.entity.Example;@Service  //建议使用@Service 注解形式注册
public class DBUserDetailsManager implements UserDetailsManager, UserDetailsPasswordService {@Resourceprivate UserMapper userMapper;  // 自定义的dao层@Overridepublic UserDetails updatePassword(UserDetails user, String newPassword) {return null;}@Overridepublic void createUser(UserDetails userDetails) {User user = new User();user.setUsername(userDetails.getUsername());user.setPasswordHash(userDetails.getPassword());userMapper.insertSelective(user);}@Overridepublic void updateUser(UserDetails userDetails) {// 自己定义}@Overridepublic void deleteUser(String username) {// 自己定义}@Overridepublic void changePassword(String oldPassword, String newPassword) {// 自己定义}@Overridepublic boolean userExists(String username) {// 自己定义return false;}/*** 从数据库中获取用户信息,继续引入持久层,UserMapper** @param username the username identifying the user whose data is required.* @return* @throws UsernameNotFoundException*/@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {Example selectByUserId = new Example(User.class);Example.Criteria criteria = selectByUserId.createCriteria();criteria.andEqualTo("username", username);User user = userMapper.selectOneByExample(selectByUserId);if (user == null) {throw new UsernameNotFoundException(username);}Collection<GrantedAuthority> authorities = new ArrayList<>();authorities.add(() -> "USER_LIST");authorities.add(() -> "USER_ADD");return org.springframework.security.core.userdetails.User.withUsername(user.getUsername()).password(user.getPasswordHash()).disabled(false).credentialsExpired(false).accountLocked(false).roles("ADMIN").build();}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

KDE-Ambari-Metrics-Collector问题排查解决手册

文档说明 本文档是为了解决KDE平台的Ambari-Metrics-Collector服务在运行时遇到的问题而提供的问题排查和解决方法的参考文档 说明: 当前的Ambari-Metrics-Collector服务包括了ams-collector和ams-hbase两个程序,在Ambari-Metrics-Collector安装的节点执行ps -elf|grep am…

【热门话题】一文带你读懂公司是如何知道张三在脉脉上发了“一句话”的

按理说呢&#xff0c;A公司和脉脉属于不同的平台&#xff0c;而且脉脉上大家可以匿名发言&#xff0c;所以&#xff0c;即便我坐在你边上&#xff0c;我发了一句话上去&#xff0c;你也不知道是谁发的。但通过一些技术&#xff0c;我们却可以分析出&#xff0c;公司是如何知道张…

2024 电工杯高校数学建模竞赛(A题)数学建模完整思路+完整代码全解全析

你是否在寻找数学建模比赛的突破点&#xff1f;数学建模进阶思路&#xff01; 作为经验丰富的数学建模团队&#xff0c;我们将为你带来2024电工杯数学建模竞赛&#xff08;B题&#xff09;的全面解析。这个解决方案包不仅包括完整的代码实现&#xff0c;还有详尽的建模过程和解…

AI网络爬虫:批量爬取电视猫上面的《庆余年》分集剧情

电视猫上面有《庆余年》分集剧情&#xff0c;如何批量爬取下来呢&#xff1f; 先找到每集的链接地址&#xff0c;都在这个class"epipage clear"的div标签里面的li标签下面的a标签里面&#xff1a; <a href"/drama/Yy0wHDA/episode">1</a> 这个…

玩转OpenHarmony PID:教你打造两轮平衡车

简介 此次为大家带来的是OpenAtom OpenHarmony&#xff08;以下简称“OpenHarmony”&#xff09;系统与PID控制算法相结合并落地的平衡车项目。 PID控制算法是一种经典的&#xff0c;并被广泛应用在控制领域的算法。类似于这种&#xff1a;需要将某一个物理量保持稳定的场合&…

增强版 Kimi:AI 驱动的智能创作平台,实现一站式内容生成(图片、PPT、PDF)!

前言 基于扣子 Coze 零代码平台&#xff0c;我们从零到一轻松实现了专属 Bot 机器人的搭建。 AI 大模型&#xff08;LLM&#xff09;、智能体&#xff08;Agent&#xff09;、知识库、向量数据库、知识图谱&#xff0c;RAG&#xff0c;AGI 的不同形态愈发显现&#xff0c;如何…

Redis系统架构中各个处理模块是干什么的?no.19

Redis 系统架构 通过前面的学习&#xff0c;相信你已经掌握了 Redis 的原理、数据类型及访问协议等内容。本课时&#xff0c;我将进一步分析 Redis 的系统架构&#xff0c;重点讲解 Redis 系统架构的事件处理机制、数据管理、功能扩展、系统扩展等内容。 事件处理机制 Redis…

分布式限流总结

1、计数器 java内部可以使用原子计数器AtomicInteger\Semaphore信号量来做简单的限流 // 限流的个数private int maxCount 10;// 指定的时间内private long interval 60;// 原子类计数器private AtomicInteger atomicInteger new AtomicInteger(0);// 起始时间private lon…

【笔记】树(Tree)

一、树的基本概念 1、树的简介 之前我们都是在谈论一对一的线性数据结构&#xff0c;可现实中也有很多一对多的情况需要处理&#xff0c;所以我们就需要一种能实现一对多的数据结构--“树”。 2、树的定义 树&#xff08;Tree&#xff09;是一种非线性的数据结构&#xff0…

百度智能云参与信通院多项边缘计算标准编制,「大模型时代下云边端协同 AI 发展研讨会」成功召开

1 中国信通院联合业界制定、发布多项标准化成果&#xff0c;推动产业发展 大模型开启了 AI 原生时代&#xff0c;云边端协同 AI 构建了「集中式大规模训练」、「边缘分布式协同推理」新范式&#xff0c;有效降低推理时延和成本&#xff0c;提升数据安全和隐私性&#xff0c;也…

基于Vue3 + js-tool-big-box工具库实现3个随机数字的小游戏动画,快来挑战你的非凡手气!

不知你是否和我一样&#xff0c;我曾有一个猜3个随机数字的梦&#xff0c;但通过多次的努力&#xff0c;梦想最终未能实现&#xff0c;而且还波多了我的饭票。所以&#xff0c;我要通过vue3 js-tool-big-box 这个工具库&#xff0c;来实现一个猜3个随机数字的小游戏动画&#…

学习通高分免费刷课实操教程

文章目录 概要整体架构流程详细步骤云上全平台登录步骤小结 概要 我之前提到过一个通过浏览器的三个脚本就可以免费高分刷课的文章&#xff0c;由于不方便拍视频进行实操演示&#xff0c;然后写下了这个实操教程&#xff0c;之前的三个脚本划到文章末尾 整体架构流程 整体大…

一键批量提取TXT文档前N行,高效处理海量文本数据,省时省力新方案!

大量的文本信息充斥着我们的工作与生活。无论是研究资料、项目文档还是市场报告&#xff0c;TXT文本文档都是我们获取和整理信息的重要来源。然而&#xff0c;面对成百上千个TXT文档&#xff0c;如何快速提取所需的关键信息&#xff0c;提高工作效率&#xff0c;成为了许多人头…

我的第一个JAVA程序IDEA版

目录 第一步 新建一个空项目第二步 新建模块第三步 新建包第四步 新建类第五步 新建main方法 第一步 新建一个空项目 第二步 新建模块 第三步 新建包 第四步 新建类 然后在包文件夹下新建类 第五步 新建main方法

Java开发之JDBC

JDBC 介绍JDBC程序&#xff08;Statement&#xff09;相关细节URLResultSet 连接池程序&#xff08;PreparedStatement&#xff09; 本文主要记录一下学习JDBC的一些知识点 介绍JDBC 首先谈谈什么是JDBC。下面放几张图&#xff0c;大致就可以清楚JDBC了。程序&#xff08;Sta…

RPC原理技术

RPC原理技术 背景介绍起源组件实现工作原理 背景 本文内容大多基于网上其他参考文章及资料整理后所得&#xff0c;并非原创&#xff0c;目的是为了需要时方便查看。 介绍 RPC&#xff0c;Remote Procedure Call&#xff0c;远程过程调用&#xff0c;允许像调用本地方法一样调…

bytebuddy入门

简介 Byte Buddy 是一个代码生成和操作库&#xff0c;用于在 Java 应用程序运行时创建和修改 Java 类&#xff0c;而无需编译器的帮助。除了 Java 类库附带的代码生成实用程序外&#xff0c;Byte Buddy 还允许创建任意类&#xff0c;并且不限于实现用于创建运行时代理的接口 核…

【信息安全】

信息安全 1.防火墙技术 防火墙是建立在内外网络边界上的过滤封锁机制。 补充内容 防火墙工作层次越低&#xff0c;工作效率越高&#xff0c;安全性越低DMZ的作用是保存一些公共服务器&#xff08;Web服务器、Eamil服务器&#xff09;防火墙的安全性从高到底&#xff1a;内网…

kafka Kerberos集群环境部署验证

背景 公司需要对kafka环境进行安全验证,目前考虑到的方案有Kerberos和SSL和SASL_SSL,最终考虑到安全和功能的丰富度,我们最终选择了SASL_SSL方案。处于知识积累的角度,记录一下kafka keberos安装部署的步骤。 机器规划 目前测试环境公搭建了三台kafka主机服务,现在将详细…

微软:最新ChatGPT-4o模型,可在 Azure OpenAI上使用

北京时间5月14日凌晨&#xff0c;OpenAI 一场不到 30 分钟的发布会&#xff0c;正式发布了 GPT-4o&#xff0c;视频语音交互丝滑到吓人&#xff0c;还即将免费可用&#xff01; GPT-4o&#xff0c;其中的「o」代表「omni」&#xff08;即全面、全能的意思&#xff09;&#xff…