Spring Security 定制UserDetailsService,动态uri权限,Thymeleaf,限制密码强度、过期、错误密码锁定超时自动解锁、禁用历史密码、新密码和现密码差异要求编辑距离

 在本教程中,我将指导您如何编写代码,以使用具有基于表单的身份验证的Spring安全API来保护Spring Boot应用程序中的网页。用户详细信息存储在MySQL数据库中,并使用春季JDBC连接到数据库。我们将从本教程中的 ProductManager 项目开始,向现有的弹簧启动项目添加登录和注销功能。

1. 创建用户表和虚拟凭据

凭据应存储在数据库中,因此让我们创建新表,表间关系ER图如下:

-- --------------------------------------------------------
-- 主机:                           127.0.0.1
-- 服务器版本:                        8.0.22 - MySQL Community Server - GPL
-- 服务器操作系统:                      Win64
-- HeidiSQL 版本:                  12.1.0.6537
-- --------------------------------------------------------

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;


-- 导出 product3 的数据库结构
CREATE DATABASE IF NOT EXISTS `product3` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;
USE `product3`;

-- 导出  表 product3.history 结构
CREATE TABLE IF NOT EXISTS `history` (
  `id` int NOT NULL AUTO_INCREMENT,
  `password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 正在导出表  product3.history 的数据:~17 rows (大约)
INSERT INTO `history` (`id`, `password`) VALUES
	(1, NULL),
	(2, NULL),
	(3, NULL),
	(4, NULL),
	(5, NULL),
	(6, NULL),
	(7, '$2a$10$f8BTEI8AkiBQV60MvzpnVehdo9.AM6Zevvg8817dj5qbNuu.p3B22'),
	(8, '$2a$10$9.ZG.D.e8u4NhGkGCVVUcOqpOB1b6WpCM7hEdawqIFo1G7Mg6/bt.'),
	(9, '$2a$10$IK/CKJLp5jH7hQYjfTjZIeAPPNuI2rGQuEWMk8Z.Of9QkUvQC28X6'),
	(10, '$2a$10$Lv.4MczeZKZNJyi58HVtp.0wztSxduK53L7O836KQS5mxItAoQsRG'),
	(11, '$2a$10$7jbJuk9kHXXlZQOspT2T0O/Quj/zrMZC0CrPgQvUGSrhZRoub7kUO'),
	(12, '$2a$10$bh68pHYzneQEWElmQRT/qOp/00WldBypIacHESvZGMHZ3vBJiN88u'),
	(13, '$2a$10$8m5rM1JZ4RLcnOpuKXFtlOeXxHxtWHk7PuXumOJFGFnRA6o5hxz7.'),
	(14, '$2a$10$VSsuBdX6sM2jtCAZ/gOhIut.aqfoymVYceSIUP6ncVMNDJmT2VjJi'),
	(15, '$2a$10$.wqeNise86FpzDlZHrWISenOnOXryn71JNBlrhAM0Nv6Ae6M739t6'),
	(16, '$2a$10$fdl7T4Gx37W8YANpb.JaF.D04vxecmMGlidcPHhziCtRpI1dXj3Dy'),
	(17, '$2a$10$VcsIbKiV7VFHHFZZAMUtKeHl5HKlaB45jCdfoLAXgQd1i8rIwC2N.'),
	(18, '$2a$10$OrlRVA057ZlHM9MNJmWTzO26vawbNu3O.dViXuN8bLfBCpyDY20ni'),
	(19, '$2a$10$qyi1TCu6X/eAAdfXQqfmtuZF.zdYEMC2ZprdyFmBQVRshLUjLZpEC');

-- 导出  表 product3.permission 结构
CREATE TABLE IF NOT EXISTS `permission` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `uri` varchar(8192) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `method` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 正在导出表  product3.permission 的数据:~27 rows (大约)
INSERT INTO `permission` (`id`, `name`, `description`, `uri`, `method`) VALUES
	(3, 'product_create', '增加产品', '/product/new', 'GET'),
	(4, 'product_delete', '删除产品', '/product/delete/*', 'GET'),
	(5, 'product_save', '保存产品', '/product/save', 'POST'),
	(9, 'product', '列表产品', '/product', 'GET'),
	(10, 'product_edit', '编辑产品', '/product/edit/*', 'GET'),
	(11, 'register', '注册用户', '/register', 'GET'),
	(12, 'process_register', '保存用户', '/process_register', 'POST'),
	(13, 'user', '列表用户', '/user', 'GET'),
	(14, 'user/info', 'user/info', '/user/info', 'GET'),
	(15, 'user_save', '保存用户', '/user/save', 'POST'),
	(16, 'user_edit', '编辑用户', '/user/edit/*', 'GET'),
	(17, 'permission', '读取权限', '/permission', 'GET'),
	(18, 'permission_create', '增加权限', '/permission/new', 'GET'),
	(19, 'permission_delete', '删除权限', '/permission/delete/*', 'GET'),
	(20, 'permission_edit', '编辑权限', '/permission/edit/*', 'GET'),
	(21, 'permission_save', '保存权限', '/permission/save', 'POST'),
	(23, 'test1', '测试1', '/test1', 'GET1'),
	(24, 'user_delete', '删除用户', '/user/delete/*', 'GET'),
	(25, 'user_create', '增加用户', '/user/new', 'GET'),
	(26, 'role', '读取角色', '/role', 'GET'),
	(27, 'role_create', '创建角色', '/role/new', 'GET'),
	(28, 'role_save', '保存角色', '/role/save', 'POST'),
	(29, 'role_edit', '编辑角色', '/role/edit/*', 'GET'),
	(30, 'role_delete', '删除角色', '/role/delete/*', 'GET'),
	(31, 'user_update', '更新用户(不含密码)', '/user/update', 'POST'),
	(32, 'user_resetpassword', '重置密码', '/user/resetpassword/*', 'GET'),
	(33, 'user_savepassword', '保存重置密码', '/user/savepassword', 'POST'),
	(34, 'order_create', '创建订单', '/order/new', 'GET');

-- 导出  表 product3.product 结构
CREATE TABLE IF NOT EXISTS `product` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `brand` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `madein` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `price` float NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 正在导出表  product3.product 的数据:~4 rows (大约)
INSERT INTO `product` (`id`, `brand`, `madein`, `name`, `price`) VALUES
	(6, '6', '6', '61', 6),
	(7, '7', '7', '7', 7),
	(9, '66', '66', '66', 66),
	(14, '666', '中国', 'new', 111);

-- 导出  表 product3.role 结构
CREATE TABLE IF NOT EXISTS `role` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 正在导出表  product3.role 的数据:~3 rows (大约)
INSERT INTO `role` (`id`, `name`, `description`) VALUES
	(1, 'ADMIN', 'Administrator role'),
	(2, 'USER_P1', 'Perfil 1'),
	(3, 'USER_P2', 'Perfil 2');

-- 导出  表 product3.role_permission 结构
CREATE TABLE IF NOT EXISTS `role_permission` (
  `id` int NOT NULL AUTO_INCREMENT,
  `role_id` int NOT NULL DEFAULT '0',
  `permission_id` int NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `role_id_permission_id` (`role_id`,`permission_id`),
  KEY `FK_role_permission_permission` (`permission_id`),
  CONSTRAINT `FK_role_permission_permission` FOREIGN KEY (`permission_id`) REFERENCES `permission` (`id`),
  CONSTRAINT `FK_role_permission_role` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=44 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 正在导出表  product3.role_permission 的数据:~31 rows (大约)
INSERT INTO `role_permission` (`id`, `role_id`, `permission_id`) VALUES
	(1, 1, 3),
	(2, 1, 4),
	(3, 1, 5),
	(4, 1, 9),
	(5, 1, 10),
	(11, 1, 11),
	(12, 1, 12),
	(13, 1, 13),
	(14, 1, 14),
	(16, 1, 15),
	(15, 1, 16),
	(17, 1, 17),
	(18, 1, 18),
	(19, 1, 19),
	(20, 1, 20),
	(22, 1, 21),
	(23, 1, 24),
	(24, 1, 25),
	(25, 1, 26),
	(26, 1, 27),
	(29, 1, 28),
	(28, 1, 29),
	(27, 1, 30),
	(35, 1, 31),
	(36, 1, 32),
	(37, 1, 33),
	(8, 2, 5),
	(6, 2, 9),
	(7, 2, 10),
	(10, 3, 4),
	(9, 3, 9);

-- 导出  表 product3.user 结构
CREATE TABLE IF NOT EXISTS `user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `email` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `name` varchar(65) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `enabled` bit(1) DEFAULT b'1',
  `homepage` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '',
  `password_changed_time` datetime(6) DEFAULT NULL,
  `account_non_locked` bit(1) DEFAULT b'1',
  `failed_attempt` int DEFAULT '0',
  `lock_time` datetime(6) DEFAULT NULL,
  `account_non_expired` bit(1) NOT NULL,
  `credentials_non_expired` bit(1) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 正在导出表  product3.user 的数据:~14 rows (大约)
INSERT INTO `user` (`id`, `username`, `email`, `name`, `password`, `enabled`, `homepage`, `password_changed_time`, `account_non_locked`, `failed_attempt`, `lock_time`, `account_non_expired`, `credentials_non_expired`) VALUES
	(1, 'admin', 'admin@admin.com', '系统管理员', '$2a$10$6uilpgI7dsrj9q/Z3lN5GeKZhECGvhjIf6FwrhHdMLodiBxIEvM3y', b'1', '', '2022-11-18 16:15:05.186000', b'1', 0, NULL, b'1', b'1'),
	(2, 'u1', 'u1@example.com', 'User P1', '$2a$10$7lsSw2BeeDCmm6gYJBdJfuotFoDkfggWq8pWTjj7awbwywNdGHZ3u', b'1', 'home', NULL, b'1', 0, NULL, b'0', b'0'),
	(3, 'u2', 'u2@example.com', 'User P2', '$2a$10$2/LSmp3YoEOT97KzgrYODen7I88ErBovM2Qehw9DL1dW9DZ7DZSAm', b'1', '', NULL, b'1', 0, NULL, b'0', b'0'),
	(11, 'allway222', 'allway222@1.com', 'allway222', '$2a$10$WTg/WDKmYaYZUbFl39Z6au78WOG8LI9bcnvbbLs/.t5MjzwrBD60u', b'1', 'home', NULL, b'1', 0, NULL, b'0', b'0'),
	(12, 'allway666', 'allway666@1.com', 'allway66622', '$2a$10$Jeky6hyUSN3i0u6IPF8squDtjkFwHL7WzP3rRoF4QOEwWfVAzi/c2', b'1', 'home', NULL, b'1', 0, NULL, b'0', b'0'),
	(13, 'allway777', 'allway777@1.com', 'allway777', '$2a$10$W0TWw1.9lBYG7PW8KsEWcuNFGiOJAOobr55bKMQD8rf68GVppUH4S', b'1', 'home', NULL, b'1', 0, NULL, b'0', b'0'),
	(14, 'test', '1@1.com', 'test', '$2a$10$RMVwmkanbTyghH3tiyyIqO94OAqzDPa8X9sb3AVQY7OGHPFOJefUS', b'1', '', '2022-11-18 16:47:14.309000', b'1', 0, NULL, b'0', b'0'),
	(25, 'user102', 'Password@123.com', 'user102', '$2a$10$hZvr4Ao7ofNFdXHjPkRWXuCuuUUJmImhJjVAiiWxTBKRF2Rx5Z8RG', b'1', '', '2022-11-15 15:17:05.924000', b'1', 0, NULL, b'0', b'0'),
	(26, 'user103', 'Password@123.com', 'user103', '$2a$10$E6Zy8hSM6EVV53G8PjRyoeGdVi6Rj0hkkSQHvZPARoKTwwFD0kWza', b'1', '', '2022-11-16 12:20:26.190000', b'1', 0, NULL, b'0', b'0'),
	(27, 'user200', '1@1.com', 'user200', '$2a$10$8gy2nX5/nTfCPEmr8KPNfuOpQHgH9.2h6NQG0uBdlBzlK5n3jz6OC', b'1', '', '2022-11-16 14:45:42.963000', b'1', 0, NULL, b'0', b'0'),
	(28, 'user300', '1@1.com', 'user300', '$2a$10$0k1AJFxJ4/LbFyRvjH5KHONlZKDAoB34R8ze9wVcpIfkXChXPUnzu', b'1', '', '2022-11-16 14:56:20.506000', b'1', 0, NULL, b'0', b'0'),
	(30, 'user500', '1@1.com', 'user500', '$2a$10$LJc24EMmR9JsKnc6uDBpLufeKZK/jIonsLlGf5n67Tn9cOm6LvLfK', b'1', '', '2022-11-16 15:31:32.021000', b'1', 0, NULL, b'0', b'0'),
	(32, 'user700', '1@1.com', 'user700', '$2a$10$IWgF7b6xbv1Am9tiL.QvaehLJcyFChX.dAluPIfTMjvFIc4cDH9Dm', b'1', '', '2022-11-18 16:13:29.637000', b'1', 0, NULL, b'1', b'1'),
	(33, 'user800', '1@1.com', 'user800', '$2a$10$Q7JUDcptZuT0f5xsM1hdqOPwRFjSFEiYflq7ZOcwWBH/SeDZUqS1O', b'1', '', '2022-11-20 12:59:42.560000', b'0', 2, '2022-11-20 13:02:40.871000', b'1', b'1');

-- 导出  表 product3.user_history 结构
CREATE TABLE IF NOT EXISTS `user_history` (
  `user_id` int NOT NULL,
  `history_id` int NOT NULL,
  PRIMARY KEY (`user_id`,`history_id`),
  KEY `FKmyn0e5qi70qvka9b47oaedacw` (`history_id`),
  CONSTRAINT `FKaa6ilb6iqih95bntoeyysb2pc` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
  CONSTRAINT `FKmyn0e5qi70qvka9b47oaedacw` FOREIGN KEY (`history_id`) REFERENCES `history` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 正在导出表  product3.user_history 的数据:~18 rows (大约)
INSERT INTO `user_history` (`user_id`, `history_id`) VALUES
	(1, 1),
	(1, 2),
	(1, 3),
	(1, 4),
	(1, 5),
	(1, 6),
	(1, 7),
	(1, 8),
	(1, 9),
	(1, 10),
	(1, 11),
	(26, 12),
	(26, 13),
	(1, 14),
	(1, 15),
	(30, 16),
	(30, 17),
	(1, 18),
	(14, 19);

-- 导出  表 product3.user_role 结构
CREATE TABLE IF NOT EXISTS `user_role` (
  `id` int NOT NULL AUTO_INCREMENT,
  `user_id` int NOT NULL DEFAULT '0',
  `role_id` int NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_id_role_id` (`user_id`,`role_id`),
  KEY `FK_user_role_role` (`role_id`),
  CONSTRAINT `FK_user_role_role` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`),
  CONSTRAINT `FK_user_role_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=97 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 正在导出表  product3.user_role 的数据:~18 rows (大约)
INSERT INTO `user_role` (`id`, `user_id`, `role_id`) VALUES
	(93, 1, 1),
	(92, 1, 2),
	(91, 1, 3),
	(2, 2, 2),
	(3, 3, 3),
	(43, 11, 1),
	(44, 12, 1),
	(26, 13, 1),
	(90, 14, 1),
	(60, 25, 1),
	(64, 26, 1),
	(63, 26, 2),
	(62, 26, 3),
	(76, 27, 1),
	(75, 28, 1),
	(87, 30, 1),
	(86, 30, 2),
	(85, 30, 3),
	(89, 32, 1),
	(96, 33, 1);

/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;

2. 配置数据源属性

接下来,在应用程序属性文件中指定数据库连接信息,如下所示:根据您的MySQL数据库更新URL,用户名和密码。

spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:3306/product3?autoReconnect=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
#logging.level.root=WARN

3. 声明弹簧安全性和 MySQL JDBC 驱动程序的依赖关系

要将Spring安全API用于项目,请在pom.xml文件中声明以下依赖项:并且要将JDBC与弹簧启动和MySQL一起使用:请注意,依赖项版本已由弹簧启动初学者父项目定义。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.4</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>net.codejava</groupId>
    <artifactId>ProductManagerJDBCAuthenticationManuallyAuthenticateCaptchaAccess</artifactId>
    <version>2.0</version>
    <name>ProductManagerJDBCAuthenticationManuallyAuthenticateCaptchaAccess</name>
    <description>ProductManagerJDBCAuthentication</description>
    <packaging>jar</packaging>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>provided</scope>
        </dependency>


    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

4. 配置 Spring  Security

要将 Spring 安全性与基于表单的身份验证和 CustomUserDetailsService 结合使用,请按如下方式创建 WebSecurityConfig 类:

package com.example;

import javax.servlet.Filter;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.session.HttpSessionEventPublisher;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Autowired
    private CustomLoginFailureHandler loginFailureHandler;
    @Autowired
    private CustomLoginSuccessHandler successHandler;

    @Autowired

    private CustomUserDetailsService customUserDetailsService;

    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder authBuilder) throws Exception {
        authBuilder.userDetailsService(customUserDetailsService)
                .passwordEncoder(new BCryptPasswordEncoder());

    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        Filter filter = new KaptchaAuthenticationFilter("/login", "/login?error");
        http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);

        http.authorizeRequests()
                .antMatchers("/captcha_image**").permitAll()
                .antMatchers("/header").permitAll()
                .antMatchers("/footer").permitAll()
                .antMatchers("/sidebar").permitAll()
                .antMatchers("/index2").permitAll()
                .antMatchers("/index3").permitAll()
                .antMatchers("/pages/**").permitAll()
                .antMatchers("/css/**").permitAll()
                .antMatchers("/js/**").permitAll()
                .antMatchers("/assets/**").permitAll()
                .antMatchers("/webjars/**").permitAll()
                .antMatchers("/common/**").permitAll()
                .antMatchers("/login").permitAll()
                .antMatchers("/logout").permitAll()
                .antMatchers("/verify").permitAll()
                .antMatchers("/home").authenticated()
                .antMatchers("/user/info").authenticated()
                .antMatchers("/change/password").authenticated()
                .antMatchers("/new/password").authenticated()
                .antMatchers("/").authenticated()
                .anyRequest()
                .access("@rbacService.hasPermission(request , authentication)")
                .and()
                .formLogin().loginPage("/login")
                .permitAll()
                .failureHandler(loginFailureHandler)
                .successHandler(successHandler)
                .and()
                .logout().permitAll()
                .and()
                .exceptionHandling().accessDeniedPage("/403");
        http
                .sessionManagement()
                .maximumSessions(1)
                .expiredUrl("/")
                .maxSessionsPreventsLogin(false)
                .sessionRegistry(sessionRegistry());
    }

    @Bean
    public SessionRegistry sessionRegistry() {
        SessionRegistry sessionRegistry = new SessionRegistryImpl();
        return sessionRegistry;
    }

    // Register HttpSessionEventPublisher
    @Bean
    public static ServletListenerRegistrationBean httpSessionEventPublisher() {
        return new ServletListenerRegistrationBean(new HttpSessionEventPublisher());
    }

    @Bean
    BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

当不使用WebSecurityConfigurerAdapter 时最大会话限制不起作用,故此处仍使用废弃的WebSecurityConfigurerAdapter

此安全配置类必须使用@EnableWebSecurity注释进行批注,并且是 Web 安全配置器适配器的子类。

CustomUserDetailsService

package com.example;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.Service;

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    public UserDetails loadUserByUsername(String userName)
            throws UsernameNotFoundException {
        User domainUser = userRepository.findByUsername(userName);

        boolean enabled = true;
        boolean accountNonExpired = true;
        boolean credentialsNonExpired = true;
        boolean accountNonLocked = true;

        return new org.springframework.security.core.userdetails.User(
                domainUser.getUsername(),
                domainUser.getPassword(),
                domainUser.isEnabled(),
                domainUser.isAccountNonExpired(),
                domainUser.isCredentialsNonExpired(),
                domainUser.isAccountNonLocked(),
                getAuthorities(domainUser.getRoles())
        );
    }

    private Collection<? extends GrantedAuthority> getAuthorities(
            Collection<Role> roles) {
        List<GrantedAuthority> authorities
                = new ArrayList<>();
        for (Role role : roles) {
            authorities.add(new SimpleGrantedAuthority(role.getName()));
            role.getPermissions().stream()
                    .map(p -> new SimpleGrantedAuthority(p.getName()))
                    .forEach(authorities::add);
        }

        return authorities;
    }

}

User

package com.example;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import lombok.Data;
import org.hibernate.annotations.DynamicUpdate;

@Data
@Entity
@DynamicUpdate

public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String username;

    private String password;

    private boolean enabled = true;

    private boolean accountNonExpired = true;

    private boolean credentialsNonExpired = true;

    private boolean accountNonLocked = true;

    private String email;

    private String name;

    private String homepage;
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(
            name = "user_role",
            joinColumns = @JoinColumn(name = "user_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private Set<Role> roles = new HashSet<>();

    @Column(name = "password_changed_time")
    private Date passwordChangedTime;

    @Column(name = "failed_attempt")
    private int failedAttempt;

    @Column(name = "lock_time")
    private Date lockTime;

    @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinTable(
            name = "user_history",
            joinColumns = @JoinColumn(name = "user_id"),
            inverseJoinColumns = @JoinColumn(name = "history_id")
    )
    private Set<History> historys = new HashSet<>();

}

角色和权限同时起作用

 

5.登录验证过程

package com.example;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class CommonController {

    @GetMapping("/login")
    public String viewLoginPage() {
        // custom logic before showing login page...

        return "login";
    }
}

kaptcha验证码

package com.example;

import java.awt.image.BufferedImage;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.Producer;

/**
 * @author will
 */
@Controller
public class KaptchaController {
    @Autowired
    private Producer captchaProducer;

    @GetMapping("/captcha_image")
    public void getKaptchaImage(HttpServletResponse response, HttpSession session) throws Exception {
        response.setDateHeader("Expires", 0);
        // Set standard HTTP/1.1 no-cache headers.
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        // Set IE extended HTTP/1.1 no-cache headers (use addHeader).
        response.addHeader("Cache-Control", "post-check=0, pre-check=0");
        // Set standard HTTP/1.0 no-cache header.
        response.setHeader("Pragma", "no-cache");
        // return a jpeg
        response.setContentType("image/jpeg");
        // create the text for the image
        String capText = captchaProducer.createText();
        // store the text in the session
        // request.getSession().setAttribute(Constants.KAPTCHA_SESSION_KEY, capText);

        // save captcha to session
        session.setAttribute(Constants.KAPTCHA_SESSION_KEY, capText);

        // create the image with the text
        BufferedImage bi = captchaProducer.createImage(capText);
        ServletOutputStream out = response.getOutputStream();

        // write the data out
        ImageIO.write(bi, "jpg", out);
        try {
            out.flush();
        } finally {
            out.close();
        }
    }
}
package com.example;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.Properties;

@Component
public class KaptchaConfig {

    @Bean
    public DefaultKaptcha getDefaultKaptcha() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        properties.put("kaptcha.border", "no");
        properties.put("kaptcha.textproducer.font.color", "black");
        properties.put("kaptcha.image.width", "150");
        properties.put("kaptcha.image.height", "40");
        properties.put("kaptcha.textproducer.font.size", "30");
        properties.put("kaptcha.session.key", "verifyCode");
        properties.put("kaptcha.textproducer.char.space", "5");
//        properties.setProperty("kaptcha.textproducer.char.length", "4");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);

        return defaultKaptcha;
    }

}

6.登录页面

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Bootstrap 5 Sign In Form with Image Example</title>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
              integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
                integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
        crossorigin="anonymous"></script>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css">
    </head>

    <body>

        <form th:action="@{/login}" method="post">
            <div class="container-fluid vh-100" style="margin-top:50px">
                <div class="" style="margin-top:50px">
                    <div class="rounded d-flex justify-content-center">
                        <div class=" col-md-4 col-sm-12 shadow-lg p-5 bg-light">
                            <div th:if="${param.error}">
                                <p class="text-danger">[[${session.SPRING_SECURITY_LAST_EXCEPTION.message}]]</p>
                            </div>

                            <div th:if="${param.logout}">
                                <p class="text-warning">You have been logged out.</p>
                            </div>
                            <div class="text-center">
                                <h3 class="text-primary">请登录</h3>
                            </div>
                            <div class="p-4">
                                <div class="input-group mb-3">
                                    <span class="input-group-text bg-secondary"><i
                                            class="bi bi-person-fill text-white"></i></span>
                                    <input id="username" type="text" name="username" required class="form-control" placeholder="用户名">
                                </div>
                                <div class="input-group mb-3">
                                    <span class="input-group-text bg-secondary"><i
                                            class="bi bi-key-fill text-white"></i></span>
                                    <input  id="password" type="password" name="password" required class="form-control" placeholder="密码">
                                </div>
                                <div class="input-group mb-3">
                                    <span class="input-group-text bg-secondary"><i
                                            class="bi bi-lock-fill text-white"></i></span>
                                    <input type="text" name="kaptcha"  class="form-control" placeholder="输入下图中的校验码">
                                </div>
                                <div class="input-group mb-3">
                                    <span class="input-group-text bg-secondary"><i
                                            class="bi bi-image-fill text-white"></i></span>
                                    <img alt="Click the picture to refresh!" class="pointer" th:src="@{/captcha_image}"
                                         onclick="this.src = '/captcha_image?d=' + new Date() * 1">
                                </div>
                                <div class="col-12">
                                    <button type="submit" class="btn btn-primary px-4 float-end mt-4">登录</button>
                                </div>

                            </div>
                        </div>
                    </div>
                </div>
            </div>
    </body>

</html>

5. 测试登录和注销

启动Spring Boot应用程序并访问 http://localhost:8080 在Web浏览器中,您将看到自定义的登录页面出现:

 现在输入正确的用户名admin和密码admin,您将看到主页如下:

 并注意欢迎消息后跟用户名。用户现在已通过身份验证以使用该应用程序。单击“注销”按钮,您将看到自定义的登录页面出现,这意味着我们已成功实现登录并注销到我们的Spring Boot应用程序。

自定义从数据库中获取动态权限验证

package com.example;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Data;

@Data
@Entity
public class Permission {

    @Id
    private Long id;
    private String name;
    private String description;
    private String uri;
    private String method;

}
package com.example;

import org.springframework.data.jpa.repository.JpaRepository;

public interface PermissionRepository extends JpaRepository<Permission, Long> {

    public Permission findByName(String name);

}
package com.example;


import javax.servlet.http.HttpServletRequest;
import org.springframework.security.core.Authentication;

/**
 * RBAC模型实现Security,即通过角色对用户进行分组,在对每个角色进行权限授权就, 进而简化用户权限分配以及管理。 需要的表: user:
 * 用户信息表 保存有的用户id,用户名、密码、账号、状态、salt加盐 role:角色表 user_role:用户角色关联表 permission: 权限表
 * 保存的有权限相关信息 role_permission: 角色与权限管理表
 */
public interface RbacService {

    //用户判断当前请求是否有操作权限
    boolean hasPermission(HttpServletRequest request, Authentication authentication);
}
package com.example;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

@Component("rbacService")
public class RbacServiceImpl implements RbacService {

    @Autowired
    private PermissionRepository permissionRepository;

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {

        //获取用户认证信息
        System.out.println(authentication.getAuthorities());
        Object principal = authentication.getPrincipal();
        System.out.println(principal.getClass());
        //判断数据是否为空 以及类型是否正确
        if (null != principal && principal instanceof User) {
            String username = ((User) principal).getUsername();
            System.out.println(username);

        }

        String requestURI = request.getRequestURI();
        System.out.println(requestURI);
        String method = request.getMethod();
        System.out.println(method);

        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        boolean hasPermission = false;
        for (GrantedAuthority authority : authorities) {
            String authorityname = authority.getAuthority();
            System.out.println(authority.getAuthority());
            Permission permission = permissionRepository.findByName(authorityname);
            System.out.println(permissionRepository.findByName(authorityname));
            if (null != permission && permission.getMethod().equals(request.getMethod()) && antPathMatcher.match(permission.getUri(), request.getRequestURI())) {
                hasPermission = true;

                break;
            }
        }
        System.out.println(hasPermission);
        return hasPermission;
    }
}

thymeleaf视图文件中,根据权限显示连接菜单

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta content="width=device-width, initial-scale=1.0" name="viewport">
        <title>Dashboard - Admin Bootstrap Template</title>
        <meta name="robots" content="noindex, nofollow">
        <meta content="" name="description">
        <meta content="" name="keywords">
        <link href="/assets/img/favicon.png" rel="icon">
        <link href="/assets/img/apple-touch-icon.png" rel="apple-touch-icon">
        <link href="/assets/css/bootstrap.min.css" rel="stylesheet">
        <link href="/assets/css/bootstrap-icons.css" rel="stylesheet">
        <link href="/assets/css/boxicons.min.css" rel="stylesheet">
        <link href="/assets/css/quill.snow.css" rel="stylesheet">
        <link href="/assets/css/quill.bubble.css" rel="stylesheet">
        <link href="/assets/css/remixicon.css" rel="stylesheet">
        <link href="/assets/css/simple-datatables.css" rel="stylesheet">
        <link href="/assets/css/style.css" rel="stylesheet">
    </head>
    <body>
        <header id="header" class="header fixed-top d-flex align-items-center" th:replace="fragments/header :: header">
        </header>
        <aside id="sidebar" class="sidebar" th:replace="fragments/sidebar :: sidebar">
        </aside>
        <main id="main" class="main">
            <div class="pagetitle">
                <nav>
                    <ol class="breadcrumb">
                        <li class="breadcrumb-item"><a href="index.html">主页</a></li>
                        <li class="breadcrumb-item active">用户管理</li>
                    </ol>
                </nav>
            </div>
            <section class="section dashboard">
                <div class="row">
                    <div class="col-lg-12">
                        <div class="card">
                            <div class="card-body">

                                <br/>
                                <br/>
                                <div class="table-title">
                                    <div class="row">
                                        <div class="col-sm-6">
                                            <h2> <b>用户管理</b></h2>
                                        </div>
                                        <div class="col-sm-6">
                                            <div sec:authorize="hasAnyAuthority('permission_create')">
                                                <a href="/user/new" class="btn btn-success" ><i class="bi bi-person-plus" data-toggle="tooltip" title="添加用户"></i></a>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <table class="table table-striped table-hover">
                                    <thead >
                                        <tr>
                                            <th>ID</th>
                                            <th>User Name</th>
                                            <th>E-mail</th>
                                            <th>Name</th>
                                            <th>Home Page</th>
                                            <th>Roles</th>
                                            <th>Actions</th>
                                        </tr>
                                    </thead>
                                    <tbody>
                                        <tr th:each="user: ${listUsers}">
                                            <td th:text="${user.id}">User ID</td>
                                            <td th:text="${user.username}">User Name</td>
                                            <td th:text="${user.email}">E-mail</td>
                                            <td th:text="${user.name}">Name</td>
                                            <td th:text="${user.homepage}">Home Page</td>
                                            <td th:text="${user.roles}">Roles</td>
                                            <td>
                                                <a sec:authorize="hasAuthority('user_edit')" th:href="@{'/user/edit/' + ${user.id}}" class="edit" ><i class="bi bi-pencil" data-toggle="tooltip" title="编辑"></i></a>
                                                <a sec:authorize="hasAuthority('user_edit')" th:href="@{'/user/resetpassword/' + ${user.id}}" class="reset" ><i class="bi bi-key" data-toggle="tooltip" title="重置密码"></i></a>
                                                <a sec:authorize="hasAuthority('user_delete')" th:href="@{'/user/delete/' + ${user.id}}" class="delete" ><i class="bi bi-trash" data-toggle="tooltip" title="删除"></i></a>


                                            </td>
                                        </tr>
                                    </tbody>
                                </table>



                            </div>
                        </div>
                    </div>
            </section>
        </main>
        <footer id="footer" class="footer" th:replace="fragments/footer :: footer">
            <div class="copyright"> &copy; Copyright <strong><span>Compnay Name</span></strong>. All Rights Reserved</div>
            <div class="credits"> with love <a href="https://freeetemplates.com/">FreeeTemplates</a></div>
        </footer>
        <a href="#" class="back-to-top d-flex align-items-center justify-content-center"><i class="bi bi-arrow-up-short"></i></a>
        <script src="/assets/js/apexcharts.min.js"></script>
        <script src="/assets/js/bootstrap.bundle.min.js"></script>
        <script src="/assets/js/chart.min.js"></script>
        <script src="/assets/js/echarts.min.js"></script>
        <script src="/assets/js/quill.min.js"></script>
        <script src="/assets/js/simple-datatables.js"></script>
        <script src="/assets/js/tinymce.min.js"></script>
        <script src="/assets/js/validate.js"></script>
        <script src="/assets/js/main.js"></script>

    </body>
</html>

 Thymeleaf集成Bootstrap 5 Free Admin Dashboard Template 1 - freeetemplates

header.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"  xmlns:sec="http://www.thymeleaf.org">
    <head>
        <meta charset="utf-8">
            <meta content="width=device-width, initial-scale=1.0" name="viewport">
                <title>Dashboard - Admin Bootstrap Template</title>
                <meta name="robots" content="noindex, nofollow">
                    <meta content="" name="description">
                        <meta content="" name="keywords">
                            <link href="assets/img/favicon.png" rel="icon">
                                <link href="assets/img/apple-touch-icon.png" rel="apple-touch-icon">
                                    <link href="https://fonts.gstatic.com" rel="preconnect">
                                        <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,600,600i,700,700i|Nunito:300,300i,400,400i,600,600i,700,700i|Poppins:300,300i,400,400i,500,500i,600,600i,700,700i" rel="stylesheet">
                                            <link href="assets/css/bootstrap.min.css" rel="stylesheet">
                                                <link href="assets/css/bootstrap-icons.css" rel="stylesheet">
                                                    <link href="assets/css/boxicons.min.css" rel="stylesheet">
                                                        <link href="assets/css/quill.snow.css" rel="stylesheet">
                                                            <link href="assets/css/quill.bubble.css" rel="stylesheet">
                                                                <link href="assets/css/remixicon.css" rel="stylesheet">
                                                                    <link href="assets/css/simple-datatables.css" rel="stylesheet">
                                                                        <link href="assets/css/style.css" rel="stylesheet">
                                                                            </head>
                                                                            <body>

                                                                                <header id="header" class="header fixed-top d-flex align-items-center" th:fragment="header">
                                                                                    <form th:action="@{/logout}" method="post" th:hidden="true" name="logoutForm">
                                                                                        <input type="submit" value="Logout" />
                                                                                    </form>
                                                                                    <div class="d-flex align-items-center justify-content-between"> <a href="index.html" class="logo d-flex align-items-center"> <img src="/assets/img/logo.png" alt=""> <span class="d-none d-lg-block">CRUD和RBAC系统</span> </a> <i class="bi bi-list toggle-sidebar-btn"></i></div>

                                                                                    <nav class="header-nav ms-auto">
                                                                                        <ul class="d-flex align-items-center">
                                                                                            <li class="nav-item d-block d-lg-none"> <a class="nav-link nav-icon search-bar-toggle " href="#"> <i class="bi bi-search"></i> </a></li>


                                                                                            <li class="nav-item dropdown pe-3">
                                                                                                <a class="nav-link nav-profile d-flex align-items-center pe-0" href="#" data-bs-toggle="dropdown"> <img src="/assets/img/profile.png" alt="Profile" class="rounded-circle"> <span class="d-none d-md-block dropdown-toggle ps-2"><span sec:authentication="name">Username</span></span> </a>
                                                                                                <ul class="dropdown-menu dropdown-menu-end dropdown-menu-arrow profile">
                                                                                                    <li class="dropdown-header">
                                                                                                        <h6><span sec:authentication="name">Username</span></h6>
                                                                                                    </li>
                                                                                                    <li>
                                                                                                        <hr class="dropdown-divider">
                                                                                                    </li>
                                                                                                    <li> <a class="dropdown-item d-flex align-items-center" href="/user/info"> <i class="bi bi-person"></i> <span>账号信息</span> </a></li>
                                                                                                    <li>
                                                                                                        <hr class="dropdown-divider">
                                                                                                    </li>
                                                                                                    <li> <a href="javascript: document.logoutForm.submit()" class="dropdown-item d-flex align-items-center" > <i class="bi bi-box-arrow-right"></i> <span>注销</span> </a>
                                                                                                    </li>

                                                                                                </ul>
                                                                                            </li>
                                                                                        </ul>
                                                                                    </nav>
                                                                                </header>
                                                                            </body>
                                                                            </html>

footer.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"  xmlns:sec="http://www.thymeleaf.org">
    <head>
        <meta charset="utf-8">
            <meta content="width=device-width, initial-scale=1.0" name="viewport">
                <title>Dashboard - Admin Bootstrap Template</title>
                <meta name="robots" content="noindex, nofollow">
                    <meta content="" name="description">
                        <meta content="" name="keywords">
                            <link href="assets/img/favicon.png" rel="icon">
                                <link href="assets/img/apple-touch-icon.png" rel="apple-touch-icon">
                                    <link href="https://fonts.gstatic.com" rel="preconnect">
                                        <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,600,600i,700,700i|Nunito:300,300i,400,400i,600,600i,700,700i|Poppins:300,300i,400,400i,500,500i,600,600i,700,700i" rel="stylesheet">
                                            <link href="assets/css/bootstrap.min.css" rel="stylesheet">
                                                <link href="assets/css/bootstrap-icons.css" rel="stylesheet">
                                                    <link href="assets/css/boxicons.min.css" rel="stylesheet">
                                                        <link href="assets/css/quill.snow.css" rel="stylesheet">
                                                            <link href="assets/css/quill.bubble.css" rel="stylesheet">
                                                                <link href="assets/css/remixicon.css" rel="stylesheet">
                                                                    <link href="assets/css/simple-datatables.css" rel="stylesheet">
                                                                        <link href="assets/css/style.css" rel="stylesheet">
                                                                            </head>
                                                                            <body>


                                                                                <footer id="footer" class="footer"  th:fragment="footer">
                                                                                    <div class="copyright"> &copy; Copyright <strong><span>CRUD和RBAC系统</span></strong>. All Rights Reserved</div>
                                                                                    <div class="credits"> with love <a href="https://freeetemplates.com/">FreeeTemplates</a></div>
                                                                                </footer>
                                                                            </body>
                                                                            </html>

sidebar.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"  xmlns:sec="http://www.thymeleaf.org">
    <head>
        <meta charset="utf-8">
            <meta content="width=device-width, initial-scale=1.0" name="viewport">
                <title>Dashboard - Admin Bootstrap Template</title>
                <meta name="robots" content="noindex, nofollow">
                    <meta content="" name="description">
                        <meta content="" name="keywords">
                            <link href="assets/img/favicon.png" rel="icon">
                                <link href="assets/img/apple-touch-icon.png" rel="apple-touch-icon">
                                    <link href="https://fonts.gstatic.com" rel="preconnect">
                                        <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,600,600i,700,700i|Nunito:300,300i,400,400i,600,600i,700,700i|Poppins:300,300i,400,400i,500,500i,600,600i,700,700i" rel="stylesheet">
                                            <link href="assets/css/bootstrap.min.css" rel="stylesheet">
                                                <link href="assets/css/bootstrap-icons.css" rel="stylesheet">
                                                    <link href="assets/css/boxicons.min.css" rel="stylesheet">
                                                        <link href="assets/css/quill.snow.css" rel="stylesheet">
                                                            <link href="assets/css/quill.bubble.css" rel="stylesheet">
                                                                <link href="assets/css/remixicon.css" rel="stylesheet">
                                                                    <link href="assets/css/simple-datatables.css" rel="stylesheet">
                                                                        <link href="assets/css/style.css" rel="stylesheet">
                                                                            </head>
                                                                            <body>



                                                                                <aside id="sidebar" class="sidebar" th:fragment="sidebar">
                                                                                    <ul class="sidebar-nav" id="sidebar-nav">
                                                                                        <li class="nav-item"> <a class="nav-link " href="index.html"> <i class="bi bi-grid"></i> <span>Dashboard</span> </a></li>
                                                                                        <li class="nav-item">
                                                                                            <a class="nav-link collapsed" data-bs-target="#components-nav" data-bs-toggle="collapse" href="#"> <i class="bi bi-menu-button-wide"></i><span>系统管理</span><i class="bi bi-chevron-down ms-auto"></i> </a>
                                                                                            <ul id="components-nav" class="nav-content collapse " data-bs-parent="#sidebar-nav">
                                                                                                <li sec:authorize="hasAuthority('user')"><a th:href="@{/user}"><i class="bi bi-circle"></i><span>用户管理</span> </a></li>
                                                                                                <li sec:authorize="hasAuthority('role')"><a th:href="@{/role}"><i class="bi bi-circle"></i><span>角色管理</span> </a></li>
                                                                                                <li sec:authorize="hasAuthority('permission')"><a th:href="@{/permission}"><i class="bi bi-circle"></i><span>权限管理</span> </a></li>
                                                                                                <li> <a href="components-breadcrumbs.html"> <i class="bi bi-circle"></i><span>Breadcrumbs</span> </a></li>
                                                                                                <li> <a href="components-buttons.html"> <i class="bi bi-circle"></i><span>Buttons</span> </a></li>
                                                                                                <li> <a href="components-cards.html"> <i class="bi bi-circle"></i><span>Cards</span> </a></li>
                                                                                                <li> <a href="components-carousel.html"> <i class="bi bi-circle"></i><span>Carousel</span> </a></li>
                                                                                                <li> <a href="components-list-group.html"> <i class="bi bi-circle"></i><span>List group</span> </a></li>
                                                                                                <li> <a href="components-modal.html"> <i class="bi bi-circle"></i><span>Modal</span> </a></li>
                                                                                                <li> <a href="components-tabs.html"> <i class="bi bi-circle"></i><span>Tabs</span> </a></li>
                                                                                                <li> <a href="components-pagination.html"> <i class="bi bi-circle"></i><span>Pagination</span> </a></li>
                                                                                                <li> <a href="components-progress.html"> <i class="bi bi-circle"></i><span>Progress</span> </a></li>
                                                                                                <li> <a href="components-spinners.html"> <i class="bi bi-circle"></i><span>Spinners</span> </a></li>
                                                                                                <li> <a href="components-tooltips.html"> <i class="bi bi-circle"></i><span>Tooltips</span> </a></li>
                                                                                            </ul>
                                                                                        </li>
                                                                                        <li class="nav-item">
                                                                                            <a class="nav-link collapsed" data-bs-target="#forms-nav" data-bs-toggle="collapse" href="#"> <i class="bi bi-journal-text"></i><span>Forms</span><i class="bi bi-chevron-down ms-auto"></i> </a>
                                                                                            <ul id="forms-nav" class="nav-content collapse " data-bs-parent="#sidebar-nav">
                                                                                                <li> <a href="forms-elements.html"> <i class="bi bi-circle"></i><span>Form Elements</span> </a></li>
                                                                                                <li> <a href="forms-layouts.html"> <i class="bi bi-circle"></i><span>Form Layouts</span> </a></li>
                                                                                                <li> <a href="forms-editors.html"> <i class="bi bi-circle"></i><span>Form Editors</span> </a></li>
                                                                                                <li> <a href="forms-validation.html"> <i class="bi bi-circle"></i><span>Form Validation</span> </a></li>
                                                                                            </ul>
                                                                                        </li>
                                                                                        <li class="nav-item">
                                                                                            <a class="nav-link collapsed" data-bs-target="#tables-nav" data-bs-toggle="collapse" href="#"> <i class="bi bi-layout-text-window-reverse"></i><span>Tables</span><i class="bi bi-chevron-down ms-auto"></i> </a>
                                                                                            <ul id="tables-nav" class="nav-content collapse " data-bs-parent="#sidebar-nav">
                                                                                                <li> <a href="tables-general.html"> <i class="bi bi-circle"></i><span>General Tables</span> </a></li>
                                                                                                <li> <a href="tables-data.html"> <i class="bi bi-circle"></i><span>Data Tables</span> </a></li>
                                                                                            </ul>
                                                                                        </li>
                                                                                        <li class="nav-item">
                                                                                            <a class="nav-link collapsed" data-bs-target="#charts-nav" data-bs-toggle="collapse" href="#"> <i class="bi bi-bar-chart"></i><span>Charts</span><i class="bi bi-chevron-down ms-auto"></i> </a>
                                                                                            <ul id="charts-nav" class="nav-content collapse " data-bs-parent="#sidebar-nav">
                                                                                                <li> <a href="charts-chartjs.html"> <i class="bi bi-circle"></i><span>Chart.js</span> </a></li>
                                                                                                <li> <a href="charts-apexcharts.html"> <i class="bi bi-circle"></i><span>ApexCharts</span> </a></li>
                                                                                                <li> <a href="charts-echarts.html"> <i class="bi bi-circle"></i><span>ECharts</span> </a></li>
                                                                                            </ul>
                                                                                        </li>
                                                                                        <li class="nav-item">
                                                                                            <a class="nav-link collapsed" data-bs-target="#icons-nav" data-bs-toggle="collapse" href="#"> <i class="bi bi-gem"></i><span>Icons</span><i class="bi bi-chevron-down ms-auto"></i> </a>
                                                                                            <ul id="icons-nav" class="nav-content collapse " data-bs-parent="#sidebar-nav">
                                                                                                <li> <a href="icons-bootstrap.html"> <i class="bi bi-circle"></i><span>Bootstrap Icons</span> </a></li>
                                                                                                <li> <a href="icons-remix.html"> <i class="bi bi-circle"></i><span>Remix Icons</span> </a></li>
                                                                                                <li> <a href="icons-boxicons.html"> <i class="bi bi-circle"></i><span>Boxicons</span> </a></li>
                                                                                            </ul>
                                                                                        </li>
                                                                                        <li class="nav-heading">Pages</li>
                                                                                        <li class="nav-item"> <a class="nav-link collapsed" href="users-profile.html"> <i class="bi bi-person"></i> <span>Profile</span> </a></li>
                                                                                        <li class="nav-item"> <a class="nav-link collapsed" href="pages-faq.html"> <i class="bi bi-question-circle"></i> <span>F.A.Q</span> </a></li>
                                                                                        <li class="nav-item"> <a class="nav-link collapsed" href="pages-contact.html"> <i class="bi bi-envelope"></i> <span>Contact</span> </a></li>
                                                                                        <li class="nav-item"> <a class="nav-link collapsed" href="pages-register.html"> <i class="bi bi-card-list"></i> <span>Register</span> </a></li>
                                                                                        <li class="nav-item"> <a class="nav-link collapsed" href="pages-login.html"> <i class="bi bi-box-arrow-in-right"></i> <span>Login</span> </a></li>
                                                                                        <li class="nav-item"> <a class="nav-link collapsed" href="pages-error-404.html"> <i class="bi bi-dash-circle"></i> <span>Error 404</span> </a></li>
                                                                                        <li class="nav-item"> <a class="nav-link collapsed" href="pages-blank.html"> <i class="bi bi-file-earmark"></i> <span>Blank</span> </a></li>
                                                                                    </ul>
                                                                                </aside>
                                                                            </body>
                                                                            </html>

list_user.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta content="width=device-width, initial-scale=1.0" name="viewport">
        <title>Dashboard - Admin Bootstrap Template</title>
        <meta name="robots" content="noindex, nofollow">
        <meta content="" name="description">
        <meta content="" name="keywords">
        <link href="/assets/img/favicon.png" rel="icon">
        <link href="/assets/img/apple-touch-icon.png" rel="apple-touch-icon">
        <link href="/assets/css/bootstrap.min.css" rel="stylesheet">
        <link href="/assets/css/bootstrap-icons.css" rel="stylesheet">
        <link href="/assets/css/boxicons.min.css" rel="stylesheet">
        <link href="/assets/css/quill.snow.css" rel="stylesheet">
        <link href="/assets/css/quill.bubble.css" rel="stylesheet">
        <link href="/assets/css/remixicon.css" rel="stylesheet">
        <link href="/assets/css/simple-datatables.css" rel="stylesheet">
        <link href="/assets/css/style.css" rel="stylesheet">
    </head>
    <body>
        <header id="header" class="header fixed-top d-flex align-items-center" th:replace="fragments/header :: header">
        </header>
        <aside id="sidebar" class="sidebar" th:replace="fragments/sidebar :: sidebar">
        </aside>
        <main id="main" class="main">
            <div class="pagetitle">
                <nav>
                    <ol class="breadcrumb">
                        <li class="breadcrumb-item"><a href="index.html">主页</a></li>
                        <li class="breadcrumb-item active">用户管理</li>
                    </ol>
                </nav>
            </div>
            <section class="section dashboard">
                <div class="row">
                    <div class="col-lg-12">
                        <div class="card">
                            <div class="card-body">

                                <br/>
                                <br/>
                                <div class="table-title">
                                    <div class="row">
                                        <div class="col-sm-6">
                                            <h2> <b>用户管理</b></h2>
                                        </div>
                                        <div class="col-sm-6">
                                            <div sec:authorize="hasAnyAuthority('permission_create')">
                                                <a href="/user/new" class="btn btn-success" ><i class="bi bi-person-plus" data-toggle="tooltip" title="添加用户"></i></a>
                                                <!-- <a href="#deleteEmployeeModal" class="btn btn-danger" data-toggle="modal"><i class="material-icons">&#xE15C;</i> <span>Delete</span></a>						 -->
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <table class="table table-striped table-hover">
                                    <thead >
                                        <tr>
                                            <th>ID</th>
                                            <th>User Name</th>
                                            <th>E-mail</th>
                                            <th>Name</th>
                                            <th>Home Page</th>
                                            <th>Roles</th>
                                            <th>Actions</th>
                                        </tr>
                                    </thead>
                                    <tbody>
                                        <tr th:each="user: ${listUsers}">
                                            <td th:text="${user.id}">User ID</td>
                                            <td th:text="${user.username}">User Name</td>
                                            <td th:text="${user.email}">E-mail</td>
                                            <td th:text="${user.name}">Name</td>
                                            <td th:text="${user.homepage}">Home Page</td>
                                            <td th:text="${user.roles}">Roles</td>
                                            <td>
                                                <a sec:authorize="hasAuthority('user_edit')" th:href="@{'/user/edit/' + ${user.id}}" class="edit" ><i class="bi bi-pencil" data-toggle="tooltip" title="编辑"></i></a>
                                                <a sec:authorize="hasAuthority('user_edit')" th:href="@{'/user/resetpassword/' + ${user.id}}" class="reset" ><i class="bi bi-key" data-toggle="tooltip" title="重置密码"></i></a>
                                                <a sec:authorize="hasAuthority('user_delete')" th:href="@{'/user/delete/' + ${user.id}}" class="delete" ><i class="bi bi-trash" data-toggle="tooltip" title="删除"></i></a>


                                            </td>
                                        </tr>
                                    </tbody>
                                </table>



                            </div>
                        </div>
                    </div>
            </section>
        </main>
        <footer id="footer" class="footer" th:replace="fragments/footer :: footer">
            <div class="copyright"> &copy; Copyright <strong><span>Compnay Name</span></strong>. All Rights Reserved</div>
            <div class="credits"> with love <a href="https://freeetemplates.com/">FreeeTemplates</a></div>
        </footer>
        <a href="#" class="back-to-top d-flex align-items-center justify-content-center"><i class="bi bi-arrow-up-short"></i></a>
        <script src="/assets/js/apexcharts.min.js"></script>
        <script src="/assets/js/bootstrap.bundle.min.js"></script>
        <script src="/assets/js/chart.min.js"></script>
        <script src="/assets/js/echarts.min.js"></script>
        <script src="/assets/js/quill.min.js"></script>
        <script src="/assets/js/simple-datatables.js"></script>
        <script src="/assets/js/tinymce.min.js"></script>
        <script src="/assets/js/validate.js"></script>
        <script src="/assets/js/main.js"></script>

    </body>
</html>

 

历史密码保存和重用检测

package com.example;

import java.util.Date;
import java.util.List;
import java.util.Set;
import javax.validation.Valid;
import org.apache.commons.text.similarity.LevenshteinDistance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.servlet.ModelAndView;

@Controller

public class UserController {

    @Autowired
    private UserService userService;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    /**
     * 注入身份认证管理器
     */
    @Autowired
    private AuthenticationManager authenticationManager;

    @GetMapping("/user/new")
    public String showRegistrationForm(Model model) {
        model.addAttribute("user", new UserDTO());

        return "user/new_user";
    }

    @GetMapping("/user")
    public String listUsers(Model model) {
        List<User> listUsers = userRepository.findAll();
        model.addAttribute("listUsers", listUsers);

        return "user/list_user";
    }

    @GetMapping("/user/edit/{id}")
    public String editUser(@PathVariable("id") Integer id, Model model) {
        User user = userService.get(id);
        List<Role> listRoles = userService.listRoles();
        model.addAttribute("user", user);
        model.addAttribute("listRoles", listRoles);
        return "user/edit_user";
    }

    @PostMapping("/user/save")
    public String saveUser(@Valid @ModelAttribute("user") UserDTO user, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return "user/new_user";
        }
        userService.saveUser(user);

        return "redirect:/user";
    }

    @PostMapping("/user/update")
    public String updateUser(User user) {
        User repoUser = userRepository.findById(user.getId()).orElse(null);
        repoUser.setUsername(user.getUsername());
        repoUser.setEmail(user.getEmail());
        repoUser.setName(user.getName());
        repoUser.setEnabled(user.isEnabled());
        repoUser.setAccountNonLocked(user.isAccountNonLocked());
        repoUser.setHomepage(user.getHomepage());
        repoUser.setRoles(user.getRoles());
        userRepository.save(repoUser);

        return "redirect:/user";
    }

    @GetMapping("/user/resetpassword/{id}")
    public String showResetPasswordForm(@PathVariable("id") Integer id, Model model) {
        User user = userRepository.findById(id).orElse(null);
        UserResetPasswordDTO userResetPasswordDTO = new UserResetPasswordDTO();
        userResetPasswordDTO.setId(user.getId());
        userResetPasswordDTO.setUsername(user.getUsername());
        model.addAttribute("user", userResetPasswordDTO);
        return "user/reset_password";
    }

    @PostMapping("/user/savepassword")
    public String savePassword(@Valid @ModelAttribute("user") UserResetPasswordDTO user, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return "user/reset_password";
        }
        User repoUser = userRepository.findById(user.getId()).orElse(null);

        repoUser.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
        Date passwordChangedTime = new Date();

        repoUser.setPasswordChangedTime(passwordChangedTime);
        userRepository.save(repoUser);

        return "redirect:/user";
    }

    @RequestMapping("/user/delete/{id}")
    public String deleteUser(@PathVariable(name = "id") Integer id) {
        userRepository.deleteById(id);

        return "redirect:/user";
    }

//    @RequestMapping(value = "user/info", method = RequestMethod.GET)
//    public ModelAndView userProfile() {
//        ModelAndView modelAndView = new ModelAndView();
//        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
//        System.out.println(auth.getName());
//        User user = userRepository.getByUsername(auth.getName());
//        modelAndView.addObject("user", user);
//        modelAndView.addObject("userName", user.getUsername());
//        modelAndView.setViewName("user-profile");
//        return modelAndView;
//    }
    @GetMapping("user/info")
    public String userProfile(Model model) {

        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        System.out.println(auth.getName());
        User user = userRepository.getByUsername(auth.getName());
        model.addAttribute("user", user);

        return "user/user_profile";
    }

//    @RequestMapping(value = "/change/password", method = RequestMethod.GET)
//    public ModelAndView changePassword() {
//        ModelAndView modelAndView = new ModelAndView();
//        modelAndView.addObject("userDTO", new UserChangePasswordDTO());
//        modelAndView.setViewName("password-update");
//        return modelAndView;
//    }
    @GetMapping("/change/password")

    public String changePassword(Model model) {

        model.addAttribute("userDTO", new UserChangePasswordDTO());
        return "user/password_update";

    }

//    @RequestMapping(value = "/new/password", method = RequestMethod.POST)
//    public ModelAndView newPassword(@Valid @ModelAttribute("userDTO") UserChangePasswordDTO userChangePasswordDTO, BindingResult bindingResult) {
//        if (bindingResult.hasErrors()) {
//            ModelAndView modelAndView = new ModelAndView();
//            modelAndView.addObject("userDTO", new UserChangePasswordDTO());
//            modelAndView.setViewName("password-update");
//            return modelAndView;
//        }
//        ModelAndView modelAndView = new ModelAndView();
//        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
//
//        if (userChangePasswordDTO.getNewPass().equals(userChangePasswordDTO.getConfirmPass())) {
//            User user = userService.findUserByUsername(auth.getName());
//            Boolean status = userService.isPasswordValid(userChangePasswordDTO.getPassword(), user.getPassword());
//            if (status == true) {
//
//                userService.changePassword(user, userChangePasswordDTO);
//                modelAndView.setViewName("login");
//            } else {
//                modelAndView.addObject("wrongPass", "Current possword was wrong..!");
//                modelAndView.setViewName("password-update");
//            }
//
//        } else {
//            modelAndView.addObject("passMatched", "Password doesn't matched..!");
//            modelAndView.setViewName("password-update");
//        }
//        return modelAndView;
//    }
    @PostMapping("/new/password")
    public String
            newPassword(@Valid @ModelAttribute("userDTO") UserChangePasswordDTO userChangePasswordDTO, BindingResult bindingResult, Model model) {
        if (bindingResult.hasErrors()) {
            return "user/password_update";

        }

        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        if (userChangePasswordDTO.getNewPass().equals(userChangePasswordDTO.getConfirmPass())) {
            User user = userService.findUserByUsername(auth.getName());
            Boolean status = userService.isPasswordValid(userChangePasswordDTO.getPassword(), user.getPassword());

            if (status == true) {

                LevenshteinDistance levenshteinWithThreshold = new LevenshteinDistance(3);
                // Returns -1 since the actual distance, 4, is higher than the threshold
                System.out.println("Levenshtein distance: " + levenshteinWithThreshold.apply(userChangePasswordDTO.getNewPass(), userChangePasswordDTO.getPassword()));
                if (levenshteinWithThreshold.apply(userChangePasswordDTO.getNewPass(), userChangePasswordDTO.getPassword()) == -1) {
                    Set<History> setHistorysCheck = user.getHistorys();
                    Boolean check = true;
                    for (History hist : setHistorysCheck) {
                        System.out.print(hist.getPassword());
                        if (bCryptPasswordEncoder.matches(userChangePasswordDTO.getNewPass(), hist.getPassword())) {
                            check = false;
                            break;
                        }
                    }

                    if (check == true) {

                        System.out.println(user.getHistorys());
                        History history = new History();
                        System.out.println("userChangePasswordDTO.getNewPass()=" + userChangePasswordDTO.getNewPass());
                        history.setPassword(bCryptPasswordEncoder.encode(userChangePasswordDTO.getNewPass()));
                        System.out.println(history);
                        Set<History> setHistorys = user.getHistorys();
                        setHistorys.add(history);
                        user.setHistorys(setHistorys);
                        System.out.println(user.getHistorys());
                        userService.changePassword(user, userChangePasswordDTO);
                        return "login";
                    } else {
                        model.addAttribute("passMatched", "New password was same as history..!");
                        return "user/password_update";
                    }
                } else {
                    model.addAttribute("passMatched", "New password need 4 diff wtih Current password..!");
                    return "user/password_update";
                }
            } else {

                model.addAttribute("wrongPass", "Current password was wrong..!");
                return "user/password_update";
            }

        } else {
            model.addAttribute("passMatched", "Password doesn't matched..!");
            return "user/password_update";
        }

    }

}

结论:

到目前为止,您已经学会了使用基于表单的身份验证和数据库内凭据来保护Spring Boot应用程序。您会看到 Spring 安全性使实现登录和注销功能变得非常容易,并且非常方便。为方便起见,您可以下载下面的示例项目。

下载源码:https://github.com/allwaysoft/ProductManagerMaximumSessionsCaptchaNewUserDetailsService

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值