本文為霍格沃茲測試學院優秀學員測試開發學習筆記,進階學習文末加群。
登入功能對軟體測試工程師可能是最常見卻是最重要,也是最容易被忽視的測試場景。本文整理一些經驗豐富的測試工程師總結的測試用例,并結合 Java Spring Security 架構來簡單說下登入的測試方向思路和部分原理,供大家交流探讨。
登入測試方向
功能測試(基礎)
- 輸入已注冊的使用者名和正确的密碼,驗證是否登入成功;
- 輸入已注冊的使用者名和不正确的密碼,驗證是否登入失敗,并且提示資訊正确;
- 輸入未注冊的使用者名和任意密碼,驗證是否登入失敗,并且提示資訊正确;
- 使用者名和密碼兩者都為空,驗證是否登入失敗,并且提示資訊正确;
- 使用者名和密碼兩者之一為空,驗證是否登入失敗,并且提示資訊正确;
- 如果登入功能啟用了驗證碼功能,在使用者名和密碼正确的前提下,輸入正确的驗證碼,驗證是否登入成功;
- 如果登入功能啟用了驗證碼功能,在使用者名和密碼正确的前提下,輸入錯誤的驗證碼,驗證是否登入失敗,并且提示資訊正确。
功能測試(深入)
1.使用者名和密碼是否大小寫敏感;
2.頁面上的密碼框是否加密顯示;
3.背景系統建立的使用者第一次登入成功時,是否提示修改密碼;
4.忘記使用者名和忘記密碼的功能是否可用;
5.前端頁面是否根據設計要求限制使用者名和密碼長度;
6.如果登入功能需要驗證碼,點選驗證碼圖檔是否可以更換驗證碼,更換後的驗證碼是否可用;
7.重新整理頁面是否會重新整理驗證碼;
8.如果驗證碼具有時效性,需要分别驗證時效内和時效外驗證碼的有效性;
9.使用者登入成功但是會話逾時後,繼續操作是否會重定向到使用者登入界面;
10.不同級别的使用者,比如管理者使用者和普通使用者,登入系統後的權限是否正确;
11.頁面預設焦點是否定位在使用者名的輸入框中;
12.快捷鍵 Tab 和 Enter 等,是否可以正常使用。
安全測試
1.使用者密碼背景存儲是否加密;
2.使用者密碼在網絡傳輸過程中是否加密;
3.密碼是否具有有效期,密碼有效期到期後,是否提示需要修改密碼;
4.不登入的情況下,在浏覽器中直接輸入登入後的 URL 位址,驗證是否會重新定向到使用者登入界面;
5.密碼輸入框是否不支援複制和粘貼;
6.密碼輸入框内輸入的密碼是否都可以在頁面源碼模式下被檢視;
7.使用者名和密碼的輸入框中分别輸入典型的“SQL 注入攻擊”字元串,驗證系統的傳回頁面;
8.使用者名和密碼的輸入框中分别輸入典型的“XSS 跨站腳本攻擊”字元串,驗證系統行為是否被篡改;
9.連續多次登入失敗情況下,系統是否會阻止後續的嘗試以應對暴力破解;
10.同一使用者在同一終端的多種浏覽器上登入,驗證登入功能的互斥性是否符合設計預期;
11.同一使用者先後在多台終端的浏覽器上登入,驗證登入是否具有互斥性。
性能壓力測試
1.單使用者登入的響應時間是否小于 3 秒;
2.單使用者登入時,背景請求數量是否過多;
3.高并發場景下使用者登入的響應時間是否小于 5 秒;
4.高并發場景下服務端的監控名額是否符合預期;
5.高集合點并發場景下,是否存在資源死鎖和不合理的資源等待;
6.長時間大量使用者連續登入和登出,伺服器端是否存在記憶體洩漏。
相容性測試
1.不同浏覽器下,驗證登入頁面的顯示以及功能正确性;
2.相同浏覽器的不同版本下,驗證登入頁面的顯示以及功能正确性;
3.不同移動裝置終端的不同浏覽器下,驗證登入頁面的顯示以及功能正确性;
4.不同分辨率的界面下,驗證登入頁面的顯示以及功能正确性。
Spring Security簡介
Spring Security 是一個能夠為基于 Spring 的企業應用系統提供聲明式的安全通路控制解決方案的安全架構。它提供了一組可以在 Spring 應用上下文中配置的 Bean,充分利用了 Spring IoC,DI(控制反轉 Inversion of Control ,DI:Dependency Injection 依賴注入)和 AOP(面向切面程式設計)功能,為應用系統提供聲明式的安全通路控制功能,減少了為企業系統安全控制編寫大量重複代碼的工作。
Java Web工程——登入
配置檔案
1、在 Maven 工程的 Pom.xml 檔案中添加 Spring Security 的依賴
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
2、配置 web.xml 檔案
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-security.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3、建立 index.html
4、建立 spring 配置檔案 spring-security.xml
<!-- 以下頁面不被攔截 -->
<http pattern="/login.html" security="none"></http>
<http pattern="/css/**" security="none"></http>
<http pattern="/img/**" security="none"></http>
<http pattern="/js/**" security="none"></http>
<http pattern="/plugins/**" security="none"></http>
<!-- 頁面攔截規則 -->
<http use-expressions="false">
<intercept-url pattern="/*" access="ROLE_ADMIN" />
<form-login login-page="/login.html" default-target-url="/admin/index.html" authentication-failure-url="/login.html" always-use-default-target="true"/>
<csrf disabled="true"/>
<headers>
<frame-options policy="SAMEORIGIN"/>
</headers>
</http>
<beans:bean id="bcryptEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
<!-- 認證管理器 -->
<authentication-manager>
<authentication-provider user-service-ref="userDetailService">
</authentication-provider>
</authentication-manager>
<beans:bean id="userDetailService"
class="com.pinyougou.service.UserDetailServiceImpl">
</beans:bean>
測試點提取:
測試點(1)

這裡設定了不被攔截的頁面,就表示在所有這些頁面的通路過程中使不需要攜帶登入資訊的,直接輸入URL即可;是以在測試的過程中要注意頁面的區分,分别測試。
參考測試用例:不登入的情況下,在浏覽器中直接輸入登入後的 URL 位址,驗證是否會重新定向到使用者登入界面;
測試點(2)
這隻設定了使用者登入的權限攔截規則,規定了登入後跳轉的頁面
參考測試用例:不同級别的使用者,比如管理者使用者和普通使用者,登入系統後的權限是否正确
測試點(3)
這裡使用了 Spring Security 的一個認證類,使用認證類調用服務UserDetails,對登入的使用者進行認證校驗,判斷使用者是否為合法登入使用者;結合後端代碼來看:
public class UserDetailsServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_SELLER"));
return new User(username,"123456", grantedAuths);
}
}
如果按照上述的寫法和配置,則使用者在輸入密碼123456時就會通過,無論使用者名為什麼,在開發階段可能為了某些功能的友善測試驗證而使用這種寫法,為防止測試環境或生産環境的代碼忘記修改,此場景也需要測試。(具體的通用密碼、賬号或驗證碼之類的可咨詢相關開發人員)
參考測試用例:參考上述功能測試用例
若後端代碼和配置這樣寫:
/**
* 認證類
* @author Administrator
*
*/
public class UserDetailsServiceImpl implements UserDetailsService {
private SellerService sellerService;
public void setSellerService(SellerService sellerService) {
this.sellerService = sellerService;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("經過了UserDetailsServiceImpl");
//建構角色清單
List<GrantedAuthority> grantAuths=new ArrayList();
grantAuths.add(new SimpleGrantedAuthority("ROLE_SELLER"));
//得到商家對象
TbSeller seller = sellerService.findOne(username);
if(seller!=null){
if(seller.getStatus().equals("1")){
return new User(username,seller.getPassword(),grantAuths);
}else{
return null;
}
}else{
return null;
}
}
}
修改spring-security.xml ,添加如下配置
<!-- 引用dubbo 服務 -->
<dubbo:application name="shop-web" />
<dubbo:registry address="zookeeper://192.168.25.129:2181"/>
<dubbo:reference id="sellerService" interface="com.pinyougou.sellergoods.service.SellerService" >
</dubbo:reference>
<beans:bean id="userDetailService" class="com.pinyougou.service.UserDetailsServiceImpl">
<beans:property name="sellerService" ref="sellerService"></bean:property>
</beans:bean>
經過上述修改後,在登陸頁輸入使用者名和密碼與資料庫一緻即可登陸
密碼加密
使用者表的密碼通常使用 MD5 等不可逆算法加密後存儲,為防止彩虹表破解更會先使用一個特定的字元串(如域名)加密,然後再使用一個随機的 salt(鹽值)加密。特定字元串是程式代碼中固定的,salt 是每個密碼單獨随機,一般給使用者表加一個字段單獨存儲,比較麻煩。BCrypt 算法将 salt 随機并混入最終加密後的密碼,驗證時也無需單獨提供之前的 salt,進而無需單獨處理 salt 問題。
我們在日常測試中除了要關注功能外,還要關注軟體的安全性,可能我們很多人并不是專業的安全測試工程師,但是一般的測試點還是要保證覆寫的
後端部分代碼和配置檔案:
@RequestMapping("/add")
public Result add(@RequestBody TbSeller seller){
//密碼加密
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String password = passwordEncoder.encode(seller.getPassword());
seller.setPassword(password);
spring-security.xml ,添加如下配置
<beans:bean id="bcryptEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
修改認證管理器的配置
<!-- 認證管理器 -->
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref='userDetailService'>
<password-encoder ref="bcryptEncoder"></password-encoder>
</authentication-provider>
</authentication-manager>
這裡我們可以看一下密碼在資料庫的顯示結果:
我們可以看到很明顯的差別,未加密的密碼直接暴露,會帶來賬戶安全隐患;而使用MD5和BCrypt加密的密碼要更為安全;理論上MD5也是不可逆的密碼,無法被破解,但是因為MD5在相同的密碼下生成的加密字元串是固定的,是以在大資料技術下可以建立資料庫将常用密碼進行一一對應存儲的方法來進行破解;相對比BCrypt加鹽的方式,BCrypt加密就更為安全的多了。
參考測試用例:參考上述安全測試
最後,準備好登入界面 login.html。
以上,本文主要用作自己的工作總結,有不對和不足的地方請大家多指正。