Spring Security 是爲基於 Spring 的應用程序提供聲明式安全保護的安全性框架。 Spring Security 提供了完整的安全性解決方案,它可以在 Web 請
求級別和方法調用級別處理身份認證和受權。由於基於 Spring 框架,因此 Spring Security 充分利用了依賴注入( dependency injection , DI )和
面向切面的技術。java
在spring mvc中使用,至少須要如下幾個模塊web
spring-security-core,spring-security-config,spring-security-web。spring
在web.xml中配置數據庫
使用java類形式的配置瀏覽器
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class WebAppSecurityInitializer extends AbstractSecurityWebApplicationInitializer { }
由於這裏咱們使用spring mvc程序使用的,因此只須要新建一個類,繼承AbstractSecurityWebApplicationInitializer,以後什麼都不用幹。安全
咱們仍是一樣在配置類中配置,這裏不介紹xml配置方式,主要有幾點服務器
1)、若是要配置安全性必需要實現WebSecurityConfigurer,可是這裏咱們直接繼承它的一個實現類更加簡單WebSecurityConfigurerAdapter。cookie
2)、咱們仍是和配置spring mvc的策略同樣,使用帶有Configuration註解的配置類來實現WebSecurityConfigurerAdapter,而且在配置類上還要加入EnableWebSecurity註解,這裏咱們暫定這個配置類的名字叫SecurityConfig。mvc
3)、將SecurityConfig配置到getRootConfigClasses方法中,這個方法在哪就不須要介紹了吧。框架
例如:
import ch09.dao.UserDao; import ch09.security.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired UserDao userDao; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(new UserRepository(userDao)); } @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin().loginPage("/login").and() .logout().logoutSuccessUrl("/").and() .csrf().disable() .httpBasic().and() .rememberMe().and() .authorizeRequests() .antMatchers("/").authenticated() .anyRequest().permitAll(); } }
對於這個類的配置大體分爲3個部分的配置:
1)、filter鏈配置
2)、保護請求
3)、如何驗證
主要是配置什麼呢?配置登陸用戶到底從哪裏去找,到底怎麼找。這裏簡單提一下有幾種方式能夠直接從內存中找,也能夠到數據庫中找,也能夠採用LDAP服務器,固然也能夠自定以,這裏咱們主要介紹一下自定義的方式,若是採用前面幾種方式對你的數據庫或者LDAP服務器都是要有一些特定要求的,好比表名之類的。
咱們須要提供一個自定義
的 UserDetailsService 接口實現
import ch09.dao.UserDao; import ch09.domain.User; import ch09.security.model.SimpleUserDetails; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @Service public class UserRepository implements UserDetailsService { private UserDao userDao; public UserRepository(UserDao userDao){ this.userDao = userDao; } public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { User user = userDao.getUserByUserName(s); return new SimpleUserDetails(user); } }
有意思的是,無論你數據來自於何方,你只要提供UserDetails的實現就能夠了,來接下來讓咱們看看UserDetails有什麼,若是你只須要登陸的話,也許你只要關注密碼和用戶名就好了。
import ch09.domain.User; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.ArrayList; import java.util.Collection; import java.util.List; public class SimpleUserDetails implements UserDetails { private User user; public SimpleUserDetails(User user){ this.user = user; } //權限列表 public Collection<? extends GrantedAuthority> getAuthorities() { List<GrantedAuthority> grantedAuthorityList = new ArrayList<GrantedAuthority>(); grantedAuthorityList.add(new SimpleGrantedAuthority("ADMIN")); return grantedAuthorityList; } //獲取密碼 public String getPassword() { return user.getUserPassword(); } //獲取用戶名 public String getUsername() { return user.getUserName(); } //帳戶未過時 public boolean isAccountNonExpired() { return true; } //帳戶未鎖定 public boolean isAccountNonLocked() { return true; } //證書未過時 public boolean isCredentialsNonExpired() { return true; } //帳戶啓用 public boolean isEnabled() { return true; } //自定義方法 public User getUser() { return user; } }
來看咱們如何使用UserDetailsService的實現類吧:
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(new UserRepository(userDao)); }
不過有件事情不得不提一下,通常數據庫的用戶密碼是不會存儲名文的,爲了安全起見都會採用密文,加密方式可能不同,可是爲了安全仍是加密一下再存比較好,這樣的話咱們就須要配置個密碼轉碼器,密碼轉碼器會把前臺輸入的密碼加密後和數據庫的密文進行比對,如何配置呢,如圖
passwordEncoder() 方法能夠接受 Spring Security 中 PasswordEncoder 接口的任意實現。 Spring Security 的加密模塊包括了三個這樣的實
現: BCryptPasswordEncoder 、 NoOpPasswordEncoder 和 StandardPasswordEncoder 。
先看一下下面簡單的配置,在講怎麼配置
@Override protected void configure(HttpSecurity http) throws Exception { http.formLogin().loginPage("/login").and() .logout().logoutSuccessUrl("/").and() .csrf().disable() .httpBasic().and() .rememberMe().and() .authorizeRequests() .antMatchers("/").authenticated() .anyRequest().permitAll(); }
配置的話分爲幾個方面:配置路徑、強制安全通道https、跨站請求僞造、登陸頁(記住密碼、退出)、 HTTP Basic 認證。
須要注意的是,咱們可使用 Spring 表達式進行安全保護
簡單來說,若是一個站點欺騙用戶提交請求到其餘服務器的話,就
會發生 CSRF 攻擊,這可能會帶來消極的後果。
從 Spring Security 3.2 開始,默認就會啓用 CSRF 防禦。
Spring Security 經過一個同步 token 的方式來實現 CSRF 防禦的功能。它將會攔截狀態變化的請求(例如,
非 GET 、 HEAD 、 OPTIONS 和 TRACE 的請求)並檢查 CSRF token 。若是請求中不包含 CSRF token 的話,或者 token 不能與服務器端的 token 相
匹配,請求將會失敗,並拋出 CsrfException 異常。
這意味着在你的應用中,全部的表單必須在一個 「_csrf」 域中提交 token ,並且這個 token 必需要與服務器端計算並存儲的 token 一致,這樣的話
當表單提交的時候,才能進行匹配。
Spring Security 已經簡化了將 token 放到請求的屬性中這一任務。若是你使用 Thymeleaf 做爲頁面模板的話,只要 <form> 標籤
的 action 屬性添加了 Thymeleaf 命名空間前綴,那麼就會自動生成一個 「_csrf」 隱藏域:
若是是jsp的話,使用 Spring 的表單綁定標籤的話, <sf:form> 標籤會自動爲咱們添加隱藏的 CSRF token 標籤。
處理 CSRF 的另一種方式就是根本不去處理它。咱們能夠在配置中經過調用 csrf().disable() 禁用 Spring Security 的 CSRF 防禦功能。
須要特別說明的一點是,若是開啓,使用默認的退出功能時須要post提交且待csrf域才能註銷。
spring security集成csrf
進行post等請求時,爲了防止csrf攻擊,須要獲取token才能訪問
所以須要添加
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
若是你使用程序清單 9.1 中最簡單的 Spring Security 配置的話,那麼就能無償地獲得一個登陸頁。實際上,在重
寫 configure(HttpSecurity) 以前,咱們都能使用一個簡單卻功能完備的登陸頁。可是,一旦重寫了 configure(HttpSecurity) 方
法,就失去了這個簡單的登陸頁面。
不過,把這個功能找回來也很容易。咱們所須要作的就是在 configure(HttpSecurity) 方法中,調用 formLogin()。
如何自定義登陸頁你只須要
http.formLogin().loginPage("/login")
可是須要注意的是,在你定義的頁面中用戶名的域須要命名爲username,密碼的域須要命名爲password,提交方式post,提交地址/login。用thymeleaf模板的寫法是"@{/login}"。
也許有時咱們須要記住密碼的功能,在配置文件裏咱們須要以下配置
默認狀況下,這個功能是經過在 cookie 中存儲一個 token 完成的,這個 token
最多兩週內有效。可是,在這裏,咱們指定這個 token 最多四周內有效( 2,419,200 秒)。
存儲在 cookie 中的 token 包含用戶名、密碼、過時時間和一個私鑰 —— 在寫入 cookie 前都進行了 MD5 哈希。默認狀況下,私鑰的名
爲 SpringSecured ,但在這裏咱們將其設置爲 spitterKey ,使它專門用於 Spittr 應用。
爲了實現這一點,登陸請
求必須包含一個名爲 remember-me 的參數。在登陸表單中,增長一個簡單複選框就能夠完成這件事情:
固然登陸,咱們還須要註銷
退出功能是經過 Servlet 容器中的 Filter 實現的(默認狀況下),這個 Filter 會攔截針對 「/logout」 的請求。所以,爲應用添加退出功能只需添加以下
的連接便可(以下以 Thymeleaf 代碼片斷的形式進行了展示):
對於應用程序的人類用戶來講,基於表單的認證是比較理想的。可是在第 16 章中,將會看到如何將咱們 Web 應用的頁面轉化爲 RESTful API 。
當應用程序的使用者是另一個應用程序的話,使用表單來提示登陸的方式就不太適合了。
HTTP Basic 認證( HTTP Basic Authentication )會直接經過 HTTP 請求自己,對要訪問應用程序的用戶進行認證。你可能在之前見過 HTTP
Basic 認證。當在 Web 瀏覽器中使用時,它將向用戶彈出一個簡單的模態對話框。
若是要啓用 HTTP Basic 認證的話,只需在 configure() 方法所傳入的 HttpSecurity 對象上調用 httpBasic() 便可。另外,還能夠經過
調用 realmName() 方法指定域。以下是在 Spring Security 中啓用 HTTP Basic 認證的典型配置:
public SimpleUserDetails getSimpleUserDetails(){ Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); if(principal instanceof UserDetails){ return (SimpleUserDetails)principal; } return null; }
前面都是如何保護代碼,如今看看如何在視圖中使用權限吧,這裏咱們只介紹thymeleaf,先看看官網的模塊圖
固然要想再頁面中使用sping security須要導入相應的包。