SpringSecurity——前后端分离登录认证

server/2025/3/31 11:53:12/

SpringSecurity——前后端分离登录认证的整个过程

前端:

使用Axios向后端发送请求

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>登录</title><script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<form>用户名:<input type="text" name="username" id="username"><br>密码:<input type="password" name="password" id="password"><br><input type="button" value="登录" onclick="login()">
</form>
</body>
<script>function login() {var username = document.getElementById("username").value;var password = document.getElementById("password").value;let formData = new FormData(); // 创建formData对象formData.append("username", username);formData.append("password", password);axios.post("http://localhost:8080/login", formData).then(function (response) {console.log(response);if (response.data.code === 200) {alert("登录成功");window.location.href = "welcome.html";} else {alert("登录失败");}}).catch(function (error){console.log(error);});}
</script></html>

 后端:

Spring Security 配置指定该 URL 作为登录处理入口。

    
@Configuration
@EnableMethodSecurity
// 配置spring的容器
public class SecurityConfig {@Bean// 安全过滤器链Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity ,CorsConfigurationSource configurationSource ) throws Exception { //httpSecurity方法参数注入Beanreturn httpSecurity// 配置自己的登录页面.formLogin( (formLogin) ->{formLogin.loginProcessingUrl("/login") // 登录账户密码往哪个地址提交}) .build();}
}

由于现在是前后端分离的,所以拿不到CSRF,因此我们需要先禁用CSRF:

禁用 CSRF:

在基于 Token 的认证中,由于不依赖 Session,因此通常会关闭 CSRF 保护。可以在 HttpSecurity 中调用 .csrf().disable() 来实现这一点。

    
@Configuration
@EnableMethodSecurity
// 配置spring的容器
public class SecurityConfig {@Bean// 安全过滤器链Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity ,CorsConfigurationSource configurationSource ) throws Exception { //httpSecurity方法参数注入Beanreturn httpSecurity// 配置自己的登录页面.formLogin( (formLogin) ->{formLogin.loginProcessingUrl("/login") // 登录账户密码往哪个地址提交}) .csrf((csrf)->{// 禁止csrf跨站请求,禁用之后,肯定就不安全了,有csrf网络攻击的风险,后续加入jwt是可以防御的csrf.disable();}).build();}
}

配置 formLogin.loginProcessingUrl("/login") 实际上告诉 Spring Security:

  • 当收到指向 /login 的 POST 请求时,Spring Security 内部的过滤器(例如 UsernamePasswordAuthenticationFilter)会拦截这个请求,并自动处理用户的认证逻辑。
  • 你不需要在自己的 Controller 中实现这个 /login 接口,因为 Spring Security 会接管并执行用户名、密码的验证,以及后续的成功或失败处理(如果你配置了相应的 successHandlerfailureHandler)。

存在跨域问题:

协议不同会跨域  https://localhost:8080    http://localhost:8080

  1. 端口不同会跨域:http://localhost:10492    http://localhost:8080 
  2. 域名不同会跨域:http://bjpowernode.com   http://baidu.com 

三个里面有任何一个不同,都是跨域,跨域是浏览器不允许的,浏览器是为了安全,不允许你跨域访问

跨域资源共享(CORS)配置

  • 允许跨域访问:
    前后端分离架构中,前端通常与后端不在同一个域名下,因此必须在后端配置 CORS 策略。可以通过在 HttpSecurity 配置中调用 .cors() 方法通常需要自己定义一个 CorsConfigurationSource Bean。在自己定义 CorsConfigurationSource Bean当中我们需要返回CorsConfigurationSource(接口)的实现类——通常情况下是UrlBasedCorsConfigurationSource

@Configuration
@EnableMethodSecurity
// 配置spring的容器
public class SecurityConfig {/*** 配置跨域* @return*/@Beanpublic CorsConfigurationSource configurationSource() {UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();// 跨域配置CorsConfiguration corsConfiguration = new CorsConfiguration();corsConfiguration.setAllowedOrigins(Arrays.asList("*")); // 允许的请求来源corsConfiguration.setAllowedMethods(Arrays.asList("*")); // 允许的请求方法corsConfiguration.setAllowedHeaders(Arrays.asList("*"));// 允许的请求头// 注册配置urlBasedCorsConfigurationSource.registerCorsConfiguration("/**",corsConfiguration);return urlBasedCorsConfigurationSource;}@Bean// 安全过滤器链Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity ,CorsConfigurationSource configurationSource ) throws Exception { //httpSecurity方法参数注入Beanreturn httpSecurity// 配置自己的登录页面.formLogin( (formLogin) ->{formLogin.loginProcessingUrl("/login") // 登录账户密码往哪个地址提交}) .csrf((csrf)->{// 禁止csrf跨站请求,禁用之后,肯定就不安全了,有csrf网络攻击的风险,后续加入jwt是可以防御的csrf.disable();}).cors((cors)->{ // 允许前端跨域访问cors.configurationSource( configurationSource);}).build();}
}

凭证接收和验证: Spring Security框架使用 UsernamePasswordAuthenticationFilter 拦截请求,获取用户提交的账号和密码。

用户信息查询:

UserServiceImpl重写loadUserByUsername方法 从数据库中加载用户信息,返回一个包含用户状态和权限信息的 UserDetails(在本例中为 TUser 对象)。

重写loadUserByUsername方法需要service接口,需要继承springsecurity框架的UserDetailsService接口

// 我们的处理登录的service接口,需要继承springsecurity框架的UserDetailsService接口
public interface UserService extends UserDetailsService {
}

service实现类 

    @Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 通过用户名查询数据库TUser user =  userMapper.selectByLoginAct(username);if (user == null){throw new UsernameNotFoundException("用户不存在");}return user; // 实现了UserDetails接口,包含所有字段}

状态检查和密码比对: 框架会检查用户对象的状态(例如账户是否有效)以及比对密码是否匹配。

登录成功或失败的处理(处理器):

根据认证结果决定登录成功后的跳转(默认跳转到上一次请求的地址或项目根路径)或失败后的处理(重定向到 /login?error),但在前后端分离场景中,需要通过自定义 Handler 返回 JSON 格式的响应。

@Configuration
@EnableMethodSecurity
// 配置spring的容器
public class SecurityConfig {@Bean// 安全过滤器链Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity ,CorsConfigurationSource configurationSource ) throws Exception { //httpSecurity方法参数注入Beanreturn httpSecurity// 配置自己的登录页面.formLogin( (formLogin) ->{formLogin.loginProcessingUrl("/login") // 登录账户密码往哪个地址提交.successHandler(myAuthenticationSuccessHandler).failureHandler(myAuthenticationFailHandler); // 登录失败的回调}) .build();}
}
 MyAuthenticationSuccessHandle(登录成功的处理器):
@Component
public class MyAuthenticationSuccessHandle implements AuthenticationSuccessHandler {@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {R result = R.builder().code(200).msg("登录成功").info(authentication.getPrincipal()).build();response.setContentType("application/json;charset=utf-8");String json = JSONUtil.toJsonStr(result);response.getWriter().write(json);}
}
 MyAuthenticationFailHandle(登录失败的处理器):
@Component
public class MyAuthenticationFailHandle implements AuthenticationFailureHandler {@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {R result = R.builder().code(500).msg("登录失败").info(exception.getMessage()).build();response.setContentType("application/json;charset=utf-8");String json = JSONUtil.toJsonStr(result);response.getWriter().write(json);}
}
退出成功的处理器
@Component
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {R result = R.builder().code(200).msg("退出成功").info(authentication.getPrincipal()).build();response.setContentType("application/json;charset=utf-8");String json = JSONUtil.toJsonStr(result);response.getWriter().write(json);}
}

在 Spring Security 中,退出操作(logout)的设计逻辑与登录有所不同。登录过程涉及用户凭证验证、状态检查和密码匹配等环节,这些都有可能失败,所以需要提供失败处理器(如登录失败处理器)。而退出操作本质上只是清理会话、清空 SecurityContext 等动作,通常不会出现“失败”的情况。因此,框架只提供了退出成功处理器(LogoutSuccessHandler),而没有专门的退出失败处理器。

无状态认证的考虑:

由于不再使用传统 Session 记录用户状态,后续访问其他需要认证的接口时会提示未登录,此时通常会引入 JWT 等机制来维持用户认证状态。

没有session、前端cookie中也不会存储sessionid;那么这样的话,用户状态怎么保持呢?

需要使用我们下面介绍的jwt解决该问题;

不分离的:Tomcat 【thymeleaf  <----> (controller、sucurity)】

前后端分离:Nginx 【Vue】  <----jwt---->  Tomcat 【 (controller、sucurity) 】


http://www.ppmy.cn/server/177115.html

相关文章

基于DeepSeek的智能体搭建

智能体是什么&#xff1f; 智能体是一种生成式人工智能应用程 序&#xff0c;其核心是通过观察环境并利用工 具自主采取行动以实现特定目标。 智能体架构 •大模型&#xff08;Language Model&#xff09;&#xff1a;作为agent流程的集中决策者使用的语言模型&#xff0c;能…

网络华为HCIA+HCIP WLAN

目录 WLAN 敏捷分布式AP架构 有线侧组网概念&#xff1a;CAPWAP协议 CAPWAP隧道 功能 有线侧组网概念&#xff1a;AP-AC组网方式 有线侧组网概念&#xff1a;AC连接方式 无线侧组网概念&#xff1a;无线通信系统 无线侧组网概念&#xff1a;无线电磁波 无线侧组网概…

Breeze 25A FOC 电调:无人机动力系统的智能心脏|精准控制 × 高效输出 × 多重防护

—— 以工业级技术重构无人机飞行性能 —— ▌核心技术突破 ✓ 创新 Vfast 观测器算法&#xff1a;实现 0.5ms 级响应速度&#xff0c;动态补偿误差&#xff1c;0.1% ✓ 矢量控制&#xff08;FOC&#xff09;技术&#xff1a;相比传统方波驱动效率提升 18%&#xff0c;力效比达…

基于SpringBoot+Vue3实现的宠物领养管理平台功能一

一、前言介绍&#xff1a; 1.1 项目摘要 随着社会经济的发展和人们生活水平的提高&#xff0c;越来越多的人开始关注并参与到宠物领养中。宠物已经成为许多家庭的重要成员&#xff0c;人们对于宠物的关爱和照顾也日益增加。然而&#xff0c;传统的宠物领养流程存在诸多不便&a…

【设计模式】装饰模式

六、装饰模式 装饰(Decorator) 模式也称为装饰器模式/包装模式&#xff0c;是一种结构型模式。这是一个非常有趣和值得学习的设计模式&#xff0c;该模式展现出了运行时的一种扩展能力&#xff0c;以及比继承更强大和灵活的设计视角和设计能力&#xff0c;甚至在有些场合下&am…

如何构建简单有效的AI Agents代理?

工程技术 在过去的一年里&#xff0c;我们与数十个跨行业的团队合作&#xff0c;构建基于大型语言模型&#xff08;LLM&#xff09;的代理。我们发现&#xff0c;最成功的实现并不是使用复杂的框架或专门的库&#xff0c;而是采用简单、可组合的模式。 在本文中&#xff0c;我…

Compose 实践与探索十六 —— 与传统的 View 系统混用

Compose 发展初期的几年&#xff0c;会是新的模块用 Compose 写&#xff0c;然后逐渐的把老界面从 View 替换成 Compose 组件&#xff0c;直到全部或几乎全部是 Compose 代码的模式。 原生的 SurfaceView 与 TextureView 的重点是在它们底层的 Surface API&#xff0c;而不是 V…

浅谈跨平台框架的演变(H5混合开发->RN->Flutter)

引言 这里分为四个阶段&#xff1a; 第一阶段 &#xff1a; 原生开发 第二阶段 &#xff1a; H5混合开发 第三阶段&#xff1a; 跨平台RN 第四阶段&#xff1a; 跨平台Flutter 正文 第一阶段&#xff1a; 原生开发 开发成本比较大 &#xff1a; 需要Android 和ios 开发两…