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

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

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// 安全过滤器链Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity ,CorsConfigurationSource configurationSource ) throws Exception { //httpSecurity方法参数注入Bean
        return httpSecurity
                // 配置自己的登录页面
                .formLogin( (formLogin) ->{
                    formLogin.loginProcessingUrl("/login") // 登录账户密码往哪个地址提交
                }) 
                .build();
    }
}

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

禁用 CSRF:

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

    
@Configuration
@EnableMethodSecurity
// 配置spring的容器
public class SecurityConfig {
    @Bean// 安全过滤器链Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity ,CorsConfigurationSource configurationSource ) throws Exception { //httpSecurity方法参数注入Bean
        return 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
     */
    @Bean
    public 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// 安全过滤器链Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity ,CorsConfigurationSource configurationSource ) throws Exception { //httpSecurity方法参数注入Bean
        return 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实现类 

    @Override
    public 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// 安全过滤器链Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity ,CorsConfigurationSource configurationSource ) throws Exception { //httpSecurity方法参数注入Bean
        return httpSecurity
                // 配置自己的登录页面
                .formLogin( (formLogin) ->{
                    formLogin.loginProcessingUrl("/login") // 登录账户密码往哪个地址提交
                            .successHandler(myAuthenticationSuccessHandler)
                            .failureHandler(myAuthenticationFailHandler); // 登录失败的回调
                }) 
                .build();
    }
}
 MyAuthenticationSuccessHandle(登录成功的处理器):
@Component
public class MyAuthenticationSuccessHandle implements AuthenticationSuccessHandler {
    @Override
    public 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 {
    @Override
    public 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 {
    @Override
    public 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.kler.cn/a/596211.html

相关文章:

  • 工作相关记录
  • 深入了解 C# 中的 LINQ:功能、语法与应用解析
  • 基于深度学习的皮肤癌智能检测与语音提示系统【python源码+Pyqt5界面+数据集+训练代码】
  • 使用ArgoCD管理Kubernetes部署指南
  • php文件包含与伪协议
  • (electron 报错)TypeError: Cannot read property ‘upgrade‘ of undefined
  • MySQL身份验证的auth_socket插件
  • Java基础面试题学习
  • ruoyi-vue部署linux(war包方式)
  • 【DNN量化工具】QKeras 工具简介
  • springmvc中使用interceptor拦截
  • TensorFlow 与 Matplotlib 核心知识点及实战案例
  • 2953. 统计完全子字符串(将题目中给的信息进行分组循环)
  • JavaIO流的使用和修饰器模式(直击心灵版)
  • 解释什么是受控组件和非受控组件
  • git 查看某个函数的所有提交日志
  • Web爬虫利器FireCrawl:全方位助力AI训练与高效数据抓取。本地部署方式
  • 【入门初级篇】报表基础操作与功能介绍
  • 大数据处理最容易的开源平台
  • 基于Python编程语言实现“机器学习”,用于车牌识别项目