1. 程式人生 > >去除Spring Security認證:Pre-Authentication配置

去除Spring Security認證:Pre-Authentication配置

Spring Security官方文件對Pre-Authentication是這樣解釋的:

There are situations where you want to use Spring Security for authorization, but the user has already been reliably authenticated by some external system prior to accessing the application. We refer to these situations as “pre-authenticated” scenarios.

這裡面涉及到Spring Security中兩個概念,認證(Authentication)和授權(Authorization)。有關這兩個概念的介紹,網上可以搜尋到其他相關資料,這裡僅通俗易懂的解釋一下:
- 認證(Authentication):認證就是判斷使用者身份是否合法,例如使用者名稱密碼登入就是認證,如果一個使用者擁有正確的密碼,即可通過認證;
- 授權(Authorization):使用者認證通過了,但是每個使用者的許可權不同,判斷使用者有哪些許可權以及是否有許可權訪問某些資源,就是授權。

Spring Security框架提供了認證和授權的功能,但是有可能只希望使用Spring Security的授權功能,而不使用它提供的認證功能,比如使用一些其他認證方式,那麼就可以使用Pre-Authentication。

Pre-Authentication配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/security
        http://www.springframework.org/schema/security/spring-security.xsd"
>
<security:http entry-point-ref="http403ForbiddenEntryPoint"> <!-- 省略其他配置 --> <security:custom-filter position="PRE_AUTH_FILTER" ref="preauthFilter" /> </security:http> <bean id="http403ForbiddenEntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint" /> <bean id="preauthFilter" class="com.xxg.test.auth.PreauthFilter"> <property name="authenticationManager" ref="authenticationManager" /> </bean> <bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider"> <property name="preAuthenticatedUserDetailsService"> <bean id="userDetailsServiceWrapper" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper"> <property name="userDetailsService" ref="userDetailsService"/> </bean> </property> </bean> <bean id="userDetailsService" class="com.xxg.test.auth.UserDetailsServiceImpl" /> <security:authentication-manager alias="authenticationManager"> <security:authentication-provider ref="preauthAuthProvider" /> </security:authentication-manager> </beans>

由於不再使用Spring Security提供的預設的使用者名稱密碼登入認證,需要修改entry-point-refHttp403ForbiddenEntryPoint,否則會出現異常:

No AuthenticationEntryPoint could be established. Please make sure you have a login mechanism configured through the namespace (such as form-login) or specify a custom AuthenticationEntryPoint with the ‘entry-point-ref’ attribute

配置Pre-Authentication的最主要的部分是需要新增一個position="PRE_AUTH_FILTER"的Filter,這個Filter繼承抽象類AbstractPreAuthenticatedProcessingFilter

public class PreauthFilter extends AbstractPreAuthenticatedProcessingFilter {

    /**
    * 重寫,返回使用者名稱,這個使用者名稱是經過其他方式認證過
    */
    @Override
    protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
        if (authenticated) {
            // 可以通過request獲取當前認證過的使用者名稱,比如通過引數、HTTP請求頭或者Cookie獲取token,再通過token呼叫第三方介面獲取使用者名稱
            return "your_username";
        } else {
            // 如果認證失敗,可以丟擲異常
            throw new PreAuthenticatedCredentialsNotFoundException("認證失敗");
        }
    }

    /**
    * 這個方法一般情況下不需要重寫,直接返回空字串即可
    */
    @Override
    protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
        return "";
    }
}

另外還有個重點配置userDetailsService,這個是用於使用者認證後的授權。這裡需要一個UserDetailsService的實現類,來獲取使用者的所有許可權。

public class UserDetailsServiceImpl implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        // 這裡可以通過使用者名稱獲取對應的許可權
        Collection<GrantedAuthority> auths = new ArrayList<>();
        auths.add(new SimpleGrantedAuthority("ROLE_USER"));
        auths.add(new SimpleGrantedAuthority("ROLE_SUPER_ADMIN"));

        User user = new User(username, "", auths);
        return user;
    }
}

使用場景

如果是普通的瀏覽器訪問的Web,以上完成配置後,使用者在瀏覽器上首次訪問會呼叫AbstractPreAuthenticatedProcessingFiltergetPreAuthenticatedPrincipal以及UserDetailsServiceloadUserByUsername方法來獲取認證使用者和授權,並將相關資訊儲存到Session中,後續的請求直接通過Session獲取使用者資訊,不再重複呼叫這些方法。

而對於API介面來說,一般情況下不會使用Session來做會話控制,例如可能會通過token的方式。API介面相對來說每次介面訪問都是無狀態的,所以針對每次請求都需要重新認證和授權。這個時候可以設定create-session="stateless"來禁掉Spring Security使用Session:

<security:http entry-point-ref="http403ForbiddenEntryPoint" create-session="stateless">
    <!-- 省略其他配置 -->
    <security:custom-filter position="PRE_AUTH_FILTER" ref="preauthFilter" />
</security:http>

參考資料