# Spring Security ## 1. Spring Security 概述 Spring Security是一個能夠為基於Spring的企業應用系統提供聲明式的安全訪問控制解決方案的安全框架。 應用程序安全性的兩個主要領域是 + 身份認證(authentication):“認證”是建立主體 (principal)的過程。主體 通常是指可以在您的應用程序中執行操作的用戶、設備或其他系統; + 授權(authorization):或稱為“訪問控制(access-control),“授權”是指決定是否允許主體在應用程序中執行操作。為了到達需要授權決定的點,認證過程已經建立了主體的身份。這些概念是常見的,並不是特定於 Spring Security。 **使用Maven** Spring Boot ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> ``` SpringMVC ```xml <!-- Spring Security Artifacts - START --> <!-- http://mvnrepository.com/artifact/org.springframework.security/spring-security-web --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>4.0.4.RELEASE</version> </dependency> <!-- http://mvnrepository.com/artifact/org.springframework.security/spring-security-config --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>4.0.4.RELEASE</version> </dependency> <!-- http://mvnrepository.com/artifact/org.springframework.security/spring-security-taglibs --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>4.0.4.RELEASE</version> </dependency> ``` ## 2. Spring Security相關類 ### 相關接口和類 - UserDetails 接口:作用是 Provides core user information. 裏面有一些主要的方法,如:`String getPassword()`; 和 `String getUsername()`; - User 類, 實現了 UserDetails 接口 :特指org.springframework.security.core.userdetails 包中的 User 類。 主要作用是包含了一些用戶的信息,比如說有成員變量 `private String password`; 和 `private final String username`; 等。 - UserDetailsService 接口:作用是 Core interface which loads user-specific data. 該接口只有一個方法,用於返回用戶的信息: `UserDetails loadUserByUsername(String username) throws UsernameNotFoundException`;,那麽,它的實現類有 CachingUserDetailsService 和 JdbcDaoImpl,一個用於從內存中拿到用戶信息,一個用於從 數據庫中拿到用戶信息。 ### 相關註解 @Enablewebsecurity,官方註釋如下: > * Add this annotation to an {@code @Configuration} class to have the Spring Security > * configuration defined in any {@link WebSecurityConfigurer} or more likely by extending > * the {@link WebSecurityConfigurerAdapter} base class and overriding individual methods: 也就是說, **該註解和 @configuration 註解一起使用,然後,再定義 WebSecurityConfigurer 類型的類或者 繼承 WebSecurityConfigurerAdapter類就構成了 Spring Security 的配置。** 這樣,我們就可以配置 用戶權限,什麽 url 什麽人才可以訪問等等的一些安全控制。 一般來說,我們會選擇繼承 WebSecurityConfigurerAdapter 類,那麽該類是幹什麽的,如下註釋: > \* Provides a convenient base class for creating a {@link WebSecurityConfigurer} instance. > \* The implementation allows customization by overriding methods. > 我們可以重寫該類的幾個方法來決定究竟攔截什麽url,設置什麽權限,等一些安全控制: > * public void configure(WebSecurity web) throws Exception {...} > * protected void configure(HttpSecurity http) throws Exception {...} > * protected void configure(AuthenticationManagerBuilder auth) {...} 例如: ``` java= @EnableWebSecurity @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest() .fullyAuthenticated() .and() .httpBasic() .and() .csrf().disable(); } } ``` 上面的3個方法的區別就是參數的不同,這些參數WebSecurity,HttpSecurity,AuthenticationManagerBuilder 又有什麽區別呢?這裏有一個解釋很好了:[HttpSecurity, WebSecurity and AuthenticationManagerBuilder](https://stackoverflow.com/questions/22998731/httpsecurity-websecurity-and-authenticationmanagerbuilder) ,簡單來說就是: - HttpSecurity:一般用它來具體控制權限,角色,url等安全的東西。 - AuthenticationManagerBuilder:用來做登錄認證的。具體的註釋,看org.springframework.security.config.annotation.web.configuration 包的 WebSecurityConfigurerAdapter 類的 protected void configure(AuthenticationManagerBuilder auth) throws Exception {...}方法的註釋,很清楚,註釋也教了怎麽用這個東西。 - WebSecurity:For example, if you wish to ignore certain requests. ## 3. Spring-Security 流程 官網上指出:If Spring Security is on the classpath then web applications will be secure by default with `‘basic’` authentication on all HTTP endpoints. 也就是默認會有 authentication 首先進行認證。 那麽,通過加斷點的方式,可以發現: - 首先會去到 OncePerRequestFilter 類的 doFilter 方法,執行doFilterInternal 方法 - 然後由於子類 BasicAuthenticationFilter 繼承了 OncePerRequestFilter,並且重寫了doFilterInternal 方法,所以會執行 BasicAuthenticationFilter 的 doFilterInternal 方法。 - BasicAuthenticationFilter 的 doFilterInternal 方法該方法中有一句:Authentication authResult = authenticationManager.authenticate(authRequest);這句 分析下 Authentication 類,authenticationManager.authenticate() 方法。 **Authentication 類** 註釋上寫明: > \* Represents the token for an authentication request or for an authenticated principal > \* once the request has been processed by the > \* {@link AuthenticationManager#authenticate(Authentication)} method. 一些方法有: > Object getCredentials(); Object getPrincipal(); Object getDetails(); ...... > **AuthenticationManager 接口** 主要提供 `Authentication authenticate(Authentication authentication) throws AuthenticationException;` 方法來對請求進行驗證。 那麽,它的默認實現類是 ProviderManager,實現了 authenticate() 方法,在該方法中繼續調用子類 AbstractUserDetailsAuthenticationProvider 對應的 authenticate(),在 AbstractUserDetailsAuthenticationProvider 的 authenticate() 調用 `user = retrieveUser(username,(UsernamePasswordAuthenticationToken) authentication);` ,這裏可以看出,默認使用的 Authentication 是UsernamePasswordAuthenticationToken。 那麽,當我們了解了這些,可以換嗎,當然可以。 順著上面的步驟,我們說到 retrieveUser() 方法,在該方法中,有 `loadedUser = this.getUserDetailsService().loadUserByUsername(username);`,對於 `this.getUserDetailsService()`,目的肯定是要得到 UserDetailsService,這個是什麽,開頭說了,不再累贅。 **這裏,我們就可以通過創建自己的 UserDetailsService ,實現自己的 loadUserByUsername 來實現從數據中認證,而不是基於內存(當然,要使用內存也可以)。** 以上就是 spring-security 的 Authentication 部分的認證流程,總的來說,代碼調用流程如下圖: ![](https://i.imgur.com/yDWuKAR.png) 可參考的資料 [Spring Security 参考手册 - 簡中](https://springcloud.cc/spring-security-zhcn.html#truejava) [Spring Security 教程](https://waylau.gitbooks.io/spring-security-tutorial/)