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 国产高清在线精品一区在线,日韩毛片大全,亚洲精品久久久久电影

          整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

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

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

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

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

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

          一、功能需求

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

          二、非功能性需求

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

          這些需求往往不容易優(yōu)先想到,需要仔細(xì)深入場景、設(shè)身處地的考慮才能很好地構(gòu)思出來。

          1、界面測試(UI Test):

          1. 布局是否美觀、合理

          2. 控件是否美觀、對齊

          3. 界面設(shè)計風(fēng)格是否統(tǒng)一

          4. 界面文字無錯別字

          2、安全性測試

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

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

          2. 用戶名和密碼是否加密后,發(fā)給web服務(wù)器

          3. 用戶名和密碼的驗證,應(yīng)該通過服務(wù)器驗證,而不單單在客戶端用javascript驗證

          4. 登錄成功后生成的cookie,是否是httponly(否則容易被腳本盜?。?/span>

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

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

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

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

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

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

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

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

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

          3、 性能壓力測試

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

          1. 單用戶登錄的響應(yīng)時間是否小于 3 秒

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

          3. 高并發(fā)場景下用戶登錄的響應(yīng)時間是否小于 5 秒

          4. 高并發(fā)場景下服務(wù)端的監(jiān)控指標(biāo)是否符合預(yù)期

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

          6. 長時間大量用戶連續(xù)登錄和登出,服務(wù)器端是否存在內(nèi)存泄漏

          4、兼容性測試

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

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

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

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

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

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

          5、弱網(wǎng)測試

          1、網(wǎng)絡(luò)延遲或者弱網(wǎng)或者切換網(wǎng)絡(luò)、斷網(wǎng)是否登錄正確

          2、未激活或者凍結(jié)的用戶登陸

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

          4、密碼強弱進(jìn)行校驗

          5、有沒有對登陸設(shè)備和地區(qū)進(jìn)行檢測

          三、測試的不可窮盡性

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

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

          載自: java大師
          博客系統(tǒng)訪問:

          登錄功能

          1、前端頁面

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

          <!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>版權(quán)所有 ? 2022 <a href="#" target="_blank">濟(jì)南高新開發(fā)區(qū)微本地軟件開發(fā)工作室</a> 魯ICP備20002306號-1</p>-->
          <!--    </div>-->
          </div>
          <div th:replace="common/script::footer"></div>
          <script th:inline="javascript">
              layui.config({
                  base: '/static/layuiadmin/' //靜態(tài)資源所在路徑
              }).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
                      });
                      // 禁止重復(fù)點擊按鈕
                      $('.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();
                                  // 關(guān)閉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過期跳轉(zhuǎn)到登錄頁并跳出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 提供的認(rèn)證過濾器鏈負(fù)責(zé)的。在Spring Security中,主要的認(rèn)證過程是由UsernamePasswordAuthenticationFilter來完成的。

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

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

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

          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()
                          // 剩下的所有的地址都是需要在認(rèn)證狀態(tài)下才可以訪問
                          .anyRequest().authenticated()
                          .and()
                          // 過濾登錄驗證碼
                          .addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
                          // 配置登錄功能
                          .formLogin()
                          .usernameParameter("userName")
                          .passwordParameter("passWord")
                          // 指定指定要的登錄頁面
                          .loginPage("/loginPage")
                          // 處理認(rèn)證路徑的請求
                          .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屬性設(shè)置為SAMEORIGIN
                          .headers().frameOptions().sameOrigin()
                          .and()
                          // 配置session管理
                          .sessionManagement()
                          //session失效默認(rèn)的跳轉(zhuǎn)地址
                          .invalidSessionUrl("/loginPage");
              }
          }
          

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

          創(chuàng)建監(jiān)聽器,在登錄成功的時候記錄登錄日志。

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

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

          @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、登錄失敗監(jiān)聽器(記錄登錄日志)

          創(chuàng)建監(jiān)聽器,在登錄失敗的時候記錄異常登錄日志。

          @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) {
                      //提供的憑據(jù)是錯誤的,用戶名或者密碼錯誤
                      message="提供的憑據(jù)是錯誤的,用戶名或者密碼錯誤";
                  } else if (abstractAuthenticationFailureEvent instanceof AuthenticationFailureCredentialsExpiredEvent) {
                      //驗證通過,但是密碼過期
                      message="驗證通過,但是密碼過期";
                  } else if (abstractAuthenticationFailureEvent instanceof AuthenticationFailureDisabledEvent) {
                      //驗證過了但是賬戶被禁用
                      message="驗證過了但是賬戶被禁用";
                  } else if (abstractAuthenticationFailureEvent instanceof AuthenticationFailureExpiredEvent) {
                      //驗證通過了,但是賬號已經(jīng)過期
                      message="驗證通過了,但是賬號已經(jīng)過期";
                  } 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中內(nèi)部發(fā)生的異常都會被封裝成此類
                      message="內(nèi)部發(fā)生的異常";
                  } 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、認(rèn)證成功處理器

          下面是一個認(rèn)證成功處理器,登錄成功后,會返回響應(yīng)的信息給前端

          @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、認(rèn)證失敗處理器

          下面是一個認(rèn)證成功處理器,登錄成功后,會返回響應(yīng)的信息給前端

          @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,就代表成功,跳轉(zhuǎn)到/請求,進(jìn)去index或者main頁面

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

          總結(jié)

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

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

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

          解Spring Security的formLogin登錄認(rèn)證模式

          一、formLogin的應(yīng)用場景

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

          準(zhǔn)備工作

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

          需求

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

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

          二、說明

          formLogin模式的三要素:

          • 登錄驗證邏輯
          • 資源訪問控制規(guī)則,如:資源權(quán)限、角色權(quán)限
          • 用戶信息

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

          三、實現(xiàn)formLogin模式基礎(chǔ)配置

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

          實現(xiàn)formLogin模式基礎(chǔ)配置(點擊可放大)

          上面的代碼分為兩部分:

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

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

          四、實現(xiàn)資源訪問限制的需求

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

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

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

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

          五、靜態(tài)資源訪問

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

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


          主站蜘蛛池模板: 丰满人妻一区二区三区视频| 国产精品一区二区久久国产| 亚洲福利一区二区精品秒拍| 亚洲午夜精品第一区二区8050| 日本无卡码一区二区三区| 精品国产一区二区三区久久蜜臀 | 精彩视频一区二区| 中字幕一区二区三区乱码| 福利一区二区三区视频在线观看| 国产一区二区视频免费| 天堂va视频一区二区| 精品国产福利一区二区| 亚洲区精品久久一区二区三区 | 国产成人一区二区三区高清 | 国产精品小黄鸭一区二区三区| 国产精品一区二区久久乐下载 | 精品人妻少妇一区二区| 午夜福利国产一区二区| 色视频综合无码一区二区三区 | 免费国产在线精品一区| 日本在线视频一区| 午夜无码一区二区三区在线观看| 福利一区国产原创多挂探花| 女人和拘做受全程看视频日本综合a一区二区视频 | 精品欧洲av无码一区二区三区| 国产AV一区二区三区无码野战| 亚洲AV日韩AV天堂一区二区三区 | 国产成人无码精品一区不卡| 好吊妞视频一区二区| 一区二区高清在线观看| 久久国产精品免费一区| 骚片AV蜜桃精品一区| 蜜桃传媒一区二区亚洲AV| 午夜性色一区二区三区不卡视频 | 一区二区三区国产精品| 精品无码一区二区三区爱欲九九| 一区二区三区四区精品视频 | 久久人妻av一区二区软件| 中文字幕一区二区三区精彩视频| 天堂一区人妻无码| 精品91一区二区三区|