參考博客: http://www.javashuo.com/article/p-tnxxjbfy-mr.htmlhtml
Spring Security 是spring項目之中的一個安全模塊,能夠很是方便與spring項目無縫集成。web
特別是在spring boot項目中加入spring security更是十分簡單, 這裏就簡單介紹一下入門案例和使用原理吧!spring
寫一個簡單的springboot測試例子:數據庫
@Controller public class AppController { @RequestMapping("/hello") @ResponseBody String home() { return "Hello ,spring security!"; } }
在springboot中的pom.xml中引入springsecurity, 加入以下代碼:緩存
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
而後在配置文件中寫入 安全
security.basic.enabled=false
spring security 默認提供了表單登陸的功能。咱們新建一個類SecurityConfiguration
,並加入一些代碼,以下所示:springboot
@Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin().and() .httpBasic(); } }
上面的代碼其實就是 一種配置,authorizeRequests() 定義哪些URL須要被保護、哪些不須要被保護。 formLogin() 定義當須要用戶登陸時候,轉到的登陸頁面。服務器
此時,咱們並無寫登陸頁面,可是spring security默認提供了一個登陸頁面,以及登陸控制器。咱們這裏簡單寫一個自定義的登陸頁面:app
<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'> <h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'> <table> <tr><td>User:</td><td><input type='text' name='username' value=''></td></tr> <tr><td>Password:</td><td><input type='password' name='password'/></td></tr> <tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr> <input name="_csrf" type="hidden" value="635780a5-6853-4fcd-ba14-77db85dbd8bd" /> </table> </form></body></html>
咱們能夠發現,這裏有個form 。action="/login"
,這個/login
依然是spring security
提供的。form表單提交了三個數據:框架
再配置文件加入以下:
security.user.name=admin
security.user.password=admin
重啓項目,訪問被保護的/hello頁面。自動跳轉到了spring security 默認的登陸頁面,咱們輸入用戶名admin密碼admin。點擊Login
按鈕。會發現登陸成功並跳轉到了/hello。
除了登陸,spring security還提供了rememberMe功能,這裏不作過多解釋
一般狀況下,咱們須要實現「特定資源只能由特定角色訪問」的功能。假設咱們的系統有以下兩個角色:
如今咱們給系統增長「/product」 表明商品信息方面的資源(USER能夠訪問);增長"/admin"代碼管理員方面的資源(USER不能訪問)。代碼以下:
@Controller @RequestMapping("/product") public class ProductTestController { @RequestMapping("/info") @ResponseBody public String productInfo(){ return " some product info "; } } ------------------------------------------- @Controller @RequestMapping("/admin") public class AdminTestController { @RequestMapping("/home") @ResponseBody public String productInfo(){ return " admin home page "; } }
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser("admin1") // 管理員,同事具備 ADMIN,USER權限,能夠訪問全部資源 .password("admin1") .roles("ADMIN", "USER") .and() .withUser("user1").password("user1") // 普通用戶,只能訪問 /product/** .roles("USER"); } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/product/**").hasRole("USER") .antMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated() .and() .formLogin().and() .httpBasic(); }
經過上面的配置, 能夠簡單實現一個springsecurity的小例子. 下面說一下springsecurity的原理:
spring security核心組件有:SecurityContext、SecurityContextHolder、Authentication、Userdetails 和 AuthenticationManager,下面分別介紹。
SecurityContext
安全上下文,用戶經過Spring Security 的校驗以後,驗證信息存儲在SecurityContext中,SecurityContext的接口定義以下:
public interface SecurityContext extends Serializable { /** * Obtains the currently authenticated principal, or an authentication request token. * * @return the <code>Authentication</code> or <code>null</code> if no authentication * information is available */ Authentication getAuthentication(); /** * Changes the currently authenticated principal, or removes the authentication * information. * * @param authentication the new <code>Authentication</code> token, or * <code>null</code> if no further authentication information should be stored */ void setAuthentication(Authentication authentication); }
SecurityContextHolder看名知義,是一個holder,用來hold住SecurityContext實例的。其做用就是存儲當前認證信息。
在典型的web應用程序中,用戶登陸一次,而後由其會話ID標識。服務器緩存持續時間會話的主體信息。
在Spring Security中,在請求之間存儲SecurityContext的責任落在SecurityContextPersistenceFilter上,
默認狀況下,該上下文將上下文存儲爲HTTP請求之間的HttpSession屬性。
它會爲每一個請求恢復上下文SecurityContextHolder,而且最重要的是,在請求完成時清除SecurityContextHolder。
SecurityContextHolder是一個類,他的功能方法都是靜態的(static)。 SecurityContextHolder能夠設置指定JVM策略(SecurityContext的存儲策略),這個策略有三種: 1. MODE_THREADLOCAL:SecurityContext 存儲在線程中。 2. MODE_INHERITABLETHREADLOCAL:SecurityContext 存儲在線程中,但子線程能夠獲取到父線程中的 SecurityContext。 3. MODE_GLOBAL:SecurityContext 在全部線程中都相同。 SecurityContextHolder默認使用MODE_THREADLOCAL模式,即存儲在當前線程中。在spring security應用中,咱們一般能看到相似以下的代碼: SecurityContextHolder.getContext().setAuthentication(token);
authentication 直譯過來是「認證」的意思,在Spring Security 中Authentication用來表示當前用戶是誰,通常來說你能夠理解爲authentication就是一組用戶名密碼信息。Authentication也是一個接口,其定義以下:
public interface Authentication extends Principal, Serializable { Collection<? extends GrantedAuthority> getAuthorities(); Object getCredentials(); Object getDetails(); Object getPrincipal(); boolean isAuthenticated(); void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException; }
接口有4個get方法,分別獲取
Authorities
, 填充的是用戶角色信息。 Credentials
,直譯,證書。填充的是密碼。 Details
,用戶信息。 Principal
直譯,形容詞是「主要的,最重要的」,名詞是「負責人,資本,本金」。感受很彆扭,因此,仍是不翻譯了,直接用原詞principal來表示這個概念,其填充的是用戶名。所以能夠推斷其實現類有這4個屬性。這幾個方法做用以下:
getAuthorities
: 獲取用戶權限,通常狀況下獲取到的是用戶的角色信息。 getCredentials
: 獲取證實用戶認證的信息,一般狀況下獲取到的是密碼等信息。 getDetails
: 獲取用戶的額外信息,(這部分信息能夠是咱們的用戶表中的信息) getPrincipal
: 獲取用戶身份信息,在未認證的狀況下獲取到的是用戶名,在已認證的狀況下獲取到的是 UserDetails (UserDetails也是一個接口,裏邊的方法有getUsername,getPassword等)。 isAuthenticated
: 獲取當前 Authentication 是否已認證。 setAuthenticated
: 設置當前 Authentication 是否已認證(true or false)。UserDetails,看命知義,是用戶信息的意思。其存儲的就是用戶信息,其定義以下:
public interface UserDetails extends Serializable { Collection<? extends GrantedAuthority> getAuthorities(); String getPassword(); String getUsername(); boolean isAccountNonExpired(); boolean isAccountNonLocked(); boolean isCredentialsNonExpired(); boolean isEnabled(); }
方法含義以下:
getAuthorites
:獲取用戶權限,本質上是用戶的角色信息。 getPassword
: 獲取密碼。 getUserName
: 獲取用戶名。 isAccountNonExpired
: 帳戶是否過時。 isAccountNonLocked
: 帳戶是否被鎖定。 isCredentialsNonExpired
: 密碼是否過時。 isEnabled
: 帳戶是否可用。提到了UserDetails
就必須得提到UserDetailsService
, UserDetailsService也是一個接口,且只有一個方法loadUserByUsername
,他能夠用來獲取UserDetails。
一般在spring security應用中,咱們會自定義一個CustomUserDetailsService來實現UserDetailsService接口,並實現其public UserDetails loadUserByUsername(final String login);
方法。
咱們在實現loadUserByUsername
方法的時候,就能夠經過查詢數據庫(或者是緩存、或者是其餘的存儲形式)來獲取用戶信息,而後組裝成一個UserDetails
,
(一般是一個org.springframework.security.core.userdetails.User
,它繼承自UserDetails) 並返回。
在實現loadUserByUsername
方法的時候,若是咱們經過查庫沒有查到相關記錄,須要拋出一個異常來告訴spring security來「善後」。
這個異常是org.springframework.security.core.userdetails.UsernameNotFoundException
。
AuthenticationManager 是一個接口,它只有一個方法,接收參數爲Authentication
,其定義以下:
public interface AuthenticationManager { Authentication authenticate(Authentication authentication) throws AuthenticationException; }
AuthenticationManager 的做用就是校驗Authentication
,若是驗證失敗會拋出AuthenticationException
異常。AuthenticationException
是一個抽象類,
所以代碼邏輯並不能實例化一個AuthenticationException異常並拋出,實際上拋出的異常一般是其實現類,如DisabledException
,LockedException
,BadCredentialsException
等。
BadCredentialsException
可能會比較常見,即密碼錯誤的時候。
總結:
SpringSecurity是個很是不錯的框架,是個顆粒級別的驗證框架,和springboot融合的很是完美, 值得學習。
http://www.javashuo.com/article/p-tnxxjbfy-mr.html 這篇博客寫的很是清楚, 把原理分析的比較透徹, 能夠接着學習底層的東西多一點。