1. 程式人生 > 其它 >SpringSecurity認證基本原理與認證2種方式

SpringSecurity認證基本原理與認證2種方式

Spring Security功能的實現主要是由一系列過濾器相互配合完 成。也稱之為過濾器鏈,Spring Security預設載入15個過濾器, 但是隨著配置可以增加或者刪除一些過濾器.

一、過濾器鏈介紹

過濾器是一種典型的AOP思想,下面簡單瞭解下這些過濾器鏈,後續再原始碼剖析中在涉及到過濾器鏈在 仔細講解

1.org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter

根據請求封裝獲取WebAsyncManager,從WebAsyncManager獲取/註冊的安全上下文可調 用處理攔截器

2.org.springframework.security.web.context.SecurityContextPersistenceFilter

SecurityContextPersistenceFilter主要是使用SecurityContextRepository在session中儲存 或更新一個SecurityContext,並將SecurityContext給以後的過濾器使用,來為後續fifilter 建立所需的上下文。SecurityContext中儲存了當前使用者的認證以及許可權資訊。

3.org.springframework.security.web.header.HeaderWriterFilter

向請求的Header中新增相應的資訊,可在http標籤內部使用security:headers來控制

4.org.springframework.security.web.csrf.CsrfFilter

csrf又稱跨域請求偽造,SpringSecurity會對所有post請求驗證是否包含系統生成的csrf的 token資訊,如果不包含,則報錯。起到防止csrf攻擊的效果。

5.org.springframework.security.web.authentication.logout.LogoutFilter

匹配URL為/logout的請求,實現使用者退出,清除認證資訊。

6.org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter

表單認證操作全靠這個過濾器,預設匹配URL為/login且必須為POST請求

7.org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter

如果沒有在配置檔案中指定認證頁面,則由該過濾器生成一個預設認證頁面。

8.org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter

由此過濾器可以生產一個預設的退出登入頁面

9.org.springframework.security.web.authentication.www.BasicAuthenticationFilter

此過濾器會自動解析HTTP請求中頭部名字為Authentication,且以Basic開頭的頭資訊

10.org.springframework.security.web.savedrequest.RequestCacheAwareFilter

通過HttpSessionRequestCache內部維護了一個RequestCache,用於快取 HttpServletRequest

11. org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter

針對ServletRequest進行了一次包裝,使得request具有更加豐富的API

12.org.springframework.security.web.authentication.AnonymousAuthenticationFilter

當SecurityContextHolder中認證資訊為空,則會建立一個匿名使用者存入到 SecurityContextHolder中。spring security為了相容未登入的訪問,也走了一套認證流程, 只不過是一個匿名的身份。

13. org.springframework.security.web.session.SessionManagementFilter

securityContextRepository限制同一使用者開啟多個會話的數量

14.org.springframework.security.web.access.ExceptionTranslationFilter

異常轉換過濾器位於整個springSecurityFilterChain的後方,用來轉換整個鏈路中出現的異 常

15. org.springframework.security.web.access.intercept.FilterSecurityInterceptor

獲取所配置資源訪問的授權資訊,根據SecurityContextHolder中儲存的使用者資訊來決定其 是否有許可權。

二、認證方式

2.1 HttpBasic認證

HttpBasic登入驗證模式是Spring Security實現登入驗證最簡單的一種方式,也可以說是最簡陋 的一種方式。它的目的並不是保障登入驗證的絕對安全,而是提供一種“防君子不防小人”的登入驗 證。

在使用的Spring Boot早期版本為1.X版本,依賴的Security 4.X版本,那麼就無需任何配置,啟動 專案訪問則會彈出預設的httpbasic認證。現在使用的是spring boot2.0以上版本(依賴Security 5.X版本),HttpBasic不再是預設的驗證模式,在spring security 5.x預設的驗證模式已經是表單 模式。

HttpBasic模式要求傳輸的使用者名稱密碼使用Base64模式進行加密。如果使用者名稱是 "admin" , 密碼是“ admin”,則將字串"admin:admin" 使用Base64編碼演算法加密。加密結果可能是: YWtaW46YWRtaW4=。HttpBasic模式真的是非常簡單又簡陋的驗證模式,Base64的加密演算法是 可逆的,想要破解並不難

2.2 formLogin登入認證模式

Spring Security的HttpBasic模式,該模式比較簡單,只是進行了通過攜帶Http的Header進行 簡單的登入驗證,而且沒有定製的登入頁面,所以使用場景比較窄。對於一個完整的應用系統,與 登入驗證相關的頁面都是高度定製化的,非常美觀而且提供多種登入方式。這就需要Spring Security支援我們自己定製登入頁面, spring boot2.0以上版本(依賴Security 5.X版本)預設會生 成一個登入頁面.

2.3 表單認證

2.3.1 在config包下編寫SecurityConfiguration配置類

/**
 * @author wuzhixuan
 * @version 1.0.0
 * @ClassName SecurityConfiguration.java
 * @Description Security配置類
 * @createTime 2021年11月24日 16:35:00
 */

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /*http.httpBasic() // 開啟base驗證
                .and().authorizeRequests().anyRequest().authenticated(); //所有請求都需要登入認證才能訪問*/
        http.formLogin().loginPage("/toLoginPage") // 開啟表單驗證
                .and().authorizeRequests()
                .antMatchers("/toLoginPage").permitAll() // 放行當前請求
                .anyRequest().authenticated(); //所有請求都需要登入認證才能訪問;
    }
}

重啟服務發現css,js等靜態檔案沒有成功載入

2.3.2 解決靜態資源被攔截問題

@Override
public void configure(WebSecurity web) throws Exception {
    //解決靜態資源被攔截的問題
    web.ignoring().antMatchers("/css/**", "/js/**", "/images/**", "/favicon.ico");
}

Spring Security 中,安全構建器 HttpSecurity 和 WebSecurity 的區別是 :

  • WebSecurity 不僅通過 HttpSecurity 定義某些請求的安全控制,也通過其他方式定義其他某些 請求可以忽略安全控制;
  • HttpSecurity 僅用於定義需要安全控制的請求(當然 HttpSecurity 也可以指定某些請求不需要 安全控制);
  • 可以認為 HttpSecurity 是 WebSecurity 的一部分, WebSecurity 是包含 HttpSecurity 的更大 的一個概念;
  • 構建目標不同
    • WebSecurity 構建目標是整個 Spring Security 安全過濾器 FilterChainProxy`,
    • HttpSecurity 的構建目標僅僅是 FilterChainProxy 中的一個 SecurityFilterChain 。

改造登入

protected void configure(HttpSecurity http) throws Exception {
    /*http.httpBasic() // 開啟base驗證
            .and().authorizeRequests().anyRequest().authenticated(); //所有請求都需要登入認證才能訪問*/
    http.formLogin() // 開啟表單驗證
            .loginPage("/toLoginPage") // 自定義登入頁面
            .loginProcessingUrl("/login") // 登入請求url
            .usernameParameter("username")  // 修改自定義表單name值
            .passwordParameter("password")
            .successForwardUrl("/") // 登入成功跳轉的路徑
            .and().authorizeRequests()
            .antMatchers("/toLoginPage").permitAll() // 放行當前請求
            .anyRequest().authenticated(); //所有請求都需要登入認證才能訪問;
    // 關閉csrf防護
    http.csrf().disable();
    // 允許iframe載入頁面
    http.headers().frameOptions().sameOrigin();
}

發現行內框架iframe這裡出現問題了. Spring Security下,X-Frame-Options預設為DENY,非Spring Security環境下,X-Frame-Options的預設大多也是DENY,這種情況下,瀏覽器拒絕當前頁面載入任何 Frame頁面,設定含義如下:

  • DENY:瀏覽器拒絕當前頁面載入任何Frame頁面 此選擇是預設的.
  • SAMEORIGIN:frame頁面的地址只能為同源域名下的頁面

設定之後看到成功展示了

2.4 基於資料庫實現認證功能

之前我們所使用的使用者名稱和密碼是來源於框架自動生成的, 那麼我們如何實現基於資料庫中的使用者名稱和 密碼功能呢? 要實現這個得需要實現security的一個UserDetailsService介面, 重寫這個接口裡面 loadUserByUsername即可

  • 編寫MyUserDetailsService並實現UserDetailsService介面,重寫loadUserByUsername方法
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException(username);// 使用者名稱沒有找到
        }
        // 先宣告一個許可權集合, 因為構造方法裡面不能傳入null
        Collection<? extends GrantedAuthority> authorities = new ArrayList<>();

        return new org.springframework.security.core.userdetails.User(username,
                "{noop}" + user.getPassword(),// {noop}表示不加密認證
                true, // 使用者是否啟用 true 代表啟用
                true,// 使用者是否過期 true 代表未過期
                true,// 使用者憑據是否過期 true 代表未過期
                true,// 使用者是否鎖定 true 代表未鎖定
                authorities
                );
    }
}
  • 在SecurityConfiguration配置類中指定自定義使用者認證
/**
 * 身份驗證管理器
 * @param auth
 * @throws Exception
 */
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(myUserDetailsService);
}

2.5 密碼加密驗證

在基於資料庫完成使用者登入的過程中,我們所是使用的密碼是明文的,規則是通過對密碼明文新增 {noop} 字首。那麼下面 Spring Security 中的密碼編碼進行一些探討。

Spring Security 中 PasswordEncoder 就是我們對密碼進行編碼的工具介面。該介面只有兩個功能: 一個是匹配驗證。另一個是密碼編碼。

  • BCrypt演算法介紹

BCrypt強雜湊方法 每次加密的結果都不一樣,所以更加的安全。