Hi 大家好 今天我將和大家分享一下我在做安全框架遇到的難題以及如果正確使用spring security以及Spring Security OAuth2 Client 。
本文將講述一下幾點(diǎn):
1 為什么需要Spring Security?
2 如何將 Spring Security與Spring Boot結(jié)合?
3 如何使用Spring Security進(jìn)行認(rèn)證,授權(quán)流程的登錄驗(yàn)證?
1 因?yàn)镾pirng Security 可以解決Session 固化問(wèn)題,如果沒(méi)有安全框架,會(huì)有cookie攻擊的問(wèn)題,黑客很容易就可以拿到一個(gè)用戶的sessionId(默認(rèn)是一個(gè)名為JSSIONID的cookie的值即為sessionId),然后偽裝登錄。僅僅通過(guò)配置就可以打開(kāi)唯一登錄功能(shiro沒(méi)有唯一登錄的機(jī)制,我以前是自己開(kāi)發(fā),結(jié)合攔截器 以及 redis實(shí)現(xiàn)了唯一登錄功能),還有csrf機(jī)制(后續(xù)和大家分享csrf)。
2 Spring Boot的眾多好處中的一個(gè)好處就是用java config代替 xml的配置,這樣無(wú)疑性能會(huì)更高一些。然而大家第一次接觸java config,會(huì)很陌生。其實(shí)java config很簡(jiǎn)單的,從表面上看就是將xml的標(biāo)簽用@Bean注解代替。Spring Boot 有更多的注解解決程序問(wèn)題,例如@EnableAsync和@Async注解,能實(shí)現(xiàn)方法的異步。等等(如果大家有興趣,后續(xù)會(huì)和大家分享Spring Boot)。
官網(wǎng)有相關(guān)的demo項(xiàng)目,可以看到Spring Security與Spring Boot的結(jié)合。
https://spring.io/guides/tutorials/spring-security-and-angular-js/
然而官網(wǎng)的demo項(xiàng)目,僅僅是演示了Spring Security框架可以解決什么企業(yè)問(wèn)題:如 安全登錄,權(quán)限控制,然而并不能拿到企業(yè)當(dāng)中來(lái)用。接下來(lái),我將和大家分享最關(guān)鍵也是最核心的內(nèi)容就是Spring Security如何在企業(yè)當(dāng)中解決安全認(rèn)證(我使用了md5加密來(lái)作為密碼加密器),權(quán)限控制。
3 首先是jar包依賴,我使用的是maven項(xiàng)目,需要添加maven依賴:
org.springframework.bootspring-boot-starter-security
下面仔細(xì)說(shuō)明一下Spring Security工作的核心類信息:
HttpSecurity類:進(jìn)行權(quán)限的配置,具體哪里路徑可以直接放行.antMatchers(...).permitAll()哪些路徑需要權(quán)限認(rèn)證 .antMatchers(...).hasAutority(...)? 僅測(cè)試需要.anyRequest().authenticated()配置登錄頁(yè)面,登錄錯(cuò)誤頁(yè)面 以及安全退出,安全退出頁(yè)面,記住我等等。
FilterInvocationSecurityMetadataSource:該類為過(guò)濾器類,容器啟動(dòng)就會(huì)加載該類進(jìn)行資源 角色的加載。需要自定義該類的實(shí)現(xiàn)類,自定義init方法,從數(shù)據(jù)庫(kù)加載該程序需要的全部的權(quán)限信息。然后每次請(qǐng)求都回走getAttributes,加載該url需要的角色列表。AccessDecisionManager:該接口 的decide方法即為權(quán)限審核接口,判斷當(dāng)前用戶有沒(méi)有訪問(wèn)該url的權(quán)限
UserDetailsService: 用戶登錄真正工作類,需要自定義實(shí)現(xiàn)類實(shí)現(xiàn)該接口,實(shí)現(xiàn)loadUserByUserNameUserDetails:用戶信息實(shí)體類接口,自定義的javaBean需要實(shí)現(xiàn)該類。下面是我的部分的java config代碼,供大家參考。
```@Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled = true)@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception?
{http.antMatcher("/**").authorizeRequests().antMatchers( "/se-home","/se-public/**","/login**", "/webjars/**","/js/**","/myexcel","/upload*").permitAll().antMatchers("/se/**").hasAuthority("ROLE_USER").anyRequest().fullyAuthenticated().and().formLogin().loginPage("/se-goLogin").failureUrl("/se-goLogin?error").loginProcessingUrl("/se-login").defaultSuccessUrl("/se/se-hello").permitAll().and().logout().logoutSuccessUrl("/se-home").permitAll().invalidateHttpSession(true).and().rememberMe();}@Autowiredpublic void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(getCustomUserDetailsServiceImpl())//自定義開(kāi)發(fā)的UserDeatilsService.passwordEncoder(getMD5Encoder());//自定義開(kāi)發(fā)的密碼加密器}``
`自定義的類都需要通過(guò)@Bean注解的形式交給Spring Boot來(lái)管理,如下獲取Spring Security的過(guò)濾器代理的類配置:``
`@Beanpublic DelegatingFilterProxy getDelegatingFilterProxy(){FilterRegistrationBean registrationBean=new FilterRegistrationBean();DelegatingFilterProxy delegatingFilterProxy=new DelegatingFilterProxy();delegatingFilterProxy.setTargetBeanName(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);registrationBean.setFilter(delegatingFilterProxy);registrationBean.setName(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);registrationBean.addUrlPatterns("/*");registrationBean.setOrder(1);return delegatingFilterProxy;}``
`上面的那個(gè)bean是有一定難度的配置,我自己琢磨了好久,參考了大量的資料,才琢磨出來(lái)。其他的配置相對(duì)比較簡(jiǎn)單大家可以自己動(dòng)手,來(lái)做,其實(shí)在做的過(guò)程中,大家的能力就在不知不覺(jué)中有了很大的提升。具體的我就不貼了喲,相信大家可以的。
做好了SpirngBoot與Spring Security的整合,接下來(lái)就是接著上一篇博客繼續(xù)說(shuō)明Spring Security 與Spring Boot結(jié)合后的使用框架原理流程:
1 容器啟動(dòng),spring security 從庫(kù)里頭加載權(quán)限信息 并以map的形式存取(HashMap? resourceMap key:資源url,value:角色名稱 ROLE_為前綴的值)
2 用戶進(jìn)行登錄操作:加載用戶的角色列表(Collection)loadUserByUsername
3 決策當(dāng)前用戶有沒(méi)有訪問(wèn)該url的權(quán)限decide從兩個(gè)集合中找到相互匹配equals的角色名稱,就放行然后就是對(duì)相應(yīng)的需要自定義的開(kāi)發(fā),只要流程搞明白了 并且知道哪些是需要自定義的開(kāi)發(fā),接下來(lái)的開(kāi)發(fā)的工作就比較簡(jiǎn)單了。
我是用google java style (http://www.hawstein.com/posts/google-java-style.html ) 來(lái)規(guī)范了自己的代碼,大家也可以這樣,養(yǎng)成一個(gè)良好的開(kāi)發(fā)習(xí)慣,很重要 會(huì)避免很多彎路。