(2)Spring Security - 了解UserDetailsService

news2024/12/14 0:40:34

目录

  • 1.认识UserDetailsService
    • 1.1.认识UserDetails
    • 1.2.UserDetailsService的默认实现 -- InMemoryUserDetailsManager
  • 2.用户信息存储在MySQL数据库中
    • 2.1.添加依赖
    • 2.2.配置MySQL和Mybatis
    • 2.3.在数据库中添加用户信息
    • 2.4.添加数据库实体类
    • 2.5.编写Mybatis代码
    • 2.6.实现UserDetails接口
    • 2.7.实现UserDetailsService接口
    • 2.8.编写配置文件
    • 2.9.编写控制器
    • 2.10.单元测试

本章目标:

  • 认识UserDetailsService
  • 尝试使用MySQL储存用户信息

1.认识UserDetailsService

在这里插入图片描述
如图所示,Authentication Filter将身份验证请求委托给AuthenticationManager,后者使用AuthenticationProvider处理身份验证。

Authentication Provider使用UserDetailsService来实现用户管理职责。它的主要职责是根据用户名从内存中或数据库中查找用户。

package org.springframework.security.core.userdetails;

/**
 * 加载用户特定数据的核心接口。
 * 它作为用户DAO在整个框架中使用,并且是DaoAuthenticationProvider使用的策略。
 * 
 * 该接口只需要一个只读方法,这简化了对新数据访问策略的支持。
 */
public interface UserDetailsService {

	/**
	 * 根据用户名定位用户。在实际实现中,搜索可能区分大小写,也可能不区分大小写,这取决于如何配置实现实例。
	 * 在这种情况下,返回的UserDetails对象可能有一个不同于实际请求的用户名。
	 * 
	 * 参数 - username:标识需要其数据的用户的用户名。
	 * 返回值 - 完全填充的用户记录(绝不为空)
	 * 异常 - UsernameNotFoundException 如果找不到用户或用户没有授予权限
	 */
	UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

在这里插入图片描述

1.1.认识UserDetails

‌UserDetails是Spring Security框架中的一个核心接口,用于表示用户的详细信息。

UserDetails接口定义在org.springframework.security.core.userdetails包下,主要用于封装从数据库中加载的用户详细信息。这些属性包括用户名、密码、权限、账户状态等,确保用户信息的安全性和完整性‌。

package org.springframework.security.core.userdetails;

import java.io.Serializable;
import java.util.Collection;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;

/*
 * 提供核心用户信息。
 * 
 * Spring Security不会出于安全目的直接使用实现。它们只是存储用户信息,这些信息稍后封装到Authentication对象中。
 * 这允许非安全相关的用户信息(如电子邮件地址,电话号码等)存储在一个方便的位置。
 * 
 * 具体实现必须特别注意确保执行每个方法的非空契约。有关参考实现(您可能希望在代码中扩展或使用),
 * 请参阅org.springframework.security.core.userdetails.User。
 * 
 */
 public interface UserDetails extends Serializable {

	/**
	 * 返回授予用户的权限。不能返回null。
	 * 返回值:按自然键排序的权限集(绝不为空)
	 */
	Collection<? extends GrantedAuthority> getAuthorities();

	/**
	 * 返回用于验证用户身份的密码。
	 */
	String getPassword();

	/**
	 * 返回用于验证用户的用户名。不能返回null。
	 */
	String getUsername();

	/**
	 * 用户帐号是否过期。过期的帐户无法进行身份验证。
	 * 返回值:如果用户的帐户有效(即未过期)则为true,如果不再有效(即过期)则为false
	 */
	boolean isAccountNonExpired();

	/**
	 * 显示用户处于锁定状态或未锁定状态。被锁定的用户无法进行认证。
	 * 返回值:如果用户未被锁定则返回true,否则返回false
	 */
	boolean isAccountNonLocked();

	/**
	 * 指示用户的凭据(密码)是否已过期。过期凭据阻止身份验证。
	 * 返回值:如果用户的凭据有效(即未过期)则返回true,如果不再有效(即过期)则返回false
	 */
	boolean isCredentialsNonExpired();

	/**
	 * 显示用户是否启用或禁用。禁用的用户无法进行认证。
	 * 返回值:如果用户被启用,返回true,否则返回false
	 */
	boolean isEnabled();

}

在这里插入图片描述

1.2.UserDetailsService的默认实现 – InMemoryUserDetailsManager

InMemoryUserDetailsManager是UserDetailsManager的非持久性实现,他将用户信息存储在内存中,断电后信息会丢失。

通过查看org.springframework.security.provisioning.InMemoryUserDetailsManager的源代码,可以知道这个实现类包含如下功能:

  • private final Map<String, MutableUserDetails> users:使用map将数据存储在内存中
  • ‌SecurityContextHolderStrategy‌是Spring Security框架中的一个接口,用于定义如何存储和管理SecurityContext。SecurityContext是Spring Security中用于存储当前用户安全信息的容器,而SecurityContextHolderStrategy则定义了SecurityContext的存储策略。
  • AuthenticationManager在Spring Security中的作用是处理认证请求,并协调多个AuthenticationProvider进行用户凭证的验证‌。AuthenticationManager是一个接口,定义了认证流程的核心方法,主要负责将Authentication对象传递给合适的AuthenticationProvider进行认证。如果认证成功,它会返回一个包含认证成功信息的Authentication对象;如果失败,则会抛出异常‌
  • 通过构造函数创建新用户
  • public void createUser(UserDetails user):创建不存在的用户
  • public void deleteUser(String username):删除用户
  • public void updateUser(UserDetails user):更新已存在的用户
  • public boolean userExists(String username):用户是否存在
  • public void changePassword(String oldPassword, String newPassword):修改当前登录的用户的密码
  • public UserDetails updatePassword(UserDetails user, String newPassword):更新指定用户的密码
  • public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException:通过用户名加载用户信息,用于认证
  • public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy):配置SecurityContextHolderStrategy‌
  • public void setAuthenticationManager(AuthenticationManager authenticationManager):配置AuthenticationManager

2.用户信息存储在MySQL数据库中

2.1.添加依赖

<dependency>
	<groupId>com.mysql</groupId>
	<artifactId>mysql-connector-j</artifactId>
	<scope>runtime</scope>
</dependency>
<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<scope>annotationProcessor</scope>
</dependency>
<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
	<version>3.0.3</version>
</dependency>

2.2.配置MySQL和Mybatis

application-dev.properties编写开发环境配置文件

# 配置MySQL
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ipms?characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456

# 配置Mybatis
mybatis.mapper-locations=classpath:/mapper/*.xml
mybatis.type-aliases-package=com.drson.usermanagement.entity
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

application.properties编写主配置文件

spring.application.name=userManagement
# 激活开发环境配置文件
spring.profiles.active=dev

2.3.在数据库中添加用户信息

# 在数据库中添加用户信息
# 创建数据库
create database if not exists ipms character set utf8mb4 collate utf8mb4_unicode_ci;
# 选择数据库
use ipms;
# 创建用户表
create table if not exists employee
(
    id                      int auto_increment primary key comment '员工ID',
    job_number              varchar(8)   not null unique comment '工号',
    name                    varchar(10)  not null comment '姓名',
    gender                  tinyint      not null comment '性别:1为男性,0为女性',
    age                     tinyint      not null comment '年龄',
    phone                   varchar(11)  not null comment '电话号码',
    address                 varchar(255) not null comment '住址',
    id_card_number          varchar(20)  not null comment '身份证号码',
    educational_background  varchar(4)   not null comment '学历',
    password                varchar(60)  not null comment '登录管理系统的密码',
    isAccountNonExpired     boolean      not null comment '账号是否过期',
    isAccountNonLocked      boolean      not null comment '账号是否被锁定',
    isCredentialsNonExpired boolean      not null comment '密码是否过期',
    isEnabled               boolean      not null comment '账户是否可用'
);
# 添加一个用户,用于测试,密码123456
insert into employee (job_number, name, gender, age, phone, address, id_card_number, educational_background, password,
                      isAccountNonExpired, isAccountNonLocked, isCredentialsNonExpired, isEnabled) VALUE
    (
     '00000000',
     'test',
     1,
     30,
     '18888888888',
     '测试住址',
     '511133197301240418',
     '大专',
     '$2y$16$xvsERHKdRrdDgLJeLdthFuLk/Tlv1rpfuGpftC8PDpZwZzCAfxq9i',
     true, true, true, true
        );
# 查询表格
select * from employee;

其中的加密后的密码$2y$04$Z6tlj.KTXI5AEEmgGPZ1J.Gxq..h8OSyUF30bku551ImynFVlQnw6,使用下面的测试代码生成:

	@Test
	public void encodePassword(){
		/*
		 * BCryptPasswordEncoder()的参数说明:
		 * 	strength:定义了哈希计算的时间因子(以 rounds 表示),其中值越大,计算哈希所需的时间就越长,从而增加了攻击者破解密码的难度。默认值是10,范围4-31
		 *	version:用于指定BCrypt算法的版本,可以是2a,2b,2y
		 *	random:用于生成随机的盐
		 */
		BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2Y,4,new SecureRandom());
		String encode = bCryptPasswordEncoder.encode("123456");
		System.out.println(encode);
	}

2.4.添加数据库实体类

在这里插入图片描述
在这里插入图片描述
修改生成的实体类,如下:

package com.drson.usermanagement.entity;//注意:修改包名

import lombok.Data;

@Data //添加注解
public class Employee {
  //使用包装类
  private Long id;
  private String jobNumber;
  private String name;
  private Long gender;
  private Long age;
  private String phone;
  private String address;
  private String idCardNumber;
  private String educationalBackground;
  private String password;
  private Long isAccountNonExpired;
  private Long isAccountNonLocked;
  private Long isCredentialsNonExpired;
  private Long isEnabled;
  //删除所有getter和setter
}

2.5.编写Mybatis代码

EmployeeMapper.java

package com.drson.usermanagement.mapper;

import com.drson.usermanagement.entity.Employee;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper //添加Mybatis注解
public interface EmployeeMapper {
    /**
     * 根据用户名,查询用户信息
     *
     * @param name 用户名
     */
    @Select("select * from employee where name=#{name}")
    Employee getEmployeeByName(String name);
}

2.6.实现UserDetails接口

EmployeeDetails.java

package com.drson.usermanagement.service;

import com.drson.usermanagement.entity.Employee;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.List;

public class EmployeeDetails implements UserDetails {

    private final Employee employee;

    public EmployeeDetails(Employee employee){
        this.employee = employee;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return List.of();
    }

    @Override
    public String getPassword() {
        return employee.getPassword();
    }

    @Override
    public String getUsername() {
        return employee.getName();
    }

    @Override
    public boolean isAccountNonExpired() {
        return employee.getIsAccountNonExpired() == 1;
    }

    @Override
    public boolean isAccountNonLocked() {
        return employee.getIsAccountNonLocked() == 1;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return employee.getIsCredentialsNonExpired() == 1;
    }

    @Override
    public boolean isEnabled() {
        return employee.getIsEnabled() == 1;
    }
}

2.7.实现UserDetailsService接口

EmployeeDetailsService.java

package com.drson.usermanagement.service;

import com.drson.usermanagement.entity.Employee;
import com.drson.usermanagement.mapper.EmployeeMapper;
import jakarta.annotation.Resource;
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.Component;

import java.util.Objects;

@Component
public class EmployeeDetailsService implements UserDetailsService {
    @Resource
    private EmployeeMapper employeeMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Employee employee = employeeMapper.getEmployeeByName(username);
        if (Objects.isNull(employee)){
            //找不到用户,抛出异常
            throw new UsernameNotFoundException(username);
        }
        //封装用户信息
        return new EmployeeDetails(employee);
    }
}

2.8.编写配置文件

WebSecurityConfig.java

package com.drson.usermanagement.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import java.security.SecureRandom;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
    //定义密码编码器
    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

2.9.编写控制器

TestController.java

package com.drson.usermanagement.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
    @GetMapping("/test")
    public String test(){
        return "test";
    }
}

2.10.单元测试

Chapter2Test.java

package com.drson.usermanagement;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

@SpringBootTest
@AutoConfigureMockMvc
public class Chapter2Test {
	@Autowired
    private MockMvc mockMvc;

    @Test
    @WithMockUser(username = "test",password = "123456")
    public void test() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/test"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content().string("test"));
    }
}

在这里插入图片描述
测试通过!

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

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

相关文章

智能设备安全-固件逆向分析

固件逆向分析实验报告-20241022 使用固件常用逆向分析工具&#xff0c;对提供的固件进行文件系统提取&#xff0c;并记录逆向分析实验过程&#xff0c;提交实验报告&#xff08;报告要求图文并茂&#xff0c;对涉及到的关键步骤附截图说明&#xff09;。具体任务如下&#xff1…

图形编辑器基于Paper.js教程17:图像转gcode前的处理,灰度,黑白,抖动

好久没有正经写博客了&#xff0c;前一段时间一直在备考中级项目管理&#xff0c;再加上项目开发只有自己一个人&#xff0c;每天忙的飞起。有闲暇时间也不想写&#xff0c;其中一部分原因也是因为很多简单问题&#xff0c;AI就能回答的很好。而对复杂的问题&#xff0c;也不是…

AI大模型学习笔记|人工智能的发展历程、智能体的发展、机器学习与深度学习的基本理论

学习链接&#xff1a;冒死上传&#xff01;价值2W的大模型入门到就业教程分享给大家&#xff01;轻松打造专属大模型助手&#xff0c;—多模态、Agent、LangChain、ViT、NLP_哔哩哔哩_bilibili 百度网盘自己整理的笔记&#xff1a; 通过网盘分享的文件&#xff1a;1-人工智能的…

qt 设置系统缩放为150%,导致的文字和界面的问题

1 当我们设置好布局后&#xff0c;在100%的设置里面都是正常的&#xff0c;但是当我们修改缩放为150%后&#xff0c;字体图标&#xff0c;界面大小就出现问题了&#xff0c;这就需要我们设置一些参数。 QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);QCoreAppl…

linux-15 关于shell(十四)printenv,hash,环境变量

此前没有用过linux&#xff0c;可能有些觉得很奇怪&#xff0c;就觉得我们在这敲来敲去命令干什么&#xff1f;为什么不使用双击这种方式来操作&#xff1f;大家知道&#xff0c;在Windows里面&#xff0c;其实我们双击也无非就是告诉我们shell需要将这个命令发送在内核上启动的…

虚拟机如何使用物理机的公私钥

一、生成公私钥&#xff08;如果没有的话&#xff09; 使用如下指令生成 生成RSA公私钥 ssh-keygen 生成EdDSA公私钥 ssh-keygen -t ed25519 Windows目录 linux会直接生成在当前目录下。 二、导出 一般都是从windows系统导入到linux系统。 可以直接将公私钥文件复制到虚拟机…

SpringBoot【十一】mybatis-plus实现多数据源配置,开箱即用!

一、前言&#x1f525; 环境说明&#xff1a;Windows10 Idea2021.3.2 Jdk1.8 SpringBoot 2.3.1.RELEASE 正常情况下我们在开发系统的时候都是使用一个数据源&#xff0c;但是由于有些项目同步数据的时候不想造成数据库io消耗压力过大&#xff0c;便会一个项目对应多个数据源…

前端报错npm ERR cb() never called问题

环境使用node版本v14.21.3&#xff0c;npm版本6.14.18 1.问题描述 1.1使用npm install后报错 npm ERR! cb() never called!npm ERR! This is an error with npm itself. Please report this error at: npm ERR! ? ? <https://npm.community>npm ERR! A complete log…

C++ STL Cookbook STL算法

目录 std::copy 将容器元素合并为一个字符串 使用 std::sort 对容器进行排序 使用 std::transform 修改容器 在容器中查找项目 使用 std::sample 采样数据集 (写在前面&#xff1a;笔者前段时间备战考试和比赛了&#xff0c;现在回来继续更新) STL实际上提供了非常非常丰…

SpringBoot【十】mybatis之xml映射文件>、<=等特殊符号写法!

一、前言&#x1f525; 环境说明&#xff1a;Windows10 Idea2021.3.2 Jdk1.8 SpringBoot 2.3.1.RELEASE 在利用mybatis进行开发的时候&#xff0c;编写sql时可能少不了>、<等比较符号&#xff0c;但是在mapper映射文件中直接使用是不行的&#xff0c;会报错&#xff0…

单元测试SpringBoot

添加测试专用属性 加载测试专用bean Web环境模拟测试 数据层测试回滚 测试用例数据设定

每天40分玩转Django:简介和环境搭建

Django简介和环境搭建 一、课程概述 学习项目具体内容预计用时Django概念Django框架介绍、MVC/MTV模式、Django特点60分钟环境搭建Python安装、pip配置、Django安装、IDE选择45分钟创建项目项目结构、基本配置、运行测试75分钟实战练习创建个人博客项目框架60分钟 二、Djang…

Jenkins参数化构建详解(This project is parameterized)

本文详细介绍了Jenkins中不同类型的参数化构建方法&#xff0c;包括字符串、选项、多行文本、布尔值和git分支参数的配置&#xff0c;以及如何使用ActiveChoiceParameter实现动态获取参数选项。通过示例展示了传统方法和声明式pipeline的语法 文章目录 1. Jenkins的参数化构建1…

Windows安装WSL子系统及docker,以及WSL和docker配置、使用及问题解决

在Windows操作系统中,Ubuntu子系统(也称为Windows Subsystem for Linux, WSL)为开发者提供了一个在Windows环境下运行Linux环境的平台。然而,有时用户在按照Ubuntu子系统或者使用WSL时,可能会遇到各种问题,下面总结一下解决方式。 想要在Windows上安装Docker(实际上是基…

Linux中的线程

目录 线程的概念 进程与线程的关系 线程创建 线程终止 线程等待 线程分离 原生线程库 线程局部存储 自己实现线程封装 线程的优缺点 多线程共享与独占资源 线程互斥 互斥锁 自己实现锁的封装 加锁实现互斥的原理 死锁 线程同步 线程的概念 回顾进程相关概念 …

shell编程(完结)

shell编程&#xff08;完结&#xff09; 声明&#xff01; 学习视频来自B站up主 ​泷羽sec​​ 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章 笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其…

ctfshow-web 151-170-文件上传

151. 我们首先想到就是上传一句话木马。但是看源代码限制了png。 &#xff08;1&#xff09;改前端代码。 这里是前端限制了上传文件类型&#xff0c;那我们就改一下就好了嘛,改成php。 这里直接修改不行&#xff0c;给大家推荐一篇简短文章&#xff0c;大家就会了&#xff08…

Docker的初识

目录 1. 容器技术发展史1.1 Jail 时代1.2 云时代1.3 云原生时代1.3.1 Google & Docker 竞争1.3.2 k8s 成为云原生事实标准 2. 虚拟化和容器化的概念2.1 什么是虚拟化、容器化2.2 为什么要虚拟化、容器化&#xff1f;2.3 虚拟化实现方式2.3.1 应用程序执行环境分层2.3.2 虚拟…

Jenkins流水线初体验(六)

DevOps之安装和配置 Jenkins (一) DevOps 之 CI/CD入门操作 (二) Sonar Qube介绍和安装(三) Harbor镜像仓库介绍&安装 (四) Jenkins容器使用宿主机Docker(五) Jenkins流水线初体验(六) 一、Jenkins流水线任务介绍 之前采用Jenkins的自由风格构建的项目,每个步骤…

链式设计模式——装饰模式和职责链模式

一、装饰模式 1、概述 动态地给一个对象添加一些额外的职责&#xff0c;就增加功能来说&#xff0c;装饰模式比生成子类更为灵活。 ConcreteComponent &#xff1a;是定义了一个具体的对象&#xff0c;可以给这个对象添加一些职责&#xff1b;Decorator &#xff1a;装饰抽象…