# 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 部分的認證流程,總的來說,代碼調用流程如下圖:

可參考的資料
[Spring Security 参考手册 - 簡中](https://springcloud.cc/spring-security-zhcn.html#truejava)
[Spring Security 教程](https://waylau.gitbooks.io/spring-security-tutorial/)