Spring Security之认证

系列文章目录

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
Spring Security之认证


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 系列文章目录
  • 前言
  • 一、什么是Spring Security
  • 二、Spring Security之认证
    • 环境准备
    • 数据库认证
      • 自定义认证逻辑
    • PasswordEncoder
    • 自定义登录页面
    • 突破CSRF防护
    • 会话管理
    • 认证成功后的处理
    • 认证失败后的处理
    • 退出登录
    • 退出成功处理器
    • Remember Me
  • 总结


前言

提示:这里可以添加本文要记录的大概内容:

当涉及到构建安全的 Web 应用程序时,Spring Security 是一个广受欢迎且功能强大的框架。它提供了一系列的特性和功能,用于保护应用程序免受未经授权的访问。
在这篇博客中,我们将深入探讨 Spring Security 的认证部分。认证是安全体系的第一道防线,它确保只有经过身份验证的用户才能访问受保护的资源。
我们将从 Spring Security 的基础知识开始,了解它的核心概念和组件。然后,我们将逐步介绍如何使用 Spring Security 实现常见的认证方式,如用户名和密码登录。
无论你是 Spring Security 的新手还是有一定经验的开发者,这篇博客都将为你提供一个深入了解 Spring Security 认证的机会。通过掌握这些知识,你将能够构建更安全、可靠的 Web 应用程序。
让我们一起开启 Spring Security 认证之旅,保护我们的应用程序免受未经授权的访问!


提示:以下是本篇文章正文内容,下面案例可供参考

一、什么是Spring Security

Spring Security 是一个用于保护 Spring 应用程序安全性的框架。它提供了一套全面的安全特性,可以帮助开发人员轻松地构建安全的应用程序。Spring Security 的核心目标是提供一种易于使用、灵活且强大的方式来保护应用程序的安全性。
Spring Security 的主要功能包括身份验证、授权、密码加密、跨域安全性等。它支持多种认证方式,如基本认证、摘要认证、JWT 认证等。同时,Spring Security 还提供了强大的授权功能,允许开发人员根据用户角色和权限来限制对特定资源的访问。
总的来说,Spring Security 是一个非常有用的框架,可以帮助开发人员轻松地构建安全的应用程序。

二、Spring Security之认证

认证就是判断用户身份是否合法,如果合法就可以继续访问,不合法就拒绝访问

环境准备

1.准备一个名为mysecurity的Mysql数据库
2.创建SpringBoot项目,添加依赖

<!-- SpringMVC -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Thymeleaf-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--Spring Security-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Mysql驱动 -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope>
</dependency>
<!-- MyBatisPlus -->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.0</version>
</dependency>
<!-- lombok -->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>
<!-- junit -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>

3.为SpringBoot项目编写配置文件

server:port: 80#日志格式
logging:pattern:console: '%d{HH:mm:ss.SSS} %clr(%-5level) ---  [%-15thread] %cyan(%-50logger{50}):%msg%n'# 数据源
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql:///mysecurity?serverTimezone=UTCusername: rootpassword: root

4.在template文件夹编写项目主页面main.html

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>主页面</title>
</head>
<body>
<h1>主页面</h1>
</body>
</html>

5.编写访问页面控制器

@Controller
public class PageController {@RequestMapping("/{page}")public String showPage(@PathVariable String page){return page;}
}

数据库认证

在实际的项目中,用户通过输入用户名和密码,待验证通过后即登录成功,而用户信息大多保存在数据库中。而用户身份验证的这个过程,我们称之为认证逻辑。
1.准备数据库数据,用户信息数据都是保存在该数据库中

CREATE TABLE `users` (`id` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(255),`password` varchar(255) ,`phone` varchar(255) ,PRIMARY KEY (`id`)
);INSERT INTO `users` VALUES (1, 'baizhan', 'baizhan', '13812345678');
INSERT INTO `users` VALUES (2, 'sxt', 'sxt', '13812345678');

2.编写用户实体类

@Data
public class Users {private Integer id;private String username;private String password;private String phone;
}

3.编写dao接口

public interface UsersMapper extends BaseMapper<Users> {
}

4.在 SpringBoot启动类中添加 @MapperScan 注解,扫描Mapper文件夹

@SpringBootApplication
@MapperScan("com.itbaizhan.myspringsecurity.mapper")
public class MysecurityApplication {public static void main(String[] args) {SpringApplication.run(MysecurityApplication.class, args);}
}

自定义认证逻辑

在实际项目中,认证逻辑是需要自定义控制的。将 UserDetailsService 接口的实现类放入Spring容器即可自定义认证逻辑。UserDetailsService 的实现类必须重写 loadUserByUsername 方法,该方法定义了具体的认证逻辑,参数 username 是前端传来的用户名,我们需要根据传来的用户名查询到该用户(一般是从数据库查询),并将查询到的用户封装成一个UserDetails对象,该对象是Spring Security提供的用户对象,包含用户名、密码、权限。Spring Security会根据UserDetails对象中的密码和客户端提供密码进行比较。相同则认证通过,不相同则认证失败。

5.创建UserDetailsService的实现类,编写自定义认证逻辑

@Service
public class MyUserDetailsService implements UserDetailsService {@Autowiredprivate UsersMapper usersMapper;// 自定义认证逻辑@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 1.构造查询条件QueryWrapper<Users> wrapper = new QueryWrapper<Users>().eq("username", username);// 2.查询用户Users users = usersMapper.selectOne(wrapper);// 3.封装为UserDetails对象UserDetails userDetails = User.withUsername(users.getUsername()).password(users.getPassword()).authorities("admin").build();// 4.返回封装好的UserDetails对象return userDetails;}
}

PasswordEncoder

在实际的项目开发中,我们往数据库的密码不会是明文密码,而是经过我们加密后的密码。当用户传入的明文密码后,SpringSecurity使用密码解析器将明文密码加密成密文密码,然后再将数据库中的密文密码进行比对,匹配成功则通过,反之不通过。

创建SecurityConfig配置类,添加SpringSecurity密码解析器(PasswordEncoder)

// Security配置类
@Configuration
public class SecurityConfig {//密码编码器@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}

自定义登录页面

SpringSecurity给我们提供了登录界面,但是再实际的项目中,登录页面都是用的自己的,这样更能突出项目特色,所以我们要自定义登录界面。
1.编写登录界面
2.在Spring Security配置类自定义登录页面

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{//Spring Security配置@Overrideprotected void configure(HttpSecurity http) throws Exception {// 自定义表单登录http.formLogin() .loginPage("/login.html") // 自定义登录页面.usernameParameter("username") // 表单中的用户名项.passwordParameter("password") // 表单中的密码项.loginProcessingUrl("/login") // 登录路径,表单向该路径提交,提交后自动执行UserDetailsService的方法.successForwardUrl("/main") //登录成功后跳转的路径.failureForwardUrl("/fail"); //登录失败后跳转的路径// 需要认证的资源http.authorizeRequests().antMatchers("/login.html").permitAll() //登录页不需要认证.anyRequest().authenticated(); //其余所有请求都需要认证//关闭csrf防护http.csrf().disable();}@Overridepublic void configure(WebSecurity web) throws Exception {// 静态资源放行web.ignoring().antMatchers("/css/**");}
}

突破CSRF防护

CSRF:跨站请求伪造,通过伪造用户请求访问受信任的站点从而进行非法请求访问,是一种攻击手段。SpringSecurity默认开启CSRF防护,这就限制了除了GET请求以外的大多数请求。我们可以通过关闭CSRF防护来解决问题,但是这就不够安全了。CSRF为了保证不是其他第三方网站访问,要求访问时携带参数名为_csrf值为令牌,令牌在服务端产生,如果携带的令牌和服务端的令牌匹配成功,则正常访问。

<form class="form" action="/login" method="post"><!-- 在表单中添加令牌隐藏域 --><input type="hidden" th:value="${_csrf.token}" name="_csrf" th:if="${_csrf}"/><input type="text" placeholder="用户名" name="username"><input type="password" placeholder="密码" name="password"><button type="submit">登录</button>
</form>

会话管理

SpringSecurity提供了会话管理功能,它将用户信息保存再会话中,我们可以通过SecurityContext对象中获取用户信息。

@RestController
public class MyController {// 获取当前登录用户名@RequestMapping("/users/username")public String getUsername(){// 1.获取会话对象SecurityContext context = SecurityContextHolder.getContext();// 2.获取认证对象Authentication authentication = context.getAuthentication();// 3.获取登录用户信息UserDetails userDetails = (UserDetails) authentication.getPrincipal();return userDetails.getUsername();}
}

认证成功后的处理

如果在认证成功后,需要处理一些自定义的逻辑,可以使用登陆成功处理器。
1.自定义登录成功处理器

public class MyLoginSuccessHandler implements AuthenticationSuccessHandler {@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {// 拿到登录用户的信息UserDetails userDetails = (UserDetails)authentication.getPrincipal();System.out.println("用户名:"+userDetails.getUsername());System.out.println("一些操作...");// 重定向到主页response.sendRedirect("/main");}
}

2.配置登录成功处理器

http.formLogin() // 使用表单登录.loginPage("/login.html") // 自定义登录页面.usernameParameter("username") // 表单中的用户名项.passwordParameter("password") // 表单中的密码项.loginProcessingUrl("/login") // 登录路径,表单向该路径提交,提交后自动执行UserDetailsService的方法//         .successForwardUrl("/main")  //登录成功后跳转的路径.successHandler(new MyLoginSuccessHandler()) //登录成功处理器.failureForwardUrl("/fail"); //登录失败后跳转的路径

认证失败后的处理

如果在认证成功后,需要处理一些自定义的逻辑,可以使用登陆失败处理器。
1.自定义登录失败处理器

public class MyLoginFailureHandler implements AuthenticationFailureHandler {@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {System.out.println("记录失败日志...");response.sendRedirect("/fail");}
}

2.配置登录失败处理器

http.formLogin() // 使用表单登录.loginPage("/login.html") // 自定义登录页面.usernameParameter("username") // 表单中的用户名项.passwordParameter("password") // 表单中的密码项.loginProcessingUrl("/login") // 登录路径,表单向该路径提交,提交后自动执行UserDetailsService的方法//         .successForwardUrl("/main")  //登录成功后跳转的路径.successHandler(new MyLoginSuccessHandler()) //登录成功处理器//         .failureForwardUrl("/fail") //登录失败后跳转的路径.failureHandler(new MyLoginFailureHandler()); //登录失败处理器// 需要认证的资源
http.authorizeRequests().antMatchers("/login.html").permitAll() // 登录页不需要认证.antMatchers("/fail").permitAll() // 失败页不需要认证.anyRequest().authenticated(); //其余所有请求都需要认证

退出登录

当用户退出后,需要清楚认证状态、销毁HttpSession对象,跳转登录界面

1.配置退出登录的路径和退出后跳转的路径

// 退出登录配置
http.logout().logoutUrl("/logout") // 退出登录路径.logoutSuccessUrl("/login.html") // 退出登录后跳转的路径.clearAuthentication(true) //清除认证状态,默认为true.invalidateHttpSession(true); // 销毁HttpSession对象,默认为true

2.在网页中添加退出登录超链接

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>主页面</title>
</head>
<body>
<h1>主页面</h1>
<a href="/logout">退出登录</a>
</body>
</html>

退出成功处理器

我们也可以自定义退出成功处理器,在退出后清理一些数据,写法如下:

1.自定义退出成功处理器

public class MyLogoutSuccessHandler implements LogoutSuccessHandler {@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {System.out.println("清除一些数据...");response.sendRedirect("/login.html");}
}

2.配置退出成功处理器

// 退出登录配置
http.logout().logoutUrl("/logout") // 退出登录路径//         .logoutSuccessUrl("/login.html") // 退出登录后跳转的路径.clearAuthentication(true) //清除认证状态,默认为true.invalidateHttpSession(true) // 销毁HttpSession对象,默认为 true.logoutSuccessHandler(new MyLogoutSuccessHandler()); //自定义退出成功处理器

Remember Me

SpringSecurity提供了“记住我”功能,当使用"记住我"功能登陆后,SpringSecurity会生成一个令牌,令牌一方面保存在数据库里,另一方面生成一个叫remember-me的Cookie保存到客户端。之后客户端访问项目时自动携带令牌,不登录即可完成认证。

1.编写“记住我”配置类

@Configuration
public class RememberMeConfig {@Autowiredprivate DataSource dataSource;// 令牌Repository@Beanpublic PersistentTokenRepository getPersistentTokenRepository() {// 为Spring Security自带的令牌控制器设置数据源JdbcTokenRepositoryImpl jdbcTokenRepositoryImpl = new JdbcTokenRepositoryImpl();jdbcTokenRepositoryImpl.setDataSource(dataSource);//自动建表,第一次启动时需要,第二次启动时注释掉
//     jdbcTokenRepositoryImpl.setCreateTableOnStartup(true);return jdbcTokenRepositoryImpl;}
}

2.修改Security配置类

// 记住我配置
http.rememberMe().userDetailsService(userDetailsService)//登录逻辑交给哪个对象.tokenRepository(repository) //持久层对象.tokenValiditySeconds(30); //保存时间,单位:秒

3.在登录页面添加“记住我”复选框

<form class="form" action="/login" method="post"><input type="text" placeholder="用户名" name="username"><input type="password" placeholder="密码" name="password"><input type="checkbox" name="remember-me" value="true"/>记住我</br><button type="submit">登录</button>
</form>

总结

提示:这里对文章进行总结:

总的来说,Spring Security 为我们提供了一个强大而灵活的框架来保护应用程序的安全性。通过理解和应用所学的知识,我们可以构建更安全、可靠的 Web 应用程序。
希望这篇博客对你有所帮助,让你对 Spring Security 的认证有了更深入的了解。如果你有任何进一步的问题或想要深入探讨其他安全相关主题,请随时留言!

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

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

相关文章

设计模式之单例模式的懒饿汉

懒汉式 说白了就是你不叫我我不动&#xff0c;你叫我我才动。 类初始化模式&#xff0c;也叫延迟占位模式。在单例类的内部由一个私有静态内部类来持有这个单例类的实例。因为在 JVM 中&#xff0c;对类的加载和类初始化&#xff0c;由虚拟机保证线程安全。 public class Singl…

cesium键盘控制模型

效果&#xff1a; 由于对添加模型和更新位置api进行二次了封装&#xff0c;下面提供思路 1.添加模型 const person reactive({modelTimer: null,position: {lon: 104.07274,lat: 30.57899,alt: 1200,heading: 0,pitch: 0,roll: 0,}, }); window.swpcesium.addEntity.addMo…

SSH 无密登录配置

1)配置 ssh (1)基本语法 ssh 另一台电脑的 IP 地址 (2)ssh 连接时出现 Host key verification failed 的解决方法 [yuxuan@yuxuan102 ~]$ ssh yuxuan103 ➢ 如果出现如下内容 Are you sure you want to continue connecting (yes/no)? ➢ 输入 yes,并回车 (3)退回到 …

简单 Web Server 程序的设计与实现 (2024)

1.题目描述 Web 服务是 Internet 最方便与受用户欢迎的服务类型&#xff0c;它的影响力也远远超出了专业技术范畴&#xff0c; 已广泛应用于电子商务、远程教育、远程医疗与信息服务等领域&#xff0c;并且有继续扩大的趋势。目前很多 的 Internet 应用都是基于 Web 技术的&…

学习Go语言Web框架Gee总结--中间件Middleware(五)

学习Go语言Web框架Gee总结--中间件Middleware 网站学习来源&#xff1a; Gee 在Go语言中&#xff0c;web框架的中间件是一种非常常见的概念&#xff0c;它允许开发人员在处理HTTP请求和响应之间插入额外的逻辑。中间件可以用于处理日志记录、身份验证、授权、错误处理等 中间件…

Linux驱动学习—中断

1、中断基础概念 1.1 什么是中断 CPU在正常运行期间&#xff0c;由外部或者内部引起的时间&#xff0c;让CPU停下当前正在运行的程序&#xff0c;转而去执行触发他的中断所对应的程序&#xff0c;这就是中断。 响应中断的过程&#xff1a; <1>中断请求 <2>中断…

windows10下重置mysql8的root密码

控制台A启动mysql: mysqld --defaults-file“D:\program\phpstudy_pro\Extensions\MySQL8.0.12\my.ini” --console --skip-grant-tables --shared-memory 控制台B连接并清空密码&#xff1a; mysql -uroot -p -P3308 use mysql; update user set authentication_string‘’ w…

三、C语言中的分支与循环—for循环 (6)

本章分支结构的学习内容如下&#xff1a; 三、C语言中的分支与循环—if语句 (1) 三、C语言中的分支与循环—关系操作符 (2) 三、C语言中的分支与循环—条件操作符 与逻辑操作符(3) 三、C语言中的分支与循环—switch语句&#xff08;4&#xff09;分支结构 完 本章循环结构的…

JavaScript基本语法

文章目录 1. JavaScript 是什么1.1 JavaScript 和 HTML 和 CSS 之间的关系1.2 JavaScript 运行过程1.3 JavaScript 的组成 2. JavaScript 的书写形式2.1 行内式2.2 内嵌式2.3 外部式 3. 变量的使用3.1 静态变量和动态变量 4. 基本数据类型4.1 undefined 未定义数据类型4.2 null…

2.3_6 用信号量实现进程互斥、同步、前驱关系

2.3_6 用信号量实现进程互斥、同步、前驱关系 #mermaid-svg-fj0wp6tJGfadcT8h {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-fj0wp6tJGfadcT8h .error-icon{fill:#552222;}#mermaid-svg-fj0wp6tJGfadcT8h .error-t…

ENVI 各版本安装指南

ENVI下载链接 https://pan.baidu.com/s/1APpjHHSsrXMaCcJUQGmFBA?pwd0531 1.鼠标右击【ENVI 5.6(64bit&#xff09;】压缩包&#xff08;win11及以上系统需先点击“显示更多选项”&#xff09;选择【解压到 ENVI 5.6(64bit&#xff09;】。 2.打开解压后的文件夹&#xff0c…

mysql基础-数据操作之增删改

目录 1.新增数据 1.1单条数据新增 1.2多条数据新增 1.3查询数据新增 2.更新 2.1单值更新 2.2多值更新 2.3批量更新 2.3.1 批量-单条件更新 2.3.2批量-多条件更新 2.4 插入或更新 2.5 联表更新 3.删除 本次分享一下数据库的DML操作语言。 操作表的数据结构&#xf…

Explain详解与索引最佳实践

听课问题(听完课自己查资料) type中常用类型详细解释 null <- system <- const <- er_ref <- ref <- range <- index <- all Explain 各列解释 EXPLAIN SELECT* FROMactorLEFT JOIN film_actor ON actor_id actor.id; 1. id 代表执行的先后顺序 比如…

前端优化之一:dns预获取 dns-prefetch 提升页面载入速度

问题&#xff1a;怎么做到dns域解析&#xff1f; 用于优化网站页面的图片 问题&#xff1a;怎么提升网站性能&#xff1f; dns域解析&#xff0c;是提升网站的一个办法。 DNS Prefetch&#xff0c;即DNS预获取&#xff0c;是前端优化的一部分。 一般来说&#xff0c;在前端…

Python笔记01-你好Python

文章目录 Python简介环境安装Hello world开发工具 Python简介 python的诞生 1989年&#xff0c;为了打发圣诞节假期&#xff0c;Gudio van Rossum吉多 范罗苏姆&#xff08;龟叔&#xff09;决心开发一个新的解释程序&#xff08;Python雏形&#xff09; 1991年&#xff0c;第…

深度学习|交叉熵

文章目录 什么是交叉熵如何构造信息量的函数关于 C 1 C_1 C1​参数的选择关于 C 2 C_2 C2​参数的选择 一个系统的熵如何比较两个系统的熵交叉熵在神经网络中的应用参考 什么是交叉熵 熵是用来衡量一个系统的混乱程度&#xff0c;混乱程度也其实代表着整个系统内部的不确定性。…

C语言浮点型详解

1. 浮点型变量介绍 1.1 类型概览 浮点型变量用于存储小数数值&#xff0c;C语言提供了三种主要的浮点数类型&#xff1a;单精度浮点型&#xff08;float&#xff09;、双精度浮点型&#xff08;double&#xff09;、长双精度浮点型&#xff08;long double&#xff09;。 类…

听GPT 讲Rust源代码--compiler(15)

File: rust/compiler/rustc_arena/src/lib.rs 在Rust源代码中&#xff0c;rustc_arena/src/lib.rs文件定义了TypedArena&#xff0c;ArenaChunk&#xff0c;DroplessArena和Arena结构体&#xff0c;以及一些与内存分配和容器操作相关的函数。 cold_path<F: FnOnce,drop,new,…

MySQL 8.0中新增的功能(三)

字符集支持 默认字符集已从latin1更改为utf8mb4。utf8mb4字符集新增了几个排序规则&#xff0c;包括utf8mb4_ja_0900_as_cs&#xff0c;这是MySQL中首个用于Unicode的日语特定排序规则。 JSON增强功能 MySQL的JSON功能性方面进行了以下增强或添加&#xff1a; ->> &a…

解决 POST http://x.x.x.x:8000/aaa/ net::ERR_CONNECTION_TIMED_OUT

记录一下我遇到的问题和解决办法 我的项目前后端分离&#xff0c;在前端用的vue访问后端时连接不上后端&#xff0c;一切操作都触发不了后端&#xff0c;数据也传不到后端去&#xff1b; 原因&#xff1a;url有问题&#xff0c;url地址写的不是本机&#xff0c;所以导致连接超…