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

记SpringBoot3.x + SpringSecurity6.x的实现

系列文章目录

第一章 记SpringBoot3.x + Thymeleaf 项目实现(MVC架构模式)
第二章 记SpringBoot3.x + SpringSecurity6.x的实现

目录

前言

一、SpringSecurity能做什么

二、使用SpringSecurity

1. 在上一章新建的wqinfo项目中引入起步依赖

2. 运行项目体验SpringSecurity的防护

3. 放行我们自己的登录页面不被SpringSecurity保护拦截

4. 实现SpringSecurity的用户登录认证

(1)修改User实体对象实现UserDetails接口

(2)让SpringSecurity获得登录用户详情信息

(3)将MyUserDetailsService注入到SpringBean,修改配置类

5. 运行项目看效果

6. 简单总结下实现流程

7. Spring Security 登录认证流程

8. Spring Security退出

总结


前言

本章讲解SpringSecurity6.x的使用,基于java的配置。接着使用上一章的项目wqinfo继续编码。更早的时候还有基于XML配置文件的配置,想了解的可以关注我以前的文章。


一、SpringSecurity能做什么

用户认证(Authentication):就是验证用户合法登录。
用户授权(Authorization):判断用户拥有什么权限,可以访问什么资源
攻击防护:防御跨站请求伪造攻击,session攻击等

二、使用SpringSecurity

1. 在上一章新建的wqinfo项目中引入起步依赖

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

2. 运行项目体验SpringSecurity的防护

SpringSecurity依赖包引入之后就启动了防护作用,启动项目后在后台生成了密码,如下:

启动项目后浏览器访问:http://127.0.0.1:8080/index.html网页或http://127.0.0.1:8080/user/index请求都会拦截并跳转到SpringSecurity默认的登录页面。默认的用户为:user

登录成功后访问到了index.html页面

3. 放行我们自己的登录页面不被SpringSecurity保护拦截

我们需要将下图的/index请求放行:

编写配置类,此处先关闭csrf:

package com.wq.wqinfo.securityconfig;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;/*** @author lv* @date 2025年8月18日17点16分* SpringSecurity配置类*/
@Configuration //表明是一个配置类bean
@EnableWebSecurity //此注解1: 加载了WebSecurityConfiguration配置类, 配置安全认证策略。2: 加载了AuthenticationConfiguration, 配置了认证信息。
public class SecurityConfig {/*** 权限相关的配置,设置一些链接不要拦截* @param http* @return* @throws Exception*/@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {// 关闭csrfhttp.csrf(it->it.disable());// 权限http.authorizeHttpRequests(it->it.requestMatchers("/user/index").permitAll()  //设置需要放行的路径.anyRequest().authenticated()  //其他路径都要进行拦截);return http.build();}
}

此时运行项目就可以访问http://127.0.0.1:8080/user/index请求了。

成功访问到了我们自己写的登录页面。

4. 实现SpringSecurity的用户登录认证

(1)修改User实体对象实现UserDetails接口

package com.wq.wqinfo.domain;import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Date;/*** 用户领域实体bean数据表映射*/
public class User implements UserDetails {private static final long serialVersionUID = 1L;private long id;private String password;private String username;private Date addtime;public long getId() {return id;}public void setId(long id) {this.id = id;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public Date getAddtime() {return addtime;}public void setAddtime(Date addtime) {this.addtime = addtime;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

User实体类实现了UserDetails这个接口,并重写了UserDetails接口的5个方法。其中4个boolean方法默认值为false,而我在此处设置成true。因为设置为false在后面的示例中将无法正常登录。

(2)让SpringSecurity获得登录用户详情信息

package com.wq.wqinfo.service.impl;import com.wq.wqinfo.domain.User;
import com.wq.wqinfo.persistence.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;/*** @author lv* @date 2025年8月19日10点11分* 实现security提供的UserDetailsService接口,获取用户详细信息*/
@Component
public class MyUserDetailsService implements UserDetailsService {/*** 注入UserMapper获取数据库用户信息*/@Autowiredprivate UserMapper userMapper;/*** 实现UserDetailsService接口提供的方法,查询你数据库中用户表的信息,返回UserDetails* @param username* @return* @throws UsernameNotFoundException*/@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {System.out.println("----loadUserByUsername用户详情查询----用户名:" + username);/*** 此处返回用户详情有两种方式。* 一是将自定义的用户实体类User实现UserDetails接口,并返回。此时User中既包含你自定义的属性,也包含UserDetails接口的属性* 二是创建UserDetails对象,并返回。即通过用户名查询出用户信息后填装到org.springframework.security.core.userdetails.User默认提供的User对象中返回。*//*** 先将User实现UserDetails接口,返回*/User user = userMapper.findByUsername(username);if (user != null) {return user;}throw new UsernameNotFoundException(username + "用户名不存在!");}
}

代码中实现了UserDetailsService接口,重写了loadUserByUsername方法,方法中调用了查询数据库用户信息的方法userMapper.findByUsername(username)。这样SpringSecurity就可以查询到我们数据库中的用户信息了。


(3)将MyUserDetailsService注入到SpringBean,修改配置类

package com.wq.wqinfo.config;import com.wq.wqinfo.service.impl.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;/*** @author lv* @date 2025年8月18日17点16分* SpringSecurity配置类*/
@Configuration //表明是一个配置类bean
@EnableWebSecurity //此注解1: 加载了WebSecurityConfiguration配置类, 配置安全认证策略。2: 加载了AuthenticationConfiguration, 配置了认证信息。
public class SecurityConfig {@Autowiredprivate MyUserDetailsService userDetailsService;@Beanpublic AuthenticationManager authenticationManager(PasswordEncoder passwordEncoder){System.out.println("用户认证管理配置---AuthenticationManager.........");//关联UserDetails和PasswordEncoderDaoAuthenticationProvider provider = new DaoAuthenticationProvider();//将编写的MyUserDetailsService注入进来provider.setUserDetailsService(userDetailsService);//将使用的密码编译器加入进来provider.setPasswordEncoder(passwordEncoder);//将DaoAuthenticationProvider对象放置到AuthenticationManager 中管理ProviderManager providerManager = new ProviderManager(provider);return providerManager;}/*** 装备一个密码转码器* @return*/@Beanpublic PasswordEncoder encoderPwd(){System.out.println("密码转码器配置---PasswordEncoder。。。。。。。。");return new BCryptPasswordEncoder();//进行转码,指定加密机制}/*** 权限相关的配置,设置一些链接不要拦截* @param http* @return* @throws Exception*/@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {System.out.println("权限配置---使用SecurityFilterChain过滤器进行配置。。。。。。。。。");// 关闭csrfhttp.csrf(it->it.disable());// 权限http.authorizeHttpRequests(it ->it.requestMatchers("/user/index").permitAll()  //设置需要放行的路径/user/index.anyRequest().authenticated()  //其他路径都要进行拦截);// 基于表单的登录认证http.formLogin(from->from.loginPage("/user/index")  //跳转到自定义的登录页面.loginProcessingUrl("/security_login")  //处理前端的登录请求,与from表单的action一致。这个请求会进入Spring Security的登录验证方法,而非自己写的。.defaultSuccessUrl("/user/success")  //默认请求成功之后的跳转路径,一般情况路径指向自定义方法或页面);return http.build();}
}

配置类中自动装配了MyUserDetailsService通过AuthenticationManager将UserDetails和PasswordEncoder关联,以Bean的方式注入到Spring容器中。SecurityFilterChain过滤器中加入基于表单的登录认证,通过loginProcessingUrl配置关联自定义登录页的请求地址,并将请求发送到Spring Security的登录验证方法,而非自己写的。

关于formLogin配置,是Spring Security提供的基于表单的登录认证。用于传统的web应用,Spring Security 负责处理表单登录请求、验证用户和密码、生成会话等操作。

到此我们就完成了Spring Security6.x的自定义表单页面用户登录认证。下面去看看运行效果。

5. 运行项目看效果

先看看项目结构:

启动项目时运行了配置类,注册了密码转码器、认证管理、权限配置过滤器

访问:http://127.0.0.1:8080/hello?name=lisi 进行了拦截,回到了登录页


输入用户名密码后登录成功,访问到了http://127.0.0.1:8080/hello?name=lisi 地址:

我们再直接到登录页面:http://127.0.0.1:8080/user/index ,输入用户和密码:

点击登录后登录成功如下图:

我们看看后台打印情况:

1、2、3步加载了配置文件,4、5访问到登录页面,6查询用户,7输入用户密码后登录成功。4,5步因为配置类中权限放行和表单认证配置时都配置了/user/index请求,所以打印了两次。从运行效果来看我们已经实现了Spring Security6.x的自定义登录页用户登录的认证。

6. 简单总结下实现流程

1. 配置类放行登录页请求路径/user/index(logins.html)

2. User实体类实现UserDetails接口及方法

3. 实现UserDetailsService接口,查询数据库用户详情信息

4. 配置类注入AuthenticationManager将UserDetailsService和PasswordEncoder关联,然后再配置
SecurityFilterChain过滤器的formLogin,其中loginProcessingUrl中的请求路径就是登录页的表单提交路径、保持一致。如此便能将页面提交的用户名和密码交给Spring Security管理,进行用户的匹配认证(我们不用关心认证过程)。认证的过程其实就是在AuthenticationManager中将页面提交的用户名,密码和UserDetailsService中获得的用户信息进行匹配认证。

7. Spring Security 登录认证流程

图中流程可分为以下几个核心步骤:

  1. 请求拦截与令牌生成 (UsernamePasswordAuthenticationFilter)

    • 拦截请求:过滤器默认拦截 /login (POST) 请求。

    • 提取凭证:从 HttpServletRequest 中获取用户名和密码参数(默认是 username 和 password)。

    • 创建令牌:使用获取到的凭证创建一个 未认证 的 UsernamePasswordAuthenticationToken 对象(它是 Authentication 接口的一个实现)。

  2. 认证调度 (AuthenticationManager)

    • 过滤器将上一步创建的令牌交给 AuthenticationManager 的 authenticate() 方法,启动认证流程。

    • AuthenticationManager 是一个认证调度器,它本身不直接处理认证,而是根据令牌类型委托给相应的 AuthenticationProvider(最常用的是 DaoAuthenticationProvider)来处理。

  3. 加载用户数据 (UserDetailsService)

    • DaoAuthenticationProvider 会调用 UserDetailsService 的 loadUserByUsername(String username) 方法。

    • UserDetailsService 根据用户名从数据源(如数据库、内存等)中查询用户信息,并封装成一个 UserDetails 对象返回。如果用户不存在,则抛出 UsernameNotFoundException

  4. 密码校验 (PasswordEncoder)

    • 认证提供者获取到 UserDetails 后,会使用 PasswordEncoder 来比对用户提交的原始密码(来自令牌)和数据库中存储的加密密码(来自 UserDetails)。

    • 这是一个至关重要的安全步骤,确保了密码不以明文形式存储和对比。

  5. 认证结果与上下文存储

    • 成功:如果所有检查都通过(用户存在且密码正确),认证提供者会创建一个已认证的 Authentication 对象(通常包含 UserDetails 和权限信息),并将其返回给过滤器。

    • 过滤器 将这个已认证的对象存入 SecurityContextHolder 的 SecurityContext 中。这意味着该用户的认证状态对于本次会话(当前线程)是立即可用的,后续的过滤器或控制器可以通过 SecurityContextHolder 获取到当前用户信息。

  6. 成功处理 (AuthenticationSuccessHandler)

    • 认证成功后,过滤器会调用配置的 AuthenticationSuccessHandler

    • 默认行为:重定向到之前缓存的请求页面或默认首页(defaultSuccessUrl)。

    • 常见自定义行为:返回登录成功的 JSON 数据、重定向到特定页面等。

  7. 失败处理 (AuthenticationFailureHandler)

    • 如果在上述任何步骤中出现异常(用户不存在、密码错误、账户锁定等),认证过程都会中止并抛出 AuthenticationException

    • 过滤器会捕获异常,并调用 AuthenticationFailureHandler 进行处理。

    • 默认行为:重定向回登录页面并附带错误信息。

    • 常见自定义行为:返回包含具体错误原因的 JSON 数据。

有兴趣的可以再看看UsernamePasswordAuthenticationFilter类的代码:

8. Spring Security退出

Spring Security默认的退出请求“/logout”,也可以自设置。支持get和post提交。退出登陆时,会清除内存中的登录认证用户主体信息,销毁会话session等。

        //添加退出的映射地址,退出后放行/user/loginOut,否则会受SpringSecurity保护拦截http.logout(logout -> {logout.logoutUrl("/logout").logoutSuccessUrl("/user/loginOut").permitAll(); //SpringSecurity默认退出地址是/logout,也可以自定。});


总结

用户认证(Authentication):就是验证用户合法登录。到这就结束了。该说的不该说的都说了。也就没什么总结的了。

http://www.dtcms.com/a/346224.html

相关文章:

  • 20250822日记
  • 深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)第四章知识点问答(37题)
  • 如何编译botan加密库?
  • 模板商城探秘:DINO-X 定制模板指南(1)
  • Ansys Motor-CAD:概述(EMag、THERM、LAB、MECH)
  • Unreal Engine UActorComponent
  • 豆包 + 蘑兔,破解写歌难题!
  • 普中烧录软件 PZISP,打不开,提示“应用程序无法启动,因为应用程序并行配置不正确.....”
  • 深度学习设计模式:责任链(Chain of Responsibility)模式(例子+业务场景+八股)
  • RFID技术在铸管生产车间AGV小车的使用
  • SQL 复杂连接与嵌套查询的优化之道:从自连接、不等值连接到 CTE 的体系化实践
  • 「数据获取」《中国农村统计年鉴》1985-2024(获取方式看绑定的资源)
  • Python中各种数据类型的常用方法
  • 国产轻量级桌面GIS软件Snaplayers从入门到精通(20)
  • 自定义单线通信协议解析
  • Unreal Engine Simulate Physics
  • MySQL InnoDB记录存储结构深度解析
  • windows 帮我写一个nginx的配置,端口是9999,静态资源的路径是D:\upload
  • 企业架构之微服务应用架构
  • 深入理解底层通信协议和应用层协议的区别
  • Java Stream常见函数与应用案例
  • 大模型应用发展与Agent前沿技术趋势(下)
  • Debezium导致线上PostgreSQL数据库磁盘日志飙升处理方案
  • Unreal Engine ATriggerVolume
  • java 海报、图片合成
  • 蓝牙部分解析和代码建构
  • SSH如何访问只有没有公网IP的云服务器
  • loss 基本稳定,acc 一直抖动,如何优化?
  • assetbuddle hash 比对
  • 【计算机网络】 IPV4和IPV6区别