Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 无码专区aaaaaa免费视频,欧美亚洲黄色,日韩去日本高清在线

          整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          “用戶登錄”測試你真的會嗎?

          “用戶登錄”測試你真的會嗎?

          后,進入金三銀四招聘旺季,相信不少參加測試相關崗位面試的時候,會遇到設計測試的題目。用戶登錄測試屬于老生常談的問題了,如果面試時能先概述一下你的設計思路、考慮的維度,再開始逐個寫出用例的設計,應該會更有條理。

          給你一個“用戶登錄”功能你會如何測試它?

          設計測試用例,通常主要有 2 個大類:功能性需求和非功能性需求。

          一、功能需求

          功能需求其實就是軟件本身需要實現的具體功能,通常它們是這個功能的最直接體現。

          在設計這種用例的時候,我們基本會用【等價類】和【邊界值】這兩種方法。

          1. 用戶名和密碼都為空,驗證是否登錄失敗,并提示信息(“用戶名為空”)

          2. 用戶名為空或密碼為空,驗證是否登錄失敗,并提示信息(“用戶名/密碼為空”)

          3. 輸入已注冊的用戶名和正確的密碼,驗證是否登錄成功

          4. 登錄成功后,鏈接能否跳轉至正確的頁面

          5. 輸入已注冊的用戶名和錯誤的密碼,驗證是否登錄失敗,并提示信息(“密碼錯誤”)

          6. 輸入未注冊的用戶名和隨意密碼,驗證是否登錄失敗,并提示信息(“用戶名錯誤”)

          7. 如果登錄功能啟用驗證碼功能,在用戶名和密碼正確的前提下,輸入正確的驗證碼,驗證是否登錄成功

          8. 如果登錄功能啟用驗證碼功能,在用戶名和密碼正確的前提下,輸入錯誤的驗證碼,驗證是否登錄失敗,并提示信息正確(“驗證碼錯誤”)

          9. 如果登錄功能啟用驗證碼功能,考慮驗證碼的辨認難易程度,是否可以點擊驗證碼圖片(“換一個”),更換驗證碼

          10. 記住用戶名和密碼的功能是否正確,且登錄失敗后不記錄密碼

          11. 必填項為空時,功能是否正確

          不合法的用戶名:空白的用戶名,不正確的用戶名,使用了字符大于用戶名的限制;正常用戶名不允許的特殊字符;系統的保留字段;

          不合法的密碼:空密碼;錯誤的密碼;字符大于密碼的限制;正常密碼不允許的特殊字符;系統的保留字段。

          二、非功能性需求

          很多時候,僅做了功能性需求的測試覆蓋還是不夠的,因為還存在一些其他"隱藏"的需求,比如:界面UI、安全性、性能、兼容性。這些往往是決定軟件質量的關鍵因素。

          這些需求往往不容易優先想到,需要仔細深入場景、設身處地的考慮才能很好地構思出來。

          1、界面測試(UI Test):

          1. 布局是否美觀、合理

          2. 控件是否美觀、對齊

          3. 界面設計風格是否統一

          4. 界面文字無錯別字

          2、安全性測試

          考慮登錄的安全性,可能需要用到以下測試用例:

          1. 用戶名和密碼的后臺存儲是否加密

          2. 用戶名和密碼是否加密后,發給web服務器

          3. 用戶名和密碼的驗證,應該通過服務器驗證,而不單單在客戶端用javascript驗證

          4. 登錄成功后生成的cookie,是否是httponly(否則容易被腳本盜取)

          5. 密碼框內輸入的密碼是否可以在頁面源碼模式下被查看

          6. 密碼是否具有有效期,有效期到期后,是否提示修改密碼

          7. 未登錄的情況下,在瀏覽器中直接輸入登錄后的URL地址,是否會重定向到登錄界面

          8. 用戶名和密碼輸入框中分別輸入典型的“SQL注入攻擊”字符串,驗證系統的返回頁面(防止SQL注入攻擊)

          9. 用戶名和密碼輸入框中分別輸入典型的“XSS跨站腳本攻擊”字符串,驗證系統行為是否被篡改(防止XSS攻擊)

          10. 連續多次登錄失敗后,系統是否會阻止后續的嘗試,錯誤登錄的次數限制,以應對暴力破解(防止暴力破解)

          11. 考慮很多用戶在同一終端上的登陸

          12. 考慮同一用戶在不同終端登錄的互斥性

          13. 同一用戶在同一終端的多種瀏覽器上登錄,驗證登錄功能的互斥性

          3、 性能壓力測試

          考慮到性能,可能還需要增加以下的測試用例:

          1. 單用戶登錄的響應時間是否小于 3 秒

          2. 單用戶登錄時,后臺請求數量是否過多

          3. 高并發場景下用戶登錄的響應時間是否小于 5 秒

          4. 高并發場景下服務端的監控指標是否符合預期

          5. 高集合點并發場景下,是否存在資源死鎖和不合理的資源等待

          6. 長時間大量用戶連續登錄和登出,服務器端是否存在內存泄漏

          4、兼容性測試

          1. 不同瀏覽器下,驗證登錄頁面的顯示以及功能正確性

          2. 相同瀏覽器的不同版本下,驗證登錄頁面的顯示以及功能正確性

          3. 不同移動終端的不同瀏覽器下,驗證登錄頁面的顯示以及功能正確性

          4. 不同分辨率的界面下,驗證登錄頁面的顯示以及功能正確性

          5. 不同平臺(Windows、Mac)、不同移動設備(iPhone、Andriod),登錄界面的顯示及功能是否正確

          6. 不同語言環境下,登錄界面的顯示及功能是否正確 (本地化測試 Localization Test)

          5、弱網測試

          1、網絡延遲或者弱網或者切換網絡、斷網是否登錄正確

          2、未激活或者凍結的用戶登陸

          3、登錄的日志是否記錄正確

          4、密碼強弱進行校驗

          5、有沒有對登陸設備和地區進行檢測

          三、測試的不可窮盡性

          通常在實際工作中,測試由于受限于時間成本和經濟成本,是不可能去窮盡所有可能的組合的,而是采用基于風險驅動的模式,有所側重地選擇測試范圍和設計測試用例,以尋求缺陷風險和研發成本之間的平衡。

          對于高質量的軟件測試,用例設計不僅需要考慮明確的功能性需求,還要涉及兼容性、安全性和性能等一系列的非功能性需求,這些非功能性需求對軟件系統的質量有著舉足輕重的作用。

          載自: java大師
          博客系統訪問:

          登錄功能

          1、前端頁面

          采用的是layui-admin框架,文中的驗證碼內容,請參考作者之前的驗證碼功能

          <!DOCTYPE html>
          <html lang="zh" xmlns:th="http://www.thymeleaf.org">
          <head>
              <title>ds博客</title>
              <div th:replace="common/link::header"></div>
              <link rel="stylesheet" th:href="@{/static/layuiadmin/style/login.css}" media="all">
          </head>
          <body>
          <div class="layadmin-user-login layadmin-user-display-show" id="LAY-user-login" style="display: none;">
              <div class="layadmin-user-login-main">
                  <div class="layadmin-user-login-box layadmin-user-login-header">
                      <h2>ds博客</h2>
                      <p>后臺登錄</p>
                  </div>
                  <div class="layadmin-user-login-box layadmin-user-login-body layui-form">
                      <div class="layui-form-item">
                          <label class="layadmin-user-login-icon layui-icon layui-icon-username" for="LAY-user-login-username"></label>
                          <input type="text" name="userName" value="test" id="LAY-user-login-username" lay-verify="required" placeholder="用戶名" class="layui-input">
                      </div>
                      <div class="layui-form-item">
                          <label class="layadmin-user-login-icon layui-icon layui-icon-password" for="LAY-user-login-password"></label>
                          <input type="password" name="passWord" value="test" id="LAY-user-login-password" lay-verify="required" placeholder="密碼" class="layui-input">
                      </div>
                      <div class="layui-form-item">
                          <div class="layui-row">
                              <div class="layui-col-xs7">
                                  <label class="layadmin-user-login-icon layui-icon layui-icon-vercode"></label>
                                  <input type="text" name="code"  lay-verify="required" placeholder="圖形驗證碼" class="layui-input">
                              </div>
                              <div class="layui-col-xs5">
                                  <div style="margin-left: 10px;">
                                      <img id="codeImg" class="layadmin-user-login-codeimg">
                                  </div>
                              </div>
                          </div>
                      </div>
                      <div class="layui-form-item" style="margin-bottom: 20px;">
                          <input type="checkbox" name="remember-me" lay-skin="primary" title="記住密碼">
                      </div>
                      <div class="layui-form-item">
                          <button class="layui-btn layui-btn-fluid layui-bg-blue"  lay-submit lay-filter="login">登 錄</button>
                      </div>
                  </div>
              </div>
          
          <!--    <div class="layui-trans layadmin-user-login-footer">-->
          <!--        <p>版權所有 ? 2022 <a href="#" target="_blank">濟南高新開發區微本地軟件開發工作室</a> 魯ICP備20002306號-1</p>-->
          <!--    </div>-->
          </div>
          <div th:replace="common/script::footer"></div>
          <script th:inline="javascript">
              layui.config({
                  base: '/static/layuiadmin/' //靜態資源所在路徑
              }).extend({
                  index: 'lib/index' //主入口模塊
              }).use(['index', 'user'], function(){
                  let $=layui.$,
                      form=layui.form;
                  // 初始化
                  getImgCode();
                  form.render();
                  //提交
                  form.on('submit(login)', function(obj) {
                      // 打開loading
                      let loading=layer.load(0, {
                          shade: false,
                          time: 2 * 1000
                      });
                      // 禁止重復點擊按鈕
                      $('.layui-btn').attr("disabled",true);
                      //請求登入接口
                      $.ajax({
                          type: 'POST',
                          url:  ctx + '/login',
                          data: obj.field,
                          dataType: 'json',
                          success: function(result) {
                              if (result.code===200) {
                                  layer.msg('登入成功', {
                                       icon: 1
                                      ,time: 1000
                                  }, function(){
                                      window.location.href='/';
                                  });
                              } else {
                                  layer.msg(result.message);
                                  // 刷新驗證碼
                                  getImgCode();
                                  // 關閉loading
                                  layer.close(loading);
                                  // 開啟點擊事件
                                  $('.layui-btn').attr("disabled", false);
                              }
                          }
                      });
                  });
                  $("#codeImg").on('click', function() {
                      // 添加驗證碼
                      getImgCode();
                  });
                  $(document).keydown(function (e) {
                      if (e.keyCode===13) {
                          $('.layui-btn').click();
                      }
                  });
                  // 解決session過期跳轉到登錄頁并跳出iframe框架
                  $(document).ready(function () {
                      if (window !=top) {
                          top.location.href=location.href;
                      }
                  });
              });
              /**
               * 獲取驗證碼
               */
              function getImgCode() {
                  let url=ctx + '/getImgCode';
                  let xhr=new XMLHttpRequest();
                  xhr.open('GET', url, true);
                  xhr.responseType="blob";
                  xhr.onload=function() {
                      if (this.status===200) {
                          let blob=this.response;
                          document.getElementById("codeImg").src=window.URL.createObjectURL(blob);
                      }
                  }
                  xhr.send();
              }
          </script>
          </body>
          </html>
          

          2、后端處理/login請求

          通過springsecurity的.loginProcessingUrl(“/login”)處理,處理邏輯如下:

          .loginProcessingUrl("/login") 用于指定處理登錄操作的URL地址,而具體的驗證邏輯是由 Spring Security 提供的認證過濾器鏈負責的。在Spring Security中,主要的認證過程是由UsernamePasswordAuthenticationFilter來完成的。

          當用戶提交登錄表單,請求到達.loginProcessingUrl("/login")配置的URL時,UsernamePasswordAuthenticationFilter會攔截這個請求,然后進行以下主要步驟:

          1. 獲取用戶名和密碼:從請求中獲取用戶輸入的用戶名和密碼。
          2. 創建認證令牌:使用獲取到的用戶名和密碼創建一個認證令牌(UsernamePasswordAuthenticationToken)。
          3. 將認證令牌傳遞給認證管理器:將認證令牌傳遞給配置的認證管理器(AuthenticationManager)進行認證。
          4. 執行認證邏輯:認證管理器會調用已配置的身份驗證提供者(AuthenticationProvider)來執行實際的身份驗證邏輯。通常,會使用用戶提供的用戶名和密碼與系統中存儲的用戶信息進行比對。
          5. 處理認證結果:認證提供者返回認證結果,如果認證成功,則將認證令牌標記為已認證,并設置用戶權限等信息。如果認證失敗,會拋出相應的異常。
          6. 處理認證成功或失敗:根據認證的結果,UsernamePasswordAuthenticationFilter將請求重定向到成功或失敗的處理器,執行相應的操作,比如跳轉頁面或返回錯誤信息。

          這個整個過程是由 Spring Security 提供的默認配置完成的,通常情況下,開發者只需要配置好認證管理器、用戶信息服務(UserDetailsService),以及成功和失敗的處理器,Spring Security 就會負責處理登錄驗證的整個流程。

          package com.ds.core.config;
          
          import com.ds.blog.system.service.SysUserService;
          import com.ds.core.security.CustomAccessDeniedHandler;
          import com.ds.core.security.DefaultAuthenticationFailureHandler;
          import com.ds.core.security.DefaultAuthenticationSuccessHandler;
          import com.ds.core.security.filter.ValidateCodeFilter;
          import net.bytebuddy.asm.Advice;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.context.annotation.Bean;
          import org.springframework.context.annotation.Configuration;
          import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
          import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
          import org.springframework.security.config.annotation.web.builders.HttpSecurity;
          import org.springframework.security.config.annotation.web.builders.WebSecurity;
          import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
          import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
          import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
          import org.springframework.security.web.access.AccessDeniedHandler;
          import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
          
          @Configuration
          @EnableWebSecurity
          @EnableGlobalMethodSecurity(prePostEnabled=true)
          public class MySecurityConfig extends WebSecurityConfigurerAdapter {
          
              @Override
              protected void configure(HttpSecurity http) throws Exception {
                  http.authorizeRequests()
                          // 放過
                          .antMatchers("/loginPage", "/getImgCode").permitAll()
                          .antMatchers("/**/*.jpg", "/**/*.png", "/**/*.gif", "/**/*.jpeg").permitAll()
                          // 剩下的所有的地址都是需要在認證狀態下才可以訪問
                          .anyRequest().authenticated()
                          .and()
                          // 過濾登錄驗證碼
                          .addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
                          // 配置登錄功能
                          .formLogin()
                          .usernameParameter("userName")
                          .passwordParameter("passWord")
                          // 指定指定要的登錄頁面
                          .loginPage("/loginPage")
                          // 處理認證路徑的請求
                          .loginProcessingUrl("/login")
                          .successHandler(defaultAuthenticationSuccessHandler)
                          .failureHandler(defaultAuthenticationFailureHandler)
                          .and()
                          .exceptionHandling()
                          .accessDeniedHandler(accessDeniedHandler)
                          .and()
                          // 登出
                          .logout()
                          .invalidateHttpSession(true)
                          .deleteCookies("remember-me")
                          .logoutUrl("/logout")
                          .logoutSuccessUrl("/loginPage")
                          .and()
                          .rememberMe()
                          // 有效期7天
                          .tokenValiditySeconds(3600 * 24 * 7)
                          // 開啟記住我功能
                          .rememberMeParameter("remember-me")
                          .and()
                          //禁用csrf
                          .csrf().disable()
                          // header response的X-Frame-Options屬性設置為SAMEORIGIN
                          .headers().frameOptions().sameOrigin()
                          .and()
                          // 配置session管理
                          .sessionManagement()
                          //session失效默認的跳轉地址
                          .invalidSessionUrl("/loginPage");
              }
          }
          

          3、登錄成功監聽器(記錄登錄日志)

          創建監聽器,在登錄成功的時候記錄登錄日志。

          1. @Slf4j
          2. @Slf4j 是 Lombok 提供的注解,用于自動生成日志對象,這里是為了方便使用日志。
          3. @Component
          4. @Component 注解將類標識為一個 Spring 組件,使得 Spring 能夠自動掃描并將其納入容器管理。
          5. AuthenticationSuccessListener 實現 ApplicationListener 接口
          6. AuthenticationSuccessListener 類實現了 ApplicationListener<AuthenticationSuccessEvent> 接口,表明它是一個事件監聽器,監聽的是用戶認證成功的事件。
          7. SysLoginLogService 注入
          8. SysLoginLogService 是一個服務類,通過 @Autowired 注解注入到當前類中。該服務類用于對登錄日志的持久化操作。
          9. onApplicationEvent 方法
          10. onApplicationEvent 方法是實現 ApplicationListener 接口的回調方法,在用戶認證成功的時候會被觸發。
          11. 通過 authenticationSuccessEvent.getAuthentication().getPrincipal() 獲取登錄的用戶信息,這里假設用戶信息是 User 類型。
          12. 通過 ServletUtil.getClientIP 獲取客戶端的IP地址,這里使用了 ServletUtil 工具類,可以通過請求上下文獲取IP地址。
          13. 創建一個 SysLoginLog 對象,將登錄成功的相關信息設置進去,包括賬號、登錄IP、備注等。
          14. 調用 sysLoginLogService.save(sysLoginLog) 將登錄日志持久化存儲。

          總的來說,這段代碼的作用是在用戶成功登錄后,通過監聽 Spring Security 的認證成功事件,記錄用戶的登錄日志信息,包括登錄賬號、登錄IP和登錄成功的備注。這樣可以實現登錄成功后的自定義操作,例如記錄登錄日志等。

          @Slf4j
          @Component
          public class AuthenticationSuccessListener implements ApplicationListener<AuthenticationSuccessEvent> {
              @Autowired
              private SysLoginLogService sysLoginLogService;
          
              @Override
              public void onApplicationEvent(AuthenticationSuccessEvent authenticationSuccessEvent) {
                  // 登錄賬號
                  User user=(User) authenticationSuccessEvent.getAuthentication().getPrincipal();
                  // 請求IP
                  String ip=ServletUtil.getClientIP(((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(), "");
                  SysLoginLog sysLoginLog=new SysLoginLog();
                  sysLoginLog.setAccount(user.getUsername());
                  sysLoginLog.setLoginIp(ip);
                  sysLoginLog.setRemark("登錄成功");
                  sysLoginLogService.save(sysLoginLog);
              }
          }
          

          4、登錄失敗監聽器(記錄登錄日志)

          創建監聽器,在登錄失敗的時候記錄異常登錄日志。

          @Slf4j
          @Component
          public class AuthenticationFailureListener implements ApplicationListener<AbstractAuthenticationFailureEvent>  {
          
              @Autowired
              private SysLoginLogService sysLoginLogService;
          
              @Override
              public void onApplicationEvent(AbstractAuthenticationFailureEvent abstractAuthenticationFailureEvent) {
                  // 登錄賬號
                  String username=abstractAuthenticationFailureEvent.getAuthentication().getPrincipal().toString();
                  // 登錄失敗原因
                  String message ;
                  if (abstractAuthenticationFailureEvent instanceof AuthenticationFailureBadCredentialsEvent) {
                      //提供的憑據是錯誤的,用戶名或者密碼錯誤
                      message="提供的憑據是錯誤的,用戶名或者密碼錯誤";
                  } else if (abstractAuthenticationFailureEvent instanceof AuthenticationFailureCredentialsExpiredEvent) {
                      //驗證通過,但是密碼過期
                      message="驗證通過,但是密碼過期";
                  } else if (abstractAuthenticationFailureEvent instanceof AuthenticationFailureDisabledEvent) {
                      //驗證過了但是賬戶被禁用
                      message="驗證過了但是賬戶被禁用";
                  } else if (abstractAuthenticationFailureEvent instanceof AuthenticationFailureExpiredEvent) {
                      //驗證通過了,但是賬號已經過期
                      message="驗證通過了,但是賬號已經過期";
                  } else if (abstractAuthenticationFailureEvent instanceof AuthenticationFailureLockedEvent) {
                      //賬戶被鎖定
                      message="賬戶被鎖定";
                  } else if (abstractAuthenticationFailureEvent instanceof AuthenticationFailureProviderNotFoundEvent) {
                      //配置錯誤,沒有合適的AuthenticationProvider來處理登錄驗證
                      message="配置錯誤";
                  } else if (abstractAuthenticationFailureEvent instanceof AuthenticationFailureProxyUntrustedEvent) {
                      // 代理不受信任,用于Oauth、CAS這類三方驗證的情形,多屬于配置錯誤
                      message="代理不受信任";
                  } else if (abstractAuthenticationFailureEvent instanceof AuthenticationFailureServiceExceptionEvent) {
                      // 其他任何在AuthenticationManager中內部發生的異常都會被封裝成此類
                      message="內部發生的異常";
                  } else {
                      message="其他未知錯誤";
                  }
                  // 請求IP
                  String ip=ServletUtil.getClientIP(((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(), "");
                  SysLoginLog sysLoginLog=new SysLoginLog();
                  sysLoginLog.setAccount(username);
                  sysLoginLog.setLoginIp(ip);
                  sysLoginLog.setRemark(message);
                  sysLoginLogService.save(sysLoginLog);
              }
          }
          

          5、認證成功處理器

          下面是一個認證成功處理器,登錄成功后,會返回響應的信息給前端

          @Component
          @Slf4j
          public class DefaultAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
          
              @Override
              public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
                  log.info("-----login in success----");
                  response.setCharacterEncoding("utf-8");
                  response.setContentType("application/json;charset=utf-8");
                  PrintWriter writer=response.getWriter();
                  writer.write(JSON.toJSONString(Result.success()));
                  writer.flush();
              }
          }
          
          .successHandler(defaultAuthenticationSuccessHandler)
          .failureHandler(defaultAuthenticationFailureHandler)
          

          6、認證失敗處理器

          下面是一個認證成功處理器,登錄成功后,會返回響應的信息給前端

          @Component
          @Slf4j
          public class DefaultAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
              @Override
              public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                  log.info("login in failure : " +  exception.getMessage());
          
                  response.setContentType("application/json;charset=utf-8");
                  response.setCharacterEncoding("utf-8");
                  PrintWriter writer=response.getWriter();
                  String message;
                  if (exception instanceof BadCredentialsException) {
                      message="用戶名或密碼錯誤,請重試。";
                      writer.write(JSON.toJSONString(Result.failure(message)));
                  }else{
                      writer.write(JSON.toJSONString(Result.failure(exception.getMessage())));
                  }
                  writer.flush();
              }
          
          .successHandler(defaultAuthenticationSuccessHandler)
          .failureHandler(defaultAuthenticationFailureHandler)
          

          7、前端頁面

          返回200,就代表成功,跳轉到/請求,進去index或者main頁面

          if (result.code===200) {
              layer.msg('登入成功', {
                   icon: 1
                  ,time: 1000
              }, function(){
                  window.location.href='/';
              });
          } else {
          

          總結

          AuthenticationSuccessEvent 是 Spring Security 中用于表示用戶認證成功的事件。判斷登錄成功的主要依據是在認證過程中,用戶提供的憑據(通常是用戶名和密碼)與系統中存儲的憑據匹配。以下是判斷登錄成功的基本流程:

          1. 用戶提交登錄表單:用戶在瀏覽器中輸入用戶名和密碼,然后點擊登錄按鈕,提交登錄表單。
          2. Spring Security 攔截登錄請求:配置的 .loginProcessingUrl("/login") 指定了登錄請求的URL,Spring Security會攔截這個URL的請求。
          3. UsernamePasswordAuthenticationFilter處理登錄請求UsernamePasswordAuthenticationFilter 是 Spring Security 內置的過濾器之一,用于處理用戶名密碼登錄認證。當用戶提交登錄表單時,UsernamePasswordAuthenticationFilter會攔截該請求,嘗試進行身份驗證。
          4. AuthenticationManager執行身份驗證UsernamePasswordAuthenticationFilter將用戶名密碼等信息封裝成一個 UsernamePasswordAuthenticationToken。通過 AuthenticationManager 進行身份驗證,AuthenticationManager 是一個接口,實際的實現為 ProviderManagerProviderManager通過配置的 AuthenticationProvider 來執行實際的身份驗證邏輯。
          5. AuthenticationProvider處理身份驗證DaoAuthenticationProviderAuthenticationProvider 的默認實現之一,用于處理基于數據庫的身份驗證。DaoAuthenticationProvider會從配置的 UserDetailsService 中獲取用戶信息,然后與用戶提交的信息進行比對。
          6. 認證成功:如果認證成功,AuthenticationProvider 會返回一個已認證的 Authentication 對象。這個已認證的 Authentication 對象包含了用戶的信息,通常是 UserDetails 的實現。
          7. AuthenticationSuccessHandler處理認證成功:在配置中,通過 .successHandler() 方法指定了處理認證成功的 AuthenticationSuccessHandler。在這個處理器中,可以執行一些額外的邏輯,例如記錄登錄日志等。
          8. AuthenticationSuccessEvent被發布:在處理成功的階段,Spring Security 發布了 AuthenticationSuccessEvent 事件,表示認證成功。

          在上述流程中,認證成功的判斷主要是在 AuthenticationProvider 中完成的。DaoAuthenticationProvider 會檢查用戶提供的密碼與數據庫中存儲的密碼是否匹配。如果匹配,就認為認證成功。當認證成功后,后續的處理流程包括 AuthenticationSuccessHandler 的執行和 AuthenticationSuccessEvent 的發布。你可以通過監聽 AuthenticationSuccessEvent 事件來執行一些額外的自定義邏輯,例如記錄登錄日志。

          解Spring Security的formLogin登錄認證模式

          一、formLogin的應用場景

          在本專欄之前的文章中,已經給大家介紹過Spring Security的HttpBasic模式,該模式比較簡單,只是進行了通過攜帶Http的Header進行簡單的登錄驗證,而且沒有定制的登錄頁面,所以使用場景比較窄。
          對于一個完整的應用系統,與登錄驗證相關的頁面都是高度定制化的,非常美觀而且提供多種登錄方式。這就需要Spring Security支持我們自己定制登錄頁面,也就是本文給大家介紹的formLogin模式登錄認證模式。

          準備工作

          • 新建一個Spring Boot 的web應用
          • 準備一個login.html登錄頁面,頁面內容非常簡單,一個from表單、用戶名和密碼輸入框,一個提交按鈕
          • 準備一個首頁index.html,在登錄成功之后需要進入index.html首頁
          • 首頁可以看到syslog(日志管理)、sysuer(用戶管理)、biz1(業務一)、biz2(業務二)四個頁面超文本鏈接選項。通過controller控制層跳轉頁面,并在對應頁面寫一些標志性文字即可,不需寫具體業務。

          需求

          • 我們希望biz1(業務一)、biz2(業務二)普通的操作用戶user就可以訪問
          • 我們希望管理員可以訪問包括syslog(日志管理)和sysuser(用戶管理)在內的所有資源

          以上就是本文介紹formLogin模式需要進行的準備工作及需求,下面我們就來實現其中的核心的登錄驗證邏輯,準備工作非常簡單請自行實現。(新建spring boot應用,登錄頁面、首頁、四個業務頁面都寫成非常簡單的html即可,不用寫實際業務和樣式。)

          二、說明

          formLogin模式的三要素:

          • 登錄驗證邏輯
          • 資源訪問控制規則,如:資源權限、角色權限
          • 用戶信息

          一般來說,使用權限認證框架的的業務系統登錄驗證邏輯是固定的,而資源訪問控制規則和用戶信息是從數據庫或其他存儲介質靈活加載的。但本文所有的用戶、資源、權限信息都是代碼配置寫死的,旨在為大家介紹formLogin認證模式,如何從數據庫加載權限認證相關信息我還會結合RBAC權限模型再寫文章的。

          三、實現formLogin模式基礎配置

          首先,我們要繼承WebSecurityConfigurerAdapter ,重寫configure(HttpSecurity http) 方法,該方法用來配置登錄驗證邏輯。請注意看下文代碼中的注釋信息。

          實現formLogin模式基礎配置(點擊可放大)

          上面的代碼分為兩部分:

          • 第一部分是formLogin配置段,用于配置登錄驗證邏輯相關的信息。如:登錄頁面、登錄成功頁面、登錄請求處理路徑等。
          • 第二部分是authorizeRequests配置端,用于配置資源的訪問權限。如:開發登錄頁面的permitAll開放訪問,“/biz1”(業務一頁面資源)需要有資源ID為"biz1"的用戶才可以訪問。

          這時候,我們通過瀏覽器訪問,隨便測試一個沒有訪問權限的資源,都會跳轉到login.html頁面。

          四、實現資源訪問限制的需求

          在上文中,我們配置了登錄驗證及資源訪問的權限規則,我們還沒有具體的用戶,下面我們就來配置具體的用戶。重寫WebSecurityConfigurerAdapter的 configure(AuthenticationManagerBuilder auth)方法

          實現資源訪問限制的需求(點擊可放大)

          • inMemoryAuthentication指的是在內存里面存儲用戶的身份認證和授權信息。
          • withUser("user")用戶名是user
          • password(passwordEncoder().encode("123456"))密碼是加密之后的123456
          • authorities("biz1","biz2")指的是user用戶擁有資源ID為biz1(業務一)和biz2(業務二)資源的權限

          這樣,我們就實現了文首提出的普通用戶只能訪問biz1(業務一)和biz2(業務二)資源的需求。那么管理員用戶可以訪問所有的資源的配置方式,你會不會呢?同樣的配方、同樣的方式、自己可以嘗試一下哦!

          五、靜態資源訪問

          在我們的實際開發中,登錄頁面login.html和控制層Controller登錄驗證'/login'都必須無條件的開放。除此之外,一些靜態資源如css、js文件通常也都不需要驗證權限,我們需要將它們的訪問權限也開放出來。下面就是實現的方法:重寫WebSecurityConfigurerAdapter類的configure(WebSecurity web) 方法

          靜態資源訪問(點擊可放大)


          主站蜘蛛池模板: 久久无码精品一区二区三区| 日本一区高清视频| 一区二区三区在线免费观看视频| 熟女少妇丰满一区二区| 精品一区二区AV天堂| 鲁丝片一区二区三区免费| 精品3d动漫视频一区在线观看| 中文字幕精品亚洲无线码一区| 丰满爆乳无码一区二区三区| 国产精品视频免费一区二区三区| 一区二区精品在线观看| 无码少妇一区二区| 亚洲一区二区精品视频| 另类国产精品一区二区| 国产无码一区二区在线| 亚洲码欧美码一区二区三区| 人妻内射一区二区在线视频| 日产精品久久久一区二区| 国产精品无码一区二区三区在| 国产激情无码一区二区| 国产精品被窝福利一区| 无码精品人妻一区| 午夜一区二区在线观看| 亚洲国产激情一区二区三区| 中文字幕VA一区二区三区| 国产成人久久精品麻豆一区| 国产主播一区二区三区| 一区二区视频在线| 2020天堂中文字幕一区在线观 | 精品无码人妻一区二区三区不卡 | 无码人妻精品一区二区三| 高清一区二区三区免费视频| 中文字幕aⅴ人妻一区二区 | 精品不卡一区中文字幕| 精品国产日韩亚洲一区在线| 日韩亚洲AV无码一区二区不卡 | 性色AV一区二区三区| 无码视频一区二区三区在线观看 | 久久精品免费一区二区三区| 国内偷窥一区二区三区视频| 精品无码国产一区二区三区AV|