当前位置: 首页 > news >正文

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.lryc.cn/news/275015.html

相关文章:

  • 智能语音机器人NXCallbot
  • Vue 3中toRaw和markRaw的使用
  • 移动神器RAX3000M路由器不刷固件变身家庭云之三:外网访问家庭云
  • 基于多反应堆的高并发服务器【C/C++/Reactor】(中)线程池的启动和从线程池中取出一个反应堆实例
  • go语言gin框架的基本使用
  • TypeScript 从入门到进阶之基础篇(六) 类型(断言 、推论、别名)| 联合类型 | 交叉类型
  • Linux操作系统基础(14):文件管理-文件属性命令
  • metaSPAdes,megahit,IDBA-UB:宏基因组装软件安装与使用
  • Apache、MySQL、PHP编译安装LAMP环境
  • L1-087:机工士姆斯塔迪奥
  • 如何做一个炫酷的Github个人简介(3DContribution)
  • 基于单片机的护理床控制器设计
  • 《3D数学基础-图形和游戏开发》阅读笔记 | 3D数学基础 (学习中 1.6更新)
  • 解锁测试性能瓶颈:深度探讨JMeter分布式性能测试!
  • SiC电机控制器(逆变器)发展概况及技术方向
  • useContext
  • Java数据结构:1. 数据结构前置知识
  • Vue中使用Element UI的Table组件实现嵌套表格(最简单示例)
  • 如何使用RESTful API构建 web 应用程序
  • 开启Android学习之旅-4-Android集成FontAwesome
  • Qt——TCP UDP网络编程
  • 有什么安全处理方案可以有效防护恶意爬虫
  • Flutter3.X基础入门教程(2024完整版)
  • GEE——土地利用分类种两个矢量集合中不同列进行相减的方式(利用join进行连接处理)
  • mnn-llm: 大语言模型端侧CPU推理优化
  • Freemarker实现Html全站静态化
  • 16.顺子日期(14)
  • 《动手学深度学习》学习笔记 第5章 深度学习计算
  • 【Redis】非关系型数据库之Redis的介绍及安装配置
  • 3D模型轻量化