簡體   English   中英

這個Spring Security示例究竟如何運作?

[英]How exactly works this Spring Security example?

我是Spring Security的新手,我對在教程中找到的配置有些懷疑。

這是用於Spring Security配置到項目中的spring-security.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:security="http://www.springframework.org/schema/security"
    xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <security:http>
        <security:intercept-url pattern="/springLogin" access="permitAll"/>
        <security:intercept-url pattern="/doSpringLogin" access="permitAll"/>
        <security:intercept-url pattern="/myprofile" access="hasRole('ROLE_USER')"/>
        <security:intercept-url pattern="/springHome" access="hasRole('ROLE_USER')"/> 
        <security:intercept-url pattern="/products" access="hasRole('ROLE_USER')"/> 
        <security:intercept-url pattern="/springLogout" access="permitAll"/>
        <security:intercept-url pattern="/springLogin?error=true" access="permitAll"/>
        <security:form-login login-page="/springLogin" login-processing-url="/doSpringLogin"
        default-target-url="/springHome" authentication-failure-url="/springLogin?error=true"
        username-parameter="username" password-parameter="password"
        />
        <security:csrf disabled="true"/>
        <security:logout logout-url="/springLogout" logout-success-url="/springLogin"/>
    </security:http>

    <bean id="userDetailsServiceImpl" class="com.demo.security.UserDetailsServiceImpl"></bean>

    <bean id="authenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
        <property name="userDetailsService" ref="userDetailsServiceImpl"></property>
    </bean>

    <bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
        <constructor-arg name="providers">
            <list>
                <ref bean="authenticationProvider"/>
            </list>
        </constructor-arg>
    </bean>

    <security:authentication-manager>
        <security:authentication-provider user-service-ref="userDetailsServiceImpl">
            <security:password-encoder hash="plaintext"></security:password-encoder>
        </security:authentication-provider>
    </security:authentication-manager>

</beans>

我把它分成了一些部分。 第一個是 標簽內容。

它包含以下內容:

<security:intercept-url pattern="/springLogin" access="permitAll"/>

我認為這意味着每個人都可以訪問與/ springLogin資源相關的頁面

<security:intercept-url pattern="/myprofile" access="hasRole('ROLE_USER')"/>

表示與/ myprofile資源相關的資源只能由設置了ROLE_USER角色的已記錄用戶(主體) 訪問

這個推理是否正確?

然后在之前的配置文件中有:

1) authenticationManager bean的聲明:

<bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
    <constructor-arg name="providers">
        <list>
            <ref bean="authenticationProvider"/>
        </list>
    </constructor-arg>
</bean>

我認為Spring使用它來使用Principal對象(例如Web應用程序的所有用戶)和Authorities (特定Principal可以執行的操作)填充SecurityContext

這個推理是否正確?

此對象將構造函數arg作為必須提供Principal信息的autentication提供者 bean的列表(例如,與特定Principal關聯的角色)

在這種情況下,提供了一個DaoAuthenticationProvider類的實現,它將name =“userDetailsS​​ervice”的bean作為屬性,這個:

<bean id="userDetailsServiceImpl" class="com.demo.security.UserDetailsServiceImpl"></bean>

這是UserDetailsS​​erviceImpl類的一個實例,這個類:

public class UserDetailsServiceImpl implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        System.out.println(username);
        User user = RegisteryDAO.getUserDAO().getUserByUsername(username);

        if(user == null){
            return null;
        }

        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
        authorities.add(new SimpleGrantedAuthority(user.getRole()));

        UserDetails userDetails = new org.springframework.security.core.userdetails.
                User(user.getUsername(), user.getPassword(), true, true, true, true, authorities);


        return userDetails;
    }

}

究竟發生了什么?

使用調試器在我看來,當用戶嘗試訪問特定頁面時,此loadUserByUsername()返回與記錄用戶相關的UserDetails對象,該對象包含表示與特定登錄用戶關聯的角色的List (例如,之前的ROLE_USER

然后我認為Spring會自動使用

<security:intercept-url pattern="/myprofile" access="hasRole('ROLE_USER')"/>

檢查用戶是否已將propper角色設置到上一個List列表中。

如果它已經將請求轉發到處理此Http請求的控制器方法,否則避免此HttpRequest來到此控制器方法並顯示一個頁面,表明用戶無法訪問此資源。

以下是您要詢問的一些概念和問題的解釋。


AuthenticationManager

AuthenticationManager是負責處理Authentication請求的組件。 對於用戶名/密碼登錄,身份驗證請求可能是UsernamePasswordAuthenticationToken實例。

對於其他實現,請查看Authentication JavaDoc

AuthenticationManager還具有AuthenticationProvider實現的集合。 這些組件能夠處理特定的Authentication類型,並且AuthenticationManager通過它們迭代嘗試找到能夠處理傳遞給它的Authentication 如果找到一個,則在顯示Authentication對象的情況下調用它,如果成功則返回完全填充的Authentication對象(否則拋出AuthenticationException )。


AuthenticationProvider

如上所述, AuthenticationProvider處理某種類型的Authentication請求。 例如,當AuthenticationManager調用時, DaoAuthenticationProvider將執行以下步驟:

  • 傳遞給它的UsernamePasswordAuthenticationToken
  • 使用提供給它的UserDetailsService服務實現(在您的情況下是UserDetailServiceImpl )來按用戶名查找用戶
  • 如果已指定,請使用PasswordEncoderSaltSource檢查用戶身份驗證令牌中提供的PasswordEncoder
  • 如果身份驗證成功,則返回填充的Authentication對象(在本例中為UsernamePasswordAuthenticationToken ),其中包含主體,憑據並標記為已authenticated
  • 如果身份驗證失敗,將拋出AuthenticationException

您正在使用的DaoAuthenticationProvider能夠處理UsernamePasswordAuthenticationToken請求。 因此通常會形成登錄,等等。 您可以通過查看其supports()方法實現來查看哪些類型的身份驗證提供程序支持,在DaoAuthenticationProvider情況下如下所示:

public boolean supports(Class<?> authentication) {
    return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}

Spring安全過濾器鏈

現在讓我們看一下Spring Security文檔定義的Security過濾器鏈:

過濾器在鏈中定義的順序非常重要。 無論您實際使用哪種過濾器,訂單應如下:

  1. ChannelProcessingFilter ,因為它可能需要重定向到不同的協議

  2. SecurityContextPersistenceFilter ,因此可以在Web請求開始時在SecurityContextHolder中設置SecurityContext,並且當Web請求結束時(可以使用下一個Web請求准備好),可以將對SecurityContext的任何更改復制到HttpSession。

  3. ConcurrentSessionFilter ,因為它使用SecurityContextHolder功能但需要更新SessionRegistry以反映來自主體的持續請求

  4. 身份驗證處理機制 - UsernamePasswordAuthenticationFilterCasAuthenticationFilterBasicAuthenticationFilter等 - 以便可以修改SecurityContextHolder以包含有效的身份驗證請求令牌

  5. The SecurityContextHolderAwareRequestFilter ,如果您使用它將Spring安全感知HttpServletRequestWrapper安裝到您的servlet容器中

  6. RememberMeAuthenticationFilter ,這樣如果沒有早期的身份驗證處理機制更新了SecurityContextHolder,並且請求提供了一個啟用記住我服務的cookie,那么一個合適的記憶身份驗證對象將放在那里

  7. AnonymousAuthenticationFilter ,這樣如果沒有早期的身份驗證處理機制更新SecurityContextHolder,那么匿名身份驗證對象將被放在那里

  8. ExceptionTranslationFilter ,用於捕獲任何Spring Security異常,以便可以返回HTTP錯誤響應或啟動相應的AuthenticationEntryPoint

  9. FilterSecurityInterceptor ,用於保護Web URI並在拒絕訪問時引發異常

當用戶提交登錄表單時,將在過濾器鏈中的步驟4中調用AuthenticationManager 在表單登錄的情況下,它將由UsernamePasswordAuthenticationFilter處理,它調用AuthenticationManager來處理身份驗證:

public Authentication attemptAuthentication(HttpServletRequest request,
        HttpServletResponse response) throws AuthenticationException {
    // ...
    return this.getAuthenticationManager().authenticate(authRequest);
}

使用調試器在我看來,當用戶嘗試訪問特定頁面時,此loadUserByUsername()返回UserDetails

實際上,在用戶進行身份驗證時會調用loadUserByUsername() ,例如在提交登錄表單之后。 如果用戶已經過身份驗證,則不會調用此用戶。

我認為這意味着每個人都可以訪問與/springLogin資源相關的頁面:

 <security:intercept-url pattern="/springLogin" access="permitAll" /> 

然后我認為Spring將自動使用以下內容來檢查用戶是否具有適當的角色:

 <security:intercept-url pattern="/myprofile" access="hasRole('ROLE_USER')" /> 

正確。 此過程由FilterSecurityInterceptor處理,它擴展了AbstractSecurityInterceptor - 處理授權的核心Spring Security組件。 如果用戶未經過身份驗證或沒有所需的角色,則ExceptionTranslationFilter會拋出並處理ExceptionTranslationFilter 此過濾器處理安全性異常。 例如,在認證失敗的情況下,它將用戶重定向到認證入口點,例如登錄頁面。


Spring Security的內部體系結構在參考文檔中有很好的描述。 我建議看看它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM