spring boot(学习笔记第十三课)

news2024/9/23 9:31:06

spring boot(学习笔记第十三课)

  • Spring Security的logout,传统后端开发模式和前后端分离模式的不同,invalidateHttpSession不好用,bug?

学习内容:

  1. 传统后端开发模式 vs 前后端分离模式
  2. Spring Security的logout功能

1. 传统后端开发模式 vs 前后端分离模式

  1. 传统后端开发模式
    上面主要练习传统后端开发模式,在这种模式下,页面的渲染都是请求后端,在后端完成页面的渲染。认证的页面都是通过https://localhost:8080/loginPage进行用户名和密码的form填写,之后重定向到需要认证的资源的页面。
    正如[spring boot(学习笔记第十二课)](https://blog.csdn.net/s在这里插入图片描述
    ealaugh1980/article/details/140224760)的练习的那样,在传统后端开发模式,需要配置各种页面.
    .formLogin(form -> form.loginPage("/loginPage")
              .loginProcessingUrl("/doLogin")//这里的url不用使用controller进行相应,spring security自动处理
              .usernameParameter("uname")//页面上form的用户名
              .passwordParameter("passwd")
              .defaultSuccessUrl("/index")//默认的认证之后的页面
              .failureForwardUrl("/loginPasswordError"))//默认的密码失败之后的页面
    .exceptionHandling(exceptionHandling ->
                            exceptionHandling.accessDeniedHandler(new CustomizeAccessDeniedHandler()))
    
  2. 前后端分离开发模式
    现在web application的已经过渡到了前后端分离开发模式,而spring boot security也兼容这种模式。
    在这里插入图片描述
    接下来通过使用postman,模拟下前后端分离模式的spring security开发和使用场景。
    • 指定认证成功和失败的handler
      注意,这里一定要去掉 .loginPage("/loginPage")
      .formLogin(form -> form.loginProcessingUrl("/loginProcess")//这里对于前后端分离,提供的非页面访问url
                 .usernameParameter("uname")
                 .passwordParameter("passwd")
                 .successHandler(new SuccessHandler())
                 .failureHandler(new FailureHandler()))
      
    • 定义认证成功和失败的handler
      //success handler
          private static class SuccessHandler implements AuthenticationSuccessHandler {
              @Override
              public void onAuthenticationSuccess(
                      HttpServletRequest httpServletRequest,
                      HttpServletResponse httpServletResponse,
                      Authentication authentication
              ) throws IOException {
                  Object principal = authentication.getPrincipal();
                  httpServletResponse.setContentType("application/json;charset=utf-8");
                  PrintWriter printWriter = httpServletResponse.getWriter();
                  httpServletResponse.setStatus(200);
                  Map<String, Object> map = new HashMap<>();
                  map.put("status", 200);
                  map.put("msg", principal);
                  ObjectMapper om = new ObjectMapper();
                  printWriter.write(om.writeValueAsString(map));
                  printWriter.flush();
                  printWriter.close();
              }
          }
      
          //failure handler
          private static class FailureHandler implements AuthenticationFailureHandler {
              @Override
              public void onAuthenticationFailure(
                      HttpServletRequest httpServletRequest,
                      HttpServletResponse httpServletResponse,
                      AuthenticationException authenticationException
              ) throws IOException {
                  httpServletResponse.setContentType("application/json;charset=utf-8");
                  PrintWriter printWriter = httpServletResponse.getWriter();
                  httpServletResponse.setStatus(401);
                  Map<String, Object> map = new HashMap<>();
                  map.put("status", 401);
                  if (authenticationException instanceof LockedException) {
                      map.put("msg", "账户被锁定,登陆失败");
                  } else if (authenticationException instanceof BadCredentialsException) {
                      map.put("msg", "账户输入错误,登陆失败");
                  } else {
                      map.put("msg", authenticationException.toString());
                  }
                  ObjectMapper om = new ObjectMapper();
                  printWriter.write(om.writeValueAsString(map));
                  printWriter.flush();
                  printWriter.close();
              }
      
    • 一定要将/loginProcesspermitAll打开。注意,这里的习惯是将认证相关的url都定义成login开头的,并且一起进行/login*permitAll设定
          @Bean
          SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
              httpSecurity.authorizeHttpRequests(auth ->
                              auth.requestMatchers("/login*")
                                      .permitAll()
      
    • 使用postman进行认证测试。
      • pattern-1 正确的密码和用户名
        这里使用http://localhost:8080/loginProcess?uname=finlay_user&passwd=123456进行访问。注意,一定要是用post,不能使用get
        这里看到SuccessHandler
        在这里插入图片描述
    • pattern-2 错误的密码和用户名
      在这里插入图片描述
    • 认证成功,但是访问资源权限不够,需要设置exceptionHandling
      • 设置 exceptionHandling.accessDeniedHandler
       .exceptionHandling(exceptionHandling ->
                exceptionHandling.accessDeniedHandler(new CustomizeAccessDeniedHandler()))
      
      • 定义 exceptionHandler
        注意,在上一课传统后端开发模式的时候,定义的是redirect到画面,但是前后端分离模式,定义JSON返回值
        • 传统后端开发模式
        // 传统后端开发模式
        private static class CustomizeAccessDeniedHandler implements AccessDeniedHandler {
        		@Override
        		public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        			   response.sendRedirect("/loginNoPermissionError");
        		}
        }
        
        • 传统前后端分离开发模式(JSON返回)
        // 传统前后端开发模式
        private static class CustomizeAccessDeniedHandler implements AccessDeniedHandler {
        		@Override
        		public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        			   response.sendRedirect("/loginNoPermissionError");
        		}
        }
        
        • 访问/loginProcess,使用finlay_user(ROLE==user)进行登录
          在这里插入图片描述
        • 访问/db/hello,这里需要ROLE==DBA)进行登录,但是目前的httpSession不满足条件。在这里插入图片描述

2. Spring Security的logout功能

这里httpSession的如果需要logout,这里练习如何进行logout动作。

  1. 传统后端开发模式如何开发logout
    注意,这里传统后端开发模式需要将successHandlerfailureHandlerlogoutSuccessHandler都注释掉,否则,这个的对应的url设置都会无效
    .formLogin(form ->
             form.loginProcessingUrl("/loginProcess")//这里对于前后端分离,提供的非页面访问url
                     .usernameParameter("uname")
                     .passwordParameter("passwd")
                     .loginPage("/loginPage")
                     .failureForwardUrl("/loginPasswordError")
                     .successForwardUrl("/index"))
    //                                .successHandler(new SuccessHandler())
    //                                .failureHandler(new FailureHandler()))
     .logout(httpSecurityLogoutConfigurer ->
             httpSecurityLogoutConfigurer.logoutUrl("/logout")
                     .clearAuthentication(true)
                     .invalidateHttpSession(true)
                     .logoutSuccessUrl("/loginPage"))
    //                                .logoutSuccessHandler(new MyLogoutHandler()))
     .exceptionHandling(exceptionHandling ->
             exceptionHandling.accessDeniedHandler(new CustomizeAccessDeniedHandler()))
     .csrf(csrf -> csrf.disable())//csrf跨域访问无效
     .sessionManagement(session -> session
             .maximumSessions(-1)
             .maxSessionsPreventsLogin(true));
    
    • 设置logout处理的url
      .logoutUrl(“/logout”),这里的/logouot不需要进行对应,spring boot security会进行响应处理。
    • 对logout进行处理
       .logout(httpSecurityLogoutConfigurer ->
               httpSecurityLogoutConfigurer.logoutUrl("/logout")
                   .clearAuthentication(true)
                   .invalidateHttpSession(true)
                   .logoutSuccessUrl("/loginPage"))
      
      • clearAuthenticationSpring Security 中的一个方法,用于清除当前用户的认证信息,即使当前用户注销登录。在 SecurityContextHolder 中保存的 SecurityContext 对象将被清除,这意味着在下一次调用 SecurityContextHolder.getContext() 时,将不再有认证信息。
      • .invalidateHttpSession(true)是将httpSession删除,彻底进行logout
      • .logoutSuccessUrl("/loginPage"))调用将重定向到行的页面/logoutPage,这里是使用登录的页面。注意,这里如果调用.logoutSuccessHandler(new MyLogoutHandler())进行设定的话,就是使用前后端分离开发模式logoutSuccessUrl("/loginPage")即便设置也会无效
    • 设置logout处理页面(controller在页面上表示登录用户的用户名
       @GetMapping("/logoutPage")
          public String logoutPage(Model model) {
              String userName = "anonymous";
              Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
              if (authentication != null && authentication.isAuthenticated()) {
                  if (authentication.getName() != null) {
                      userName = authentication.getName();
                  }
              }
              model.addAttribute("login_user",userName);
              return "logout";
          }
      
    • 设置logout处理页面(html
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>logout</title>
      </head>
      <body>
      <div th:text="${login_user}"></div>
      <form th:action="@{/logout}" method="post">
          <button type="submit" class="btn">Logout</button>
      </form>
      </body>
      </html>
      
    • 使用logout功能进行logout
      在这里插入图片描述
      在显示logout按钮的同时,也显示出了Authentication authentication = SecurityContextHolder.getContext().getAuthentication();取出来的login_user名字。
    • 点击logout按钮,成功后返回 .logoutSuccessUrl("/loginPage"))在这里插入图片描述
  2. 前后端分离开发模式如何开发logout
    • .logoutSuccessUrl("/loginPage"))替换成 .logoutSuccessHandler(new MyLogoutHandler()))

       .logout(httpSecurityLogoutConfigurer ->
               httpSecurityLogoutConfigurer.logoutUrl("/logout")
                       .clearAuthentication(true)
                       .invalidateHttpSession(true)
      //                                .logoutSuccessUrl("/loginPage"))
                       .logoutSuccessHandler(new MyLogoutHandler()))
      
    • 定义MyLogoutHandlerlogout结果包装成JSON格式,传给前端。

          private static class MyLogoutHandler implements LogoutSuccessHandler {
              @Override
              public void onLogoutSuccess(HttpServletRequest request
                      , HttpServletResponse response
                      , Authentication authentication) throws IOException {
                  HttpSession session = request.getSession(false);
                  if (session != null) {
                      // 使会话失效
                      session.invalidate();
                  }
                  response.setContentType("application/json;charset=utf-8");
                  PrintWriter printWriter = response.getWriter();
                  response.setStatus(200);
                  Map<String, Object> map = new HashMap<>();
                  map.put("status", 200);
                  map.put("msg", "logout OK");
                  ObjectMapper om = new ObjectMapper();
                  printWriter.write(om.writeValueAsString(map));
                  printWriter.flush();
                  printWriter.close();
              }
          }
      
    • 如果logout完毕了,没有有效httpSession,那么访问/db/hello资源的话,怎么让spring security返回JSON,让前端框架接收到呢。这里需要AuthenticationEntryPoint

      • 设定AuthenticationEntryPoint
        .logout(httpSecurityLogoutConfigurer ->
                 httpSecurityLogoutConfigurer.logoutUrl("/logout")
                         .clearAuthentication(true)
                         .invalidateHttpSession(true)
        //                                .logoutSuccessUrl("/loginPage"))
                         .logoutSuccessHandler(new MyLogoutHandler()))
        .exceptionHandling(exceptionHandling ->
                 exceptionHandling
                         .accessDeniedHandler(new CustomizeAccessDeniedHandler())
                         .authenticationEntryPoint(new RestAuthenticationEntryPoint()))
        
      • 定义AuthenticationEntryPoint
            private static class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
                @Override
                public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                    response.setContentType("application/json");
        
                    String body = "{\"error\":\"Not Authenticated\"}";
                    OutputStream out = response.getOutputStream();
                    out.write(body.getBytes());
                    out.flush();
                }
            }
        
    • 使用postman模拟前端进行login在这里插入图片描述

    • 模拟前端调用/logout进行logout在这里插入图片描述

    • 模拟前端调用/db/hello进行没有httpSession的访问,期待返回authenciationErrorJSON应答。
      在这里插入图片描述

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

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

相关文章

WPF学习(4) -- 数据模板

一、DataTemplate 在WPF&#xff08;Windows Presentation Foundation&#xff09;中&#xff0c;DataTemplate 用于定义数据的可视化呈现方式。它允许你自定义如何展示数据对象&#xff0c;从而实现更灵活和丰富的用户界面。DataTemplate 通常用于控件&#xff08;如ListBox、…

pytorch中一些最基本函数和类

1.Tensor操作 Tensor是PyTorch中最基本的数据结构&#xff0c;类似于NumPy的数组&#xff0c;但可以在GPU上运行加速计算。 示例&#xff1a;创建和操作Tensor import torch# 创建一个零填充的Tensor x torch.zeros(3, 3) print(x)# 加法操作 y torch.ones(3, 3) z x y pr…

C++进阶(while循环——函数应用)

知识点代码框架总结 输入n组数据 &#xff0c;对n组数据里面的每一组进行处理&#xff08;输出、求和 、运算、其他&#xff09; int n;//几组数据cin >> n;//2while(n--){//对每组数据进行处理}看到下面的样例&#xff0c;肌肉型反映出上面的框架//2// 1 2 3// 4 5 6若…

Golang | Leetcode Golang题解之第233题数字1的个数

题目&#xff1a; 题解&#xff1a; func countDigitOne(n int) (ans int) {// mulk 表示 10^k// 在下面的代码中&#xff0c;可以发现 k 并没有被直接使用到&#xff08;都是使用 10^k&#xff09;// 但为了让代码看起来更加直观&#xff0c;这里保留了 kfor k, mulk : 0, 1;…

二叉搜索树大冒险:寻找-插入-删除

OK&#xff0c;看我们题目就可知道啦&#xff0c;今天要分享学习的一种数据结构就是二叉搜索树。 内容题目也说了三个大概的&#xff0c;分别是寻找、插入、删除。 讲这个之前呢&#xff0c;那么就先讲讲这个二叉搜索树是何方神圣呢&#xff1f; 二叉搜索树&#xff1a; 又…

移动端 火星坐标体系、百度坐标体系和全球坐标体系,该如何选择?

项目场景&#xff1a; 在梳理项目代码时&#xff0c;看到代码中的WGS-84&#xff0c;忽然想起有次面试问我这个问题&#xff0c;今天就好好的梳理下这个问题。 问题描述 移动端获取定位一般用什么编码&#xff1f;为什么要用这个&#xff1f; 原因分析&#xff1a; 解决方案&…

J025_斗地主游戏案例开发(简版)

一、需求描述 完成斗地主游戏的案例开发。 业务&#xff1a;总共有54张牌&#xff1b; 点数&#xff1a;3、4、5、6、7、8、9、10、J、Q、K、A、2 花色&#xff1a;黑桃、红桃、方片、梅花 大小王&#xff1a;大王、小王 点数分别要组合4种花色&#xff0c;大小王各一张。…

LeetCode --- 134双周赛

题目 3206. 交替组 I 3207. 与敌人战斗后的最大分数 3208. 交替组 II 3209. 子数组按位与值为 K 的数目 一、交替组 I & II 题目中问环形数组中交替组的长度为3的子数组个数&#xff0c;主要的问题在于它是环形的&#xff0c;我们要考虑首尾相接的情况&#xff0c;如何…

【流媒体】 通过ffmpeg硬解码拉流RTSP并播放

简介 目前RTSP拉流是网络摄像头获取图片数据常用的方法&#xff0c;但通过CPU软解码的方式不仅延时高且十分占用资源&#xff0c;本文提供了一种从网络摄像头RTSP硬解码的拉流的方法&#xff0c;并且提供python代码以便从网络摄像头获取图片进行后续算法处理。 下载ffmpeg F…

回归求助 教程分享

大侠幸会&#xff0c;在下全网同名「算法金」 0 基础转 AI 上岸&#xff0c;多个算法赛 Top 「日更万日&#xff0c;让更多人享受智能乐趣」 今日 217/10000 抱个拳&#xff0c;送个礼 更多内容&#xff0c;见微*公号往期文章&#xff1a;通透&#xff01;&#xff01;十大回…

Python | Leetcode Python题解之第233题数字1的个数

题目&#xff1a; 题解&#xff1a; class Solution:def countDigitOne(self, n: int) -> int:# mulk 表示 10^k# 在下面的代码中&#xff0c;可以发现 k 并没有被直接使用到&#xff08;都是使用 10^k&#xff09;# 但为了让代码看起来更加直观&#xff0c;这里保留了 kk,…

计算机网络——网络层(IP地址与MAC地址、地址解析协议ARP、IP数据报格式以及转发分组、ICMP、IPV6)

IP地址与MAC地址 由于MAC地址已固化在网卡上的ROM 中&#xff0c;因此常常将 MAC地址称为硬件地址或物理地址&#xff1b;物理地址的反义词就是虚拟地址、软件地址或逻辑地址&#xff0c;IP地址就属于这类地址。 从层次的角度看&#xff0c;MAC地址是数据链路层使用的地址&…

基于lstm的股票Volume预测

LSTM&#xff08;Long Short-Term Memory&#xff09;神经网络模型是一种特殊的循环神经网络&#xff08;RNN&#xff09;&#xff0c;它在处理长期依赖关系方面表现出色&#xff0c;尤其适用于时间序列预测、自然语言处理&#xff08;NLP&#xff09;和语音识别等领域。以下是…

特殊记忆柱群、特殊感觉中枢、强度中枢

智能软件的某些思维状态的标志&#xff0c;能够被一般感觉中枢“先天”感知&#xff0c;或者是与一般感觉中枢“后天”建立记忆联系。在它们建立奖惩记忆联系后&#xff0c;这些思维状态能够兴奋特殊感觉中枢或者一般感觉中枢对应的记忆柱群&#xff0c;也能够被相应感觉中枢的…

【大模型书籍】复旦新出!大规模语言模型:从理论到实践(推荐)

自2018年以来&#xff0c;包含Google、OpenAI、Meta、百度、华为等公司和研究机构都纷纷发布了包括BERT&#xff0c; GPT等在内多种模型&#xff0c;并在几乎所有自然语言处理任务中都表现出色。 今天给大家推荐一本大模型方面的书籍<大规模语言模型&#xff1a;从理论到实…

【数学建模】——力学模型建立的基本理论及方法

目录 一、基本理论 1. 牛顿力学 1.1 牛顿第一定律&#xff08;惯性定律&#xff09; 1.2 牛顿第二定律&#xff08;动力学定律&#xff09; 1.3 牛顿第三定律&#xff08;作用反作用定律&#xff09; 2. 能量守恒定律 2.1 动能和势能 2.2 能量守恒 3. 动量守恒定律…

简易秒表的实现

目录 描述 输入描述&#xff1a; 输出描述&#xff1a; 参考代码 描述 请编写一个模块&#xff0c;实现简易秒表的功能&#xff1a;具有两个输出&#xff0c;当输出端口second从1-60循环计数&#xff0c;每当second计数到60&#xff0c;输出端口minute加一&#xff0c;一直…

java配置nginx网络安全,防止国外ip访问,自动添加黑名单,需手动重新加载nginx

通过访问日志自动添加国外ip黑名单 创建一个类&#xff0c;自己添加一个main启动类即可测试 import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.json.JSONArray; import org.json.JSONObject; import org.sp…

Azcopy Sync同步Azure文件共享

Azcopy Sync同步Azure文件共享 一、工作原理二、安装 AzCopy在 Windows 上在 Linux 上 三、资源准备1. 创建源和目标 Azure 存储账户2. 创建源和目标文件共享3. 确定路径4. 生成源和目的存储账户的共享访问签名&#xff08;SAS&#xff09;令牌配置权限示例生成的 URL 四、Azco…

Java小白入门到实战应用教程-开发环境搭建-IDEA2024安装激huo详细教程

writer:eleven 安装IDEA2024 一、下载IDEA 推荐大家去官网下载 我这里也给大家直接准备了安装包&#xff0c;和激huo教程&#xff0c;大家可以自行下载使用。 注意&#xff1a;激huo教程只用于学习交流&#xff0c;不可商用。 IDEA2024安装包及激huo教程 说明&#xff1a…