溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Spring Security有哪些核心組件

發(fā)布時(shí)間:2021-08-30 11:37:19 來源:億速云 閱讀:197 作者:小新 欄目:編程語言

小編給大家分享一下Spring Security有哪些核心組件,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

Spring Security 核心設(shè)計(jì)

Spring Security 有五個(gè)核心組件:SecurityContext、SecurityContextHolder、Authentication、Userdetails 和 AuthenticationManager。下面分別介紹一下各個(gè)組件。

SecurityContext

SecurityContext 即安全上下文,關(guān)聯(lián)當(dāng)前用戶的安全信息。用戶通過 Spring Security 的校驗(yàn)之后,SecurityContext 會(huì)存儲(chǔ)驗(yàn)證信息,下文提到的 Authentication 對象包含當(dāng)前用戶的身份信息。SecurityContext 的接口簽名如清單 1 所示:

清單 1. SecurityContext 的接口簽名

public interface SecurityContext extends Serializable {
    Authentication getAuthentication();
    void setAuthentication(Authentication authentication);
}

SecurityContext 存儲(chǔ)在 SecurityContextHolder 中。

SecurityContextHolder
SecurityContextHolder 存儲(chǔ) SecurityContext 對象。SecurityContextHolder 是一個(gè)存儲(chǔ)代理,有三種存儲(chǔ)模式分別是:

  • MODE_THREADLOCAL:SecurityContext 存儲(chǔ)在線程中。

  • MODE_INHERITABLETHREADLOCAL:SecurityContext 存儲(chǔ)在線程中,但子線程可以獲取到父線程中的 SecurityContext。

  • MODE_GLOBAL:SecurityContext 在所有線程中都相同。

SecurityContextHolder 默認(rèn)使用 MODE_THREADLOCAL 模式,SecurityContext 存儲(chǔ)在當(dāng)前線程中。調(diào)用 SecurityContextHolder 時(shí)不需要顯示的參數(shù)傳遞,在當(dāng)前線程中可以直接獲取到 SecurityContextHolder 對象。但是對于很多 C 端的應(yīng)用(音樂播放器,游戲等等),用戶登錄完畢,在軟件的整個(gè)生命周期中只有當(dāng)前登錄用戶,面對這種情況 SecurityContextHolder 更適合采用 MODE_GLOBAL 模式,SecurityContext 相當(dāng)于存儲(chǔ)在應(yīng)用的進(jìn)程中,SecurityContext 在所有線程中都相同。

Authentication

Authentication 即驗(yàn)證,表明當(dāng)前用戶是誰。什么是驗(yàn)證,比如一組用戶名和密碼就是驗(yàn)證,當(dāng)然錯(cuò)誤的用戶名和密碼也是驗(yàn)證,只不過 Spring Security 會(huì)校驗(yàn)失敗。Authentication 接口簽名如清單 2 所示:

清單 2. Authentication 的接口簽名

public interface Authentication extends Principal, Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();
    Object getCredentials();
    Object getDetails();
    Object getPrincipal();
    boolean isAuthenticated();
    void setAuthenticated(boolean isAuthenticated);
}

Authentication 是一個(gè)接口,實(shí)現(xiàn)類都會(huì)定義 authorities,credentials,details,principal,authenticated 等字段,具體含義如下:

  • getAuthorities: 獲取用戶權(quán)限,一般情況下獲取到的是用戶的角色信息。

  • getCredentials: 獲取證明用戶認(rèn)證的信息,通常情況下獲取到的是密碼等信息。

  • getDetails: 獲取用戶的額外信息,比如 IP 地址、經(jīng)緯度等。

  • getPrincipal: 獲取用戶身份信息,在未認(rèn)證的情況下獲取到的是用戶名,在已認(rèn)證的情況下獲取到的是 UserDetails (暫時(shí)理解為,當(dāng)前應(yīng)用用戶對象的擴(kuò)展)。

  • isAuthenticated: 獲取當(dāng)前 Authentication 是否已認(rèn)證。

  • setAuthenticated: 設(shè)置當(dāng)前 Authentication 是否已認(rèn)證。

在驗(yàn)證前,principal 填充的是用戶名,credentials 填充的是密碼,detail 填充的是用戶的 IP 或者經(jīng)緯度之類的信息。通過驗(yàn)證后,Spring Security 對 Authentication 重新注入,principal 填充用戶信息(包含用戶名、年齡等), authorities 會(huì)填充用戶的角色信息,authenticated 會(huì)被設(shè)置為 true。重新注入的 Authentication 會(huì)被填充到 SecurityContext 中。

UserDetails

UserDetails 提供 Spring Security 需要的用戶核心信息。UserDetails 的接口簽名如清單 3 所示:

清單 3. UserDetails 的接口簽名

public interface UserDetails extends Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();
    String getPassword();
    String getUsername();
    boolean isAccountNonExpired();
    boolean isAccountNonLocked();
    boolean isCredentialsNonExpired();
    boolean isEnabled();
}

UserDetails 用 isAccountNonExpired, isAccountNonLocked,isCredentialsNonExpired,isEnabled 表示用戶的狀態(tài)(與下文中提到的 DisabledException,LockedException,BadCredentialsException 相對應(yīng)),具體含義如下:

  • getAuthorites:獲取用戶權(quán)限,本質(zhì)上是用戶的角色信息。

  • getPassword: 獲取密碼。

  • getUserName: 獲取用戶名。

  • isAccountNonExpired: 賬戶是否過期。

  • isAccountNonLocked: 賬戶是否被鎖定。

  • isCredentialsNonExpired: 密碼是否過期。

  • isEnabled: 賬戶是否可用。

UserDetails 也是一個(gè)接口,實(shí)現(xiàn)類都會(huì)繼承當(dāng)前應(yīng)用的用戶信息類,并實(shí)現(xiàn) UserDetails 的接口。假設(shè)應(yīng)用的用戶信息類是 User,自定義的 CustomUserdetails 繼承 User 類并實(shí)現(xiàn) UserDetails 接口。

AuthenticationManager

AuthenticationManager 負(fù)責(zé)校驗(yàn) Authentication 對象。在 AuthenticationManager 的 authenticate 函數(shù)中,開發(fā)人員實(shí)現(xiàn)對 Authentication 的校驗(yàn)邏輯。如果 authenticate 函數(shù)校驗(yàn)通過,正常返回一個(gè)重新注入的 Authentication 對象;校驗(yàn)失敗,則拋出 AuthenticationException 異常。authenticate 函數(shù)簽名如清單 4 所示:

清單 4. authenticate 函數(shù)簽名

Authentication authenticate(Authentication authentication)throws AuthenticationException;

AuthenticationManager 可以將異常拋出的更加明確:

  • 當(dāng)用戶不可用時(shí)拋出 DisabledException。

  • 當(dāng)用戶被鎖定時(shí)拋出 LockedException。

  • 當(dāng)用戶密碼錯(cuò)誤時(shí)拋出 BadCredentialsException。

重新注入的 Authentication 會(huì)包含當(dāng)前用戶的詳細(xì)信息,并且被填充到 SecurityContext 中,這樣 Spring Security 的驗(yàn)證流程就完成了,Spring Security 可以識別到 "你是誰"。

基本校驗(yàn)流程示例

下面采用 Spring Security 的核心組件寫一個(gè)最基本的用戶名密碼校驗(yàn)示例,如清單 5 所示:

清單 5. Spring Security 核心組件偽代碼

AuthenticationManager amanager = new CustomAuthenticationManager();
Authentication namePwd = new CustomAuthentication(“name”, “password”);
try {
    Authentication result = amanager.authenticate(namePwd);
    SecurityContextHolder.getContext.setAuthentication(result);
} catch(AuthenticationException e) {
    // TODO 驗(yàn)證失敗
}

Spring Security 的核心組件易于理解,其基本校驗(yàn)流程是: 驗(yàn)證信息傳遞過來,驗(yàn)證通過,將驗(yàn)證信息存儲(chǔ)到 SecurityContext 中;驗(yàn)證失敗,做出相應(yīng)的處理。

Spring Security 在 Web 中的設(shè)計(jì)

Spring Security 的一個(gè)常見應(yīng)用場景就是 Web。下面討論 Spring Security 在 Web 中的使用方式。

Spring Security 最簡登錄實(shí)例

Spring Security 在 Web 中的使用相對要復(fù)雜一點(diǎn),會(huì)涉及到很多組件?,F(xiàn)在給出自定義登錄的偽代碼,如清單 6 所示。您可以點(diǎn)擊這里,查看完整的代碼。

清單 6. Web 登錄偽代碼

@Controller
public class UserController { 
 
    @PostMapping(“/login”)
    public void login(String name, String password){
       matchNameAndPassword(name, password);
       User user = getUser(name);
       Authentication auth = new CustomAuthentication(user, password);
       auth.setAuthenticated(true);
       SecurityContextHolder.getContext.setAuthentication(auth);
    }
}

觀察代碼會(huì)發(fā)現(xiàn),如果用 Spring Security 來集成已存在的登錄邏輯,真正和 Spring Security 關(guān)聯(lián)的代碼只有短短 3 行。驗(yàn)證邏輯可以不經(jīng)過 AuthenticationManager,真正需要做的就是把經(jīng)過驗(yàn)證的用戶信息注入到 Authentication 中,并將 Authentication 填充到 SecurityContext 中。在實(shí)際情況中,登錄邏輯的確可以這樣寫,尤其是已經(jīng)存在登錄邏輯的時(shí)候,通常會(huì)這樣寫。這樣寫雖然方便,但是不符合 Spring Security 在 Web 中的架構(gòu)設(shè)計(jì)。

下面視頻中會(huì)介紹已存在的項(xiàng)目如何與 Spring Security 進(jìn)行集成,要求對已存在的登錄驗(yàn)證邏輯不變,但可以使用 Spring Security 的優(yōu)秀特性和功能。

視頻地址:https://mediacenter.ibm.com/media/0_qvbohipj

項(xiàng)目地址:https://github.com/springAppl/yuchigong

Spring Security 在 Web 中的核心組件

下面介紹在 Web 環(huán)境中 Spring Security 的核心組件。

FilterChainProxy

FilterChaniProxy 是 FilterChain 代理。FilterChain 維護(hù)了一個(gè) Filter 隊(duì)列,這些 Filter 為 Spring Security 提供了強(qiáng)大的功能。一個(gè)很常見的問題是:Spring Security 在 Web 中的入口是哪里?答案是 Filter。Spring Security 在 Filter 中創(chuàng)建 Authentication 對象,并調(diào)用 AuthenticationManager 進(jìn)行校驗(yàn)。Spring Security 選擇 Filter,而沒有采用上文中 Controller 的方式有以下優(yōu)點(diǎn)。Spring Security 依賴 J2EE 標(biāo)準(zhǔn),無需依賴特定的 MVC 框架。另一方面 Spring MVC 通過 Servlet 做請求轉(zhuǎn)發(fā),如果 Spring Security 采用 Servlet,那么 Spring Security 和 Spring MVC 的集成會(huì)存在問題。FilterChain 維護(hù)了很多 Filter,每個(gè) Filter 都有自己的功能,因此在 Spring Security 中添加新功能時(shí),推薦通過 Filter 的方式來實(shí)現(xiàn)。

ProviderManager

ProviderManager 是 AuthenticationManager 的實(shí)現(xiàn)類。ProviderManager 并沒有實(shí)現(xiàn)對 Authentication 的校驗(yàn)功能,而是采用代理模式將校驗(yàn)功能交給 AuthenticationProvider 去實(shí)現(xiàn)。這樣設(shè)計(jì)是因?yàn)樵?Web 環(huán)境中可能會(huì)支持多種不同的驗(yàn)證方式,比如用戶名密碼登錄、短信登錄、指紋登錄等等,如果每種驗(yàn)證方式的代碼都寫在 ProviderManager 中,想想都是災(zāi)難。因此為每種驗(yàn)證方式提供對應(yīng)的 AuthenticationProvider,ProviderManager 將驗(yàn)證任務(wù)代理給對應(yīng)的 AuthenticationProvider,這是一種不錯(cuò)的解決方案。在 ProviderManager 中可以找到以下代碼,如清單 7 所示:

清單 7. ProviderManager 代碼片段

private List<AuthenticationProvider> providers;
public Authentication authenticate(Authentication authentication)
           throws AuthenticationException {
    ......
    for (AuthenticationProvider provider : getProviders()) {
       if (!provider.supports(toTest)) {
           continue;
       }
       try {
           result = provider.authenticate(authentication);
           if (result != null) {
               copyDetails(authentication, result);
               break;
           }
       }
    }
}

ProviderManager 維護(hù)了一個(gè) AuthenticationProvider 隊(duì)列。當(dāng) Authentication 傳遞進(jìn)來時(shí),ProviderManager 通過 supports 函數(shù)查找支持校驗(yàn)的 AuthenticationProvider。如果沒有找到支持的 AuthenticationProvider 將拋出 ProviderNotFoundException 異常。

AuthenticationProvider

AuthenticationProvider 是在 Web 環(huán)境中真正對 Authentication 進(jìn)行校驗(yàn)的組件。其接口簽名如清單 8 所示:

清單 8. AuthenticationProvider 的接口簽名

public interface AuthenticationProvider {
    Authentication authenticate(Authentication authentication)
           throws AuthenticationException;
    boolean supports(Class<?> authentication);
}

其中,authenticate 函數(shù)用于校驗(yàn) Authentication 對象;supports 函數(shù)用于判斷 provider 是否支持校驗(yàn) Authentication 對象。

當(dāng)應(yīng)用添加新的驗(yàn)證方式時(shí),驗(yàn)證邏輯需要寫在對應(yīng) AuthenticationProvider 中的 authenticate 函數(shù)中。驗(yàn)證通過返回一個(gè)重新注入的 Authentication,驗(yàn)證失敗拋出 AuthenticationException 異常。

Spring Security 在 Web 中的認(rèn)證示例

下面的視頻中會(huì)介紹采用 Spring Security 提供的 UsernamePasswordAuthenticationFilter 實(shí)現(xiàn)登錄驗(yàn)證。

視頻地址:https://mediacenter.ibm.com/media/0_7vq75cue

項(xiàng)目地址:https://github.com/springAppl/rachel

下面以用戶名密碼登錄為例來梳理 Spring Security 在 Web 中的認(rèn)證流程。上文提到 Spring Security 是以 Filter 來作為校驗(yàn)的入口點(diǎn)。在用戶名密碼登錄中對應(yīng)的 Filter 是 UsernamePasswordAuthenticationFilter。attemptAuthentication 函數(shù)會(huì)執(zhí)行調(diào)用校驗(yàn)的邏輯。在 attemptAuthentication 函數(shù)中,可以找到以下代碼,如清單 9 所示:

清單 9. attemptAuthentication 函數(shù)代碼片段

public Authentication attemptAuthentication(HttpServletRequest request,HttpServletResponse 
response) throws AuthenticationException {
    ......
    UsernamePasswordAuthenticationToken authRequest = new 
UsernamePasswordAuthenticationToken(username, password);
    setDetails(request, authRequest);
    return this.getAuthenticationManager().authenticate(authRequest);
}

attemptAuthentication 函數(shù)會(huì)調(diào)用 AuthenticationManager 執(zhí)行校驗(yàn)邏輯,并獲取到重新注入后的 Authentication。在 UsernamePasswordAuthenticationFilter 父類 AbstractAuthenticationProcessingFilter 的 successfulAuthentication 函數(shù)中發(fā)現(xiàn)以下代碼,如清單 10 所示:

清單 10. successAuthentication 函數(shù)

protected void successfulAuthentication(HttpServletRequest request,
           HttpServletResponse response, FilterChain chain, Authentication 
authResult)throws IOException, ServletException {
    ......     SecurityContextHolder.getContext().setAuthentication(authResult);
    ......
}

successfulAuthentication 函數(shù)會(huì)把重新注入的 Authentication 填充到 SecurityContext 中,完成驗(yàn)證。

在 Web 中,AuthenticationManager 的實(shí)現(xiàn)類 ProviderManager 并沒有實(shí)現(xiàn)校驗(yàn)邏輯,而是代理給 AuthenticationProvider, 在用戶名密碼登錄中就是 DaoAuthenticationProvider。DaoAuthenticationProvider 主要完成 3 個(gè)功能:獲取 UserDetails、校驗(yàn)密碼、重新注入 Authentication。在 authenticate 函數(shù)中發(fā)現(xiàn)以下代碼,如清單 11 所示:

清單 11. DaoAuthenticationProvider.authenticate 函數(shù)簽名

public Authentication authenticate(Authentication authentication)
           throws AuthenticationException {
    ......
    // 獲取 UserDetails
    UserDetails user = this.userCache.getUserFromCache(username);
    if (user == null) {
       cacheWasUsed = false;
       try {
           user = retrieveUser(username,         
    (UsernamePasswordAuthenticationToken) authentication);
       }
       ......
    }
    ......
    try {
       ......
       //校驗(yàn)密碼
       additionalAuthenticationChecks(
           user,
       (UsernamePasswordAuthenticationToken) authentication
       );
    }
    ......
    // 從新注入 Authentication
    return createSuccessAuthentication(
           principalToReturn, 
           authentication, 
           user
        );
}

首先從 userCache 緩存中查找 UserDetails, 如果緩存中沒有獲取到,調(diào)用 retrieveUser 函數(shù)獲取 UserDetails。retrieveUser 函數(shù)簽名如清單 12 所示:

清單 12. retrieveUser 函數(shù)簽名

protected final UserDetails retrieveUser(String username,
    UsernamePasswordAuthenticationToken authentication)
           throws AuthenticationException {
    UserDetails loadedUser;
    try {
        loadedUser = this.getUserDetailsService().loadUserByUsername(username);
    }
    ......
    return loadedUser;
}

retrieveUser 函數(shù)調(diào)用 UserDetailsService 獲取 UserDetails 對象。UserDetailsService 接口簽名如清單 13 所示:

清單 13. UserDetailsService 接口簽名

public interface UserDetailsService {
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

UserDetailsService 非常簡單,只有一個(gè) loadUserByUserName 函數(shù),函數(shù)參數(shù)雖然名為 username,但只要是用戶的唯一標(biāo)識符即可。下面是基于數(shù)據(jù)庫存儲(chǔ)的簡單示例, 如清單 14 所示:

清單 14. CustomUserDetailsService 類簽名

public class CustomUserDetailsService implements UserDetailsService {
   
  @Autowired
  private UserDao userDao;
 
  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 
{
    User user = userDao.findByName(username);
    if(Objects.isNull(user)) {
      throw new UsernameNotFoundException();
    }
    UserDetails details = new CustomUserDetails(user);
    return details;
  }
}

調(diào)用 UserDao 獲取 User 對象,將 User 對象包裝成 UserDetails 對象。如果沒有找到 User 對象,需要拋出 UsernameNotFoundException 異常。

DaoAuthenticationProvider 密碼校驗(yàn)調(diào)用 additionalAuthenticationChecks 函數(shù),具體通過 PasswordEncoder 比對用戶輸入的密碼和存儲(chǔ)在應(yīng)用中的密碼是否相等,如果不相等,拋出 BadCredentialsException 異常。

DaoAuthenticationProvider 對 Authentication 對象的重新注入通過調(diào)用 createSuccessAuthentication 函數(shù), 如清單 15 所示:

清單 15. createSuccessAuthentication 函數(shù)簽名

protected Authentication createSuccessAuthentication(Object principal,
           Authentication authentication, UserDetails user) {
    UsernamePasswordAuthenticationToken result = new    
    UsernamePasswordAuthenticationToken(
        principal, 
        authentication.getCredentials(),     
        authoritiesMapper.mapAuthorities(user.getAuthorities())
    );
    result.setDetails(authentication.getDetails());
    return result;
}

以上就是 Spring Security 在 Web 環(huán)境中對于用戶名密碼校驗(yàn)的整個(gè)流程,簡言之:

  1. UsernamePasswordAuthenticationFilter 接受用戶名密碼登錄請求,將 Authentication 傳遞給 ProviderManager 進(jìn)行校驗(yàn)。

  2. ProviderManager 將校驗(yàn)任務(wù)代理給 DaoAuthenticationProvider。

  3. DaoAuthenticationProvider 對 Authentication 的用戶名和密碼進(jìn)行校驗(yàn),校驗(yàn)通過后返回重新注入的 Authentication 對象。

  4. UsernamePasswordAuthenticationFilter 將重新注入的 Authentication 對象填充到 SecurityContext 中。

指紋登錄實(shí)踐

指紋登錄和用戶名密碼登錄區(qū)別很小,只是將密碼換成了指紋特征值。下面采用 Spring Security 推薦寫法 Filter-AuthenticationProvider 的形式來定義相關(guān)組件以實(shí)現(xiàn)指紋登錄。完整的項(xiàng)目地址:https://github.com/springAppl/rachel。

FingerPrintToken

FingerPrintToken 增加 name 和 fingerPrint 字段,分別代表用戶名和指紋特征值,如清單 16 所示:

清單 16. FingerPrintToken 函數(shù)簽名

public class FingerPrintToken implements Authentication {
    private String name;  
    private String fingerPrint;
    ......
}

FingerPrintFilter

FingerPrintFilter 處理指紋登錄請求,調(diào)用 AuthenticationManager 進(jìn)行驗(yàn)證,驗(yàn)證通過后調(diào)用 SecurityContextHolder 將重新注入的 Authentication 填充到 SecurityContext 中,如清單 17 所示:

清單 17. doFilter 函數(shù)簽名

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, 
FilterChain filterChain) throws IOException, ServletException {
    if (Objects.equals(httpServletRequest.getRequestURI(), "/api/finger-print")) {
        // 調(diào)用 AuthenticationManager, 并填充 SecurityContext
    }
}

FingerPrintProvider

FingerPrintProvider 負(fù)責(zé)處理 FingerPrintToken,需要在 supports 函數(shù)中支持處理 FingerPrintToken。authenticate 函數(shù)負(fù)責(zé) UserDetails 獲取,指紋校驗(yàn),F(xiàn)ingerPrintToken 的重新注入。

FingerPrintUserDetails

FingerPrintUserDetails 繼承 User 并實(shí)現(xiàn) UserDetails 的方法,應(yīng)用的用戶信息可以加載到 Spring Security 中使用。

FingerPrintUserDetailsService

FingerPrintUserDetailsService 獲取 FingerUserDetails。通過 UserDao 查找到 User,并將 User 轉(zhuǎn)換為 Spring Security 可識別 UserDetails。

SecurityConfig

SecurityConfig 繼承 WebSecurityConfigurerAdapter,需要定義 Spring Security 配置類。Spring Security 的配置不是本文的重點(diǎn),配置時(shí)只需要注意以下幾點(diǎn):

  1. 將 FingerPrintFilter、FingerPrintProvider 添加進(jìn)去。

  2. 將 FingerPrintFilter 的執(zhí)行順序放置在 SecurityContextPersistenceFilter 之后即可。Spring Security 維護(hù)了一個(gè) Filter 的 list,因此每個(gè) Filter 是有順序的。

  3. 將 "/api/test" 請求設(shè)置為用戶驗(yàn)證成功后才允許方問。

配置代碼在 configure 函數(shù)中,如清單 18 所示:

清單 18. configure 函數(shù)

protected void configure(HttpSecurity http) throws Exception {
    http
       .userDetailsService(userDetailsService())
       .addFilterAfter(fingerPrintFilter(), SecurityContextPersistenceFilter.class)
       .authenticationProvider(fingerPrintProvider())
       .authorizeRequests()
       .mvcMatchers(HttpMethod.GET, "/api/test").authenticated()
}

以上是“Spring Security有哪些核心組件”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI