Spring Security 提供了基於javaEE的企業應用軟件全面的安全服務。這裏特別強調支持使用Spring框架構件的項目,Spring框架是企業軟件開發javaEE方案的領導者。若是你尚未使用Spring來開發企業應用程序,咱們熱忱的鼓勵你仔細的看一看。熟悉Spring特別是一來注入原理兩幫助你更快更方便的使用Spring Security。css
人們使用Spring Secruity的緣由有不少,單大部分都發現了javaEE的Servlet規範或EJB規範中的安全功能缺少典型企業應用場景所需的深度。提到這些規範,重要的是要認識到他們在WAR或EAR級別沒法移植。所以若是你更換服務器環境,這裏有典型的大量工做去從新配置你的應用程序員安全到新的目標環境。使用Spring Security 解決了這些問題,也爲你提供許多其餘有用的,可定製的安全功能。html
正如你可能知道的兩個應用程序的兩個主要區域是「認證」和「受權」(或者訪問控制)。這兩個主要區域是Spring Security 的兩個目標。「認證」,是創建一個他聲明的主題的過程(一個「主體」通常是指用戶,設備或一些能夠在你的應用程序中執行動做的其餘系統)。「受權」指肯定一個主體是否容許在你的應用程序執行一個動做的過程。爲了抵達須要受權的目的,主體的身份已經有認證過程創建。這個概念是通用的而不僅在Spring Security中。java
Spring Security主要的組件圖:程序員
在身份驗證層,Spring Security 的支持多種認證模式。這些驗證絕大多數都是要麼由第三方提供,或由相關的標準組織,如互聯網工程任務組開發。另外Spring Security 提供本身的一組認證功能。具體而言,Spring Security 目前支持全部這些技術集成的身份驗證:web
Spring Security5.x官方文檔地址以下:spring
https://docs.spring.io/spring-security/site/docs/5.0.7.RELEASE/reference/htmlsingle/
https://docs.spring.io/spring-security/site/docs/5.0.7.RELEASE/api/數據庫
SecurityContextPersistenceFilter:api
LogoutFilter:安全
AbstractAuthenticationProcessingFilter:bash
DefaultLoginPageGeneratingFilter:
BasicAuthenticationFilter:
SecurityContextHolderAwareRequestFilter:
RememberMeAuthenticationFilter:
AnonymousAuthenticationFilter:
ExceptionTranslationFilter:
SessionManagementFilter:
FilterSecurityInterceptor:
以上就是Spring Security經常使用的11個權限攔截器,那麼這些攔截器是按什麼樣的順序執行的呢?這就須要先了解一下FilterChainProxy這個過濾器鏈代理類了:
打開IDEA,建立一個SpringBoot項目:
勾選相應的模塊:
在項目中新建一個config包,在該包下建立 SpringSecurityConfig 配置類,用於配置Spring Security的攔截規則。代碼以下:
package org.zero.security.securitydemo.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; /** * @program: security-demo * @description: Spring Security 配置類 * @author: 01 * @create: 2018-08-29 23:20 **/ @Configuration @EnableWebSecurity public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // 定義一個簡單的訪問規則 http.authorizeRequests() .antMatchers("/").permitAll() // 容許任意訪問根路徑 .anyRequest().authenticated() // 除根路徑之外的請求都須要驗證 .and().logout().permitAll() // 容許任意訪問註銷路徑 .and().formLogin(); // 容許表單登陸 // 禁用默認的csrf驗證 http.csrf().disable(); } @Override public void configure(WebSecurity web) throws Exception { // 不攔截靜態資源的訪問 web.ignoring().antMatchers("/js/**", "/css/**", "/images/**"); } }
而後新建一個controller包,在該包中建立 DemoController 控制器類,用於開啓一些接口進行測試。代碼以下:
package org.zero.security.securitydemo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * @program: security-demo * @description: spring security demo * @author: 01 * @create: 2018-08-29 23:14 **/ @RestController public class DemoController { @GetMapping("/") public String demo(){ return "Hello Spring Security"; } @GetMapping("/hello") public String hello(){ return "Hello World"; } }
啓動項目,訪問根目錄,正常輸出了相應的字符串:
而訪問/hello
,就會跳轉到登陸頁面,須要進行驗證,這就表明SpringSecurity的配置:
Case一、簡單的登陸:
SpringSecurity自帶有一套基於內存的驗證,這樣咱們僅須要實現簡單的登陸功能的時候,就不須要額外去建立數據庫了。在 SpringSecurityConfig 類中,加入以下方法:
@Configuration @EnableWebSecurity public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication(). // 使用基於內存的認證方式 passwordEncoder(new BCryptPasswordEncoder()). // 設置密碼的加密方式 withUser("admin"). // 設置用戶名稱 password(new BCryptPasswordEncoder().encode("123456")). // 設置密碼 roles("ADMIN"); // 自定義該用戶的角色 } ... }
重啓項目,當訪問受控制的資源時,就會跳轉到以下登陸頁面,輸入設定好的用戶名和密碼:
訪問成功:
訪問logout接口能夠退出登陸:
Case二、有指定的角色,每一個角色有指定的權限:
即使是簡單的登陸,也可能會遇到有一些資源須要管理員角色才能訪問。因此咱們來看看如何限定一個資源只能被管理員用戶訪問。在configure方法中,增長一個普通用戶,代碼以下:
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication(). passwordEncoder(new BCryptPasswordEncoder()). withUser("admin"). password(new BCryptPasswordEncoder().encode("123456")). roles("ADMIN"); auth.inMemoryAuthentication(). passwordEncoder(new BCryptPasswordEncoder()). withUser("user"). password(new BCryptPasswordEncoder().encode("user")). roles("USER"); }
在DemoController類中增長一個接口,並指定這個接口只能被admin角色的用戶訪問。代碼以下:
@RestController // 開啓驗證 @EnableGlobalMethodSecurity(prePostEnabled = true) public class DemoController { // 指定該接口只能被ADMIN角色的用戶訪問,ROLE_這個前綴是固定要寫的 @PreAuthorize("hasRole('ROLE_ADMIN')") @GetMapping("/roleAuth") public String role(){ return "admin auth"; } ... }
重啓項目,登陸非admin角色的用戶:
訪問roleAuth接口,會返回403錯誤:
登陸admin用戶,訪問roleAuth接口成功:
@PreAuthorize裏的表達式可使用 and 、or這種運算符,例如:
@PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_ROOT')")
除了@PreAuthorize註解外,還有:
Case三、自定義密碼加密:
咱們能夠自定義本身的加密方式去作密碼的加密及匹配,我這裏使用MD5做爲演示。首先建立一個 MyPasswordEncoder 類並實現 PasswordEncoder 接口。具體代碼以下:
package org.zero.security.securitydemo.encoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.util.DigestUtils; /** * @program: security-demo * @description: 自定義密碼加密器 * @author: 01 * @create: 2018-09-07 21:43 **/ public class MyPasswordEncoder implements PasswordEncoder { @Override public String encode(CharSequence charSequence) { // 使用Spring自帶的工具進行MD5加密 return DigestUtils.md5DigestAsHex(charSequence.toString().getBytes()); } @Override public boolean matches(CharSequence rawPassword, String encodedPassword) { // 驗證密碼加密後是否一致 return encode(rawPassword).equals(encodedPassword); } }
使用咱們本身自定義的密碼加密器,修改configure方法的代碼以下:
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication(). passwordEncoder(new MyPasswordEncoder()). withUser("admin"). password(new MyPasswordEncoder().encode("123456")). roles("ADMIN"); auth.inMemoryAuthentication(). passwordEncoder(new MyPasswordEncoder()). withUser("user"). password(new MyPasswordEncoder().encode("user")). roles("USER"); }
Case四、參數驗證:
經過@PreAuthorize
註解,咱們能夠在方法執行前,進行權限參數的驗證。例如我要驗證id小於時,且username參數的值和當前登陸的用戶名一致。代碼以下:
@PreAuthorize("#id<10 and principal.username.equals(#username)") @GetMapping("/check_info") public String checkInfo(Integer id, String username) { return "success"; }
若是參數是一個對象也能夠進行校驗,代碼以下:
@PreAuthorize("#user.username.equals('admin')") @GetMapping("/check_user") public String checkUser(User user) { return "success"; }
優勢:
缺點: