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


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

一、過濾器鏈介紹

image-20211124155050759

過濾器是一種典型的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的加密算法是 可逆的,想要破解並不難

image-20211124161756023

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(); //所有請求都需要登錄認證才能訪問;
    }
}

image-20211124164629344

重啟服務發現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 。

image-20211124170821308

改造登錄

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頁面的地址只能為同源域名下的頁面

image-20211124171005902

設置之后看到成功展示了

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 就是我們對密碼進行編碼的工具接口。該接口只有兩個功能: 一個是匹配驗證。另一個是密碼編碼。

image-20211124172444242

  • BCrypt算法介紹

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM