springsecurity講解1

SpringSecurity是一個權(quán)限驗證的安裝框架,有身份驗證(用戶名密碼)和用戶授權(quán)(權(quán)限)等功能.

快速上手

SpringSecurity在整合springboot的時候非常簡單,只在maven文件中引入jar即可
pom.xml

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

在日志中有密碼,在訪問restfull接口的時候,需要輸入用戶名和密碼,
密碼是在日志中提示的,用戶名是user

Using generated security password: afdda172-b982-40aa-9552-18560c8e8ecc
進階

快速上手是springsecurity默認的,如何定制化呢
需要實現(xiàn)WebSecurityConfigurerAdapter,首先實現(xiàn)類要用@Configuration
和@EnableWebSecurity標注
重寫兩個方法configure(HttpSecurity http)配置規(guī)則和configure(AuthenticationManagerBuilder auth)身份認證,從內(nèi)存或者數(shù)據(jù)庫中獲取用戶信息,configure(WebSecurity web)可以規(guī)定忽略哪些路徑

@Configuration
@EnableWebSecurity
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception{
        http
                .authorizeRequests()
                .antMatchers("/hello").permitAll() // 對于hello路徑放行
                .anyRequest().authenticated()
                .and()
                .formLogin().and()
        ;  //瀏覽器以form表單形式

    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        // 用戶信息存儲在內(nèi)存中
        auth.inMemoryAuthentication().withUser("user")
            .password(new BCryptPasswordEncoder().encode("1234")).authorities("ADMIN");
    }

    @Override
    public void configure(WebSecurity web) throws Exception{
//忽略/world路徑
        web.ignoring().antMatchers("/world");
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        // 官網(wǎng)建議的加密方式,相同的密碼,每次加密都不一樣,安全性更好一點
        return new BCryptPasswordEncoder();
    }

上面的示例,在內(nèi)存中生成了用戶,使用form表單的方式,放行了 hello和world路徑的訪問

filter

那springsecurity是如何工作的,就是依托于servlet的filter調(diào)用鏈,加上springsecurity的filter,這個filter中又維護了一個filter鏈,springsecurity終止自己的調(diào)用鏈后,繼續(xù)執(zhí)行servlet的filter
org.springframework.security.web.FilterChainProxy.VirtualFilterChain#doFilter看這個方法就能理解security的filter的大致工作原理,currentPosition為security的filter自己調(diào)用鏈的數(shù)量,如果都執(zhí)行完,就繼續(xù)執(zhí)行servlet的filter
springsecurit主要的filter為
--UsernamePasswordAuthenticationFilter 校驗用戶名密碼,主要的作用為驗證用戶名密碼,如果正確則退出springsecurity自己的過濾器鏈.
--ExceptionTranslationFilter 如果FilterSecurityInterceptor權(quán)限不通過,重定向到登入頁
--FilterSecurityInterceptor 校驗權(quán)限等
當然不止這三個,所有filter之間的order為100,目的是為了可以將自定義的filter放到容器中并指定順序,
一般都是放到UsernamePasswordAuthenticationFilter 前后,加入自己的filter之后,訪問都會執(zhí)行整個調(diào)用鏈,也就都會訪問自己添加的filter,所以需要考慮加判斷,不滿足條件直接跳過,執(zhí)行下一個filter,獲取或者終止調(diào)用鏈,直接跳過當前filter就是執(zhí)行chain.doFilter(request, response);終止就是不執(zhí)行
chain.doFilter(request, response);那調(diào)用鏈就結(jié)束了.如果要深入了解可以關(guān)注
org.springframework.security.web.FilterChainProxy.VirtualFilterChain#doFilter
這里分析UsernamePasswordAuthenticationFilter ,因為我們自定義的filter都是遵循
UsernamePasswordAuthenticationFilter 的模式,比如想做一個手機驗證.
UsernamePasswordAuthenticationFilter 是如何工作的,這里就不詳細分析每一個細節(jié)了,大概分析思路,就可以擴展了.
UsernamePasswordAuthenticationFilter 是AbstractAuthenticationProcessingFilter的子類,在構(gòu)造方法中初始化了AntPathRequestMatcher


image.png

意思是我只對login路徑,方法為post進行攔截,自定義自己的filter的時候,就可以模仿這個構(gòu)造器
doFilter方法在父類AbstractAuthenticationProcessingFilter中,可以看到調(diào)用了
attemptAuthentication方法,

authResult = attemptAuthentication(request, response);

UsernamePasswordAuthenticationFilter 的attemptAuthentication方法的返回值

return this.getAuthenticationManager().authenticate(authRequest);

this.getAuthenticationManager()返回的是ProviderManager,那看一下authenticate
方法中的for循環(huán)

for (AuthenticationProvider provider : getProviders())

for循環(huán)里關(guān)注兩個代碼,
if (!provider.supports(toTest))和result = provider.authenticate(authentication);
toTest是放進來的Token,這里指的是UsernamePasswordAuthenticationToken,
如果想要自定義,比如手機驗證使用自定義mobileAuthenticationToken,
還需要提供自己的provider,因為getProviders()返回的是provider集合,那如何知道使用哪個provider,就是通過if (!provider.supports(toTest))
舉例手機驗證就能清晰一點
MobileAuthenticationToken 直接復(fù)制UsernamePasswordAuthenticationToken代碼,改一下類名就可以,
MobileAuthenticationProvider繼承AuthenticationProvider,實現(xiàn)兩個方法
authenticate(Authentication authentication)和supports(Class<?> authentication)
這兩個方法就對應(yīng)上面說的for循環(huán)中的兩行代碼
authenticate(Authentication authentication)方法中,驗證手機驗證碼是否正確,返回MobileAuthenticationToken 中this.authenticated要復(fù)制為true,證明已經(jīng)驗證成功了,這個非常重要,
創(chuàng)建MobileAuthenticationFilter,復(fù)制UsernamePasswordAuthenticationFilter就好,簡單改一下就可以,構(gòu)造方法中創(chuàng)建AntPathRequestMatcher,攔截路徑/mobile/form,方法POST,那如何將自定義的filter放到springsecurity的調(diào)用鏈呢,最開始說的WebSecurityConfigurerAdapter子類重寫的configure(HttpSecurity http)方法中
另一個要考慮的問題就是,登入之后其他路徑是怎么被放行的呢,先講解通過session的方式


image.png

這就是登入成功之后,可以被訪問的原因,這是其中一個filter,SecurityContextPersistenceFilter,
通過sessionid從內(nèi)存中獲取MobileAuthenticationToken ,并放到SecurityContextHolder中,是ThreadLocal類型的變量,該線程就可以使用了,會在springsecurity的最后一個filter(FilterSecurityInterceptor),從SecurityContextHolder獲取MobileAuthenticationToken,并判斷
token中的authenticated是否為true,如果是則證明已經(jīng)認證過,當然還有判斷是否有訪問該接口的權(quán)限,就不展開了.
說回UsernamePasswordAuthenticationToken,

流程就是先調(diào)用父類的AbstractAuthenticationProcessingFilter的doFilter
-->authResult = attemptAuthentication(request, response);
UsernamePasswordAuthenticationFilter#attemptAuthentication
-->this.getAuthenticationManager().authenticate(authRequest);
在for循環(huán)中調(diào)用
result = provider.authenticate(authentication);
UsernamePasswordAuthenticationFilter在調(diào)用attemptAuthentication方法的時候,使用UsernamePasswordAuthenticationToken,在for循環(huán)里調(diào)用provider的support方法,判斷應(yīng)該使用DaoAuthenticationProvider,provider.authenticate(authentication)方法其實是父類的authenticate方法,authenticate作用是通過調(diào)用DaoAuthenticationProvider#retrieveUser方法,獲取用戶信息,并將用戶密碼和前端輸入的密碼比較,如果成功之后,要返回UsernamePasswordAuthenticationToken,并將authenticated要為true,證明已經(jīng)驗證過了,這個在自定義filter的時候非常重要.
filter中還有認證成功處理器和認證失敗處理器


image.png

如果是前后端分離的,就response寫入信息就好了.
剛提到的DaoAuthenticationProvider#retrieveUser方法,獲取用戶信息,這也是一個擴展點,這種源碼用到了成員變量,基本就是可以擴展的地方,流程就是默認給一個值,也可以程序員手動的賦值.

UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);

這里說的成員變量,就是 this.getUserDetailsService()的userDetailsService
創(chuàng)建一個類實現(xiàn)UserDetailsService,重寫loadUserByUsername方法,該方法從數(shù)據(jù)庫里獲取用戶信息,并封裝為UserDetails返回即可.
流程就是這樣,下篇文章實戰(zhàn),對上面說的能有個更好的理解.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市周崭,隨后出現(xiàn)的幾起案子绘迁,更是在濱河造成了極大的恐慌修噪,老刑警劉巖巡验,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辫封,死亡現(xiàn)場離奇詭異砖第,居然都是意外死亡荷腊,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門悄窃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哮肚,“玉大人,你說我怎么就攤上這事广匙≡侍耍” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵鸦致,是天一觀的道長潮剪。 經(jīng)常有香客問我,道長分唾,這世上最難降的妖魔是什么抗碰? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮绽乔,結(jié)果婚禮上弧蝇,老公的妹妹穿的比我還像新娘。我一直安慰自己折砸,他們只是感情好看疗,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著睦授,像睡著了一般两芳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上去枷,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天怖辆,我揣著相機與錄音,去河邊找鬼删顶。 笑死竖螃,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的逗余。 我是一名探鬼主播特咆,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼猎荠!你這毒婦竟也來了坚弱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤关摇,失蹤者是張志新(化名)和其女友劉穎荒叶,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體输虱,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡些楣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片愁茁。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蚕钦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鹅很,到底是詐尸還是另有隱情嘶居,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布促煮,位于F島的核電站邮屁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏菠齿。R本人自食惡果不足惜佑吝,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望绳匀。 院中可真熱鬧芋忿,春花似錦、人聲如沸疾棵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽陋桂。三九已至逆趣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嗜历,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工抖所, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留梨州,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓田轧,卻偏偏與公主長得像暴匠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子傻粘,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容