快速搭建基於Spring Boot + Spring Security 環境


個人博客網:https://wushaopei.github.io/    (你想要這里多有)

1、Spring Security 權限管理框架介紹

簡介: Spring Security 提供了基於javaEE的企業應有個你軟件全面的安全服務。這里特別強調支持使用SPring框架構件的項目,Spring框架是企業軟件開發javaEE方案的領導者。

Spring Security 的兩個目標: “認證” 與“授權”。

  • 認證”,是建立一個他聲明的主題的過程(一個“主體”一般是指用戶,設備或一些可以在你的應用程序中執行動作的其他系統)。
  • 授權” 指確定一個主體是否允許在你的應用程序執行一個動作的過程。為了抵達需要授權的店,主體的身份已經有認證過程建立。這個概念是通用的而不只在Spring Security中。

           

在SpringSecurity 中,請求進入后會被攔截器攔截到,並交由認證管理器首先處理;這是在身份驗證層,Spring Security 的支持多種認證模式。包括 Basic、Digest、X.509、LDAP、Form(基於表單的認證) 等多種HTTP 認證。

淺析:

Basic 認證:在HTTP1.0提出的認證方法,針對特定的用戶和資源,需要提供特定的密碼認證后方可訪問,其中密碼是使用明文傳輸的;一個請求到來時,瀏覽器彈出對話框,讓用戶輸入用戶名和密碼,並用BASE 64 進行編碼進行傳輸,服務器接受並解析這些信息,並認證通過,才會允許繼續訪問。

Digest認證: 解決Basic 認證的安全問題,當訪問時,瀏覽器依舊彈出對話框,讓用戶輸入用戶名和密碼,瀏覽器會對用戶名、密碼、HTTP請求方法、被請求資源的URI進行組合后進行MD5運算,然后把計算得到的摘要信息發送給服務器;服務器獲取報文頭部相關認證信息后獲取到 username ,同時獲取到密碼,同樣對用戶名、密碼、HTTP請求方法、被請求資源的URI進行組合后和 response 進行比較,如果相同,才算認證通過。

2、Spring Security 常用權限攔截器:

        

主要功能性攔截器有 11 個, FilterChainProxy 對攔截器進行過濾操作並代理執行!

3、Spring Security 項目 Demo

(1)環境搭建及使用

  1. 基於Spring Boot + Spring Security 環境
  2. 常用 Case 實現

(2)創建SpringBoot 工程

                 

(3)整合SpringSecurity 依賴

	        <!--springsecurity 主要依賴--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> </dependency>

(4)主啟動類配置:

@RestController //返回響應體為json @SpringBootApplication @EnableAutoConfiguration //掃描Bean注入到容器中 public class Bootstrap { public static void main(String[] args) { SpringApplication.run(Bootstrap.class, args); } @RequestMapping("/") // 測試接口 public String home (){ return "hello spring boot"; } @RequestMapping("/hello") //測試接口 public String hello (){ return "hello spring boot"; } }

(5)配置 Servlet 初始化

/** * @ClassName ServletInitializer 告訴程序,項目啟動時從 Bootstrap 開始 * @Description TODO * @Author wushaopei * @Date 2019/9/19 10:30 * @Version 1.0 */ public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application){ return application.sources(Bootstrap.class); } } 

(6)啟動SpringBoot 項目- - run 主啟動類:

啟動SpringBoot 項目進行接口訪問時,彈出登錄界面,說明SpringSecurity生效了

           

4、SpringSecurity攔截配置:

注意:在 3  的基礎上,對工程進一步配置:

(1)創建 SpringSecurityConfig 類管理 攔截配置:

@Configuration // 將Bean 放到容器中管理 @EnableWebSecurity // 用於將Security 生成 Bean public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { /* * 接口請求攔截 **/ @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() //安全請求策略 .antMatchers("/").permitAll() //可放行請求配置 .anyRequest().authenticated() //其他請求進行攔截 .and() .logout().permitAll() // 注銷任意訪問 .and() .formLogin(); http.cors().disable(); } /* * 前端攔截 * */ @Override public void configure(WebSecurity web){ // 忽視 js 、css 、 images 后綴訪問 web.ignoring().antMatchers("/js/**","/css/**","/images/**"); } } 

該 SpringSecurityConfig 繼承 WebSecurityConfigurerAdapter 類,並重寫 configure(HttpSecurity http)和 configure(WebSecurity web)兩個方法,分別針對 接口請求 靜態頁面 資源進行攔截。

接口請求: ip:port/  的請求一律通過,主要進入到 Bootstrap 中的 RequestMapping(“/”)接口中;並對 “/”后帶有標識地址的請求進行攔截認證;

靜態資源: 此處對靜態 js 、css、images 三者進行放行。

效果截圖:

配置 SpringSecurity 后,默認可以通過 “/” 的接口請求; 而當進行 如“/hello”接口請求時會被攔截進行驗證

5、基於SpringSecurity 權限管理 Case 實操

權限管理 Case 的需求有兩個要求:

  1. 第一點 :只要能登錄即可;
  2. 第二點:有指定的角色,每個角色有指定的權限.

(1)只要能登錄即可,功能體現:

在SpringSecurityConfig.java中配置用戶信息:

/* * 告訴程序,系統中有個用戶 用戶名為 admin ,密碼為 admin 角色為 ADMIN * */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN"); }

注意 :

在springboot使用spring security 做權限管理 ,使用內存用戶驗證,會返回無響應報錯:
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"

解決方法:

這是因為Spring boot 2.0.3引用的security 依賴是 spring security 5.X版本,此版本需要提供一個PasswordEncorder的實例,否則后台匯報錯誤:

java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
並且頁面毫無響應。
因此,需要創建PasswordEncorder的實現類。

創建PasswordEncorder 實體類后可通過兩種方式完成明文驗證通過:

第一種:直接在配置好的 PasswordEncorder 類上添加 @Component 裝配 MyPasswordEncoder 為Bean 注入到容器即可;

@Component public class MyPasswordEncoder implements PasswordEncoder { @Override public String encode(CharSequence charSequence) { return charSequence.toString(); } @Override public boolean matches(CharSequence charSequence, String s) { return s.equals(charSequence.toString()); } }

然后繼續使用

  /* * 告訴程序,系統中有個用戶 用戶名為 admin ,密碼為 admin 角色為 ADMIN * */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //可以設置內存指定的登錄的賬號密碼,指定角色 //不加.passwordEncoder(new MyPasswordEncoder())或者注入該類的Bean //就不是以明文的方式進行匹配,會報錯 auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN"); }

進行 密碼明文登錄,可以成功

第二種:在auth.inMemoryAuthentication()后面使用.passwordEncoder(new MyPasswordEncoder())對登錄信息進行包裝后提交即可;這樣也可以成功

//這樣,頁面提交時候,密碼以明文的方式進行匹配。 auth.inMemoryAuthentication().passwordEncoder(new MyPasswordEncoder()).withUser("wsp").password("wsp").roles("ADMIN"); 

推薦: 最好是使用第一種 裝載成 Bean 后注入容器,對於開發效率更高,也更便捷。

(2)有指定的角色,每個角色有指定的權限,功能體現:

① 創建接口 role1() 並對其添加 @PreAuthorize("hasRole('ROLE_ADMIN')")  以進行角色攔截

    @PreAuthorize("hasRole('ROLE_ADMIN')") // 角色攔截校驗注解 @RequestMapping("/roleAuth") public String role1(){ return "roleAuth"; }

(2)角色攔截 - - 功能解析:

 @PreAuthorize("hasRole('ROLE_ADMIN')") 注解說明:

②並在 SpringSecurityConfig.java 中創建新用戶,以提供測試:

@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //可以設置內存指定的登錄的賬號密碼,指定角色 //不加.passwordEncoder(new MyPasswordEncoder()) //就不是以明文的方式進行匹配,會報錯 auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN"); auth.inMemoryAuthentication().withUser("demo").password("demo").roles("DEMO"); .passwordEncoder(new MyPasswordEncoder())。 //這樣,頁面提交時候,密碼以明文的方式進行匹配。 auth.inMemoryAuthentication().passwordEncoder(new MyPasswordEncoder()).withUser("wsp").password("wsp").roles("ADMIN"); }

③ 創建好接口后,進行登錄嘗試,發現,使用 用戶為 demo ,角色 為 DEMO 依舊可以通過該接口

問題解析:

這是因為還需要再添加一個@EnableGlobalMethodSecurity(prePostEnabled = true) 注解到 啟動器上,以讓 @PreAuthorize("hasRole('ROLE_ADMIN')") 這個注解生效,完整 啟動類代碼如下:

@RestController @SpringBootApplication @EnableAutoConfiguration @EnableGlobalMethodSecurity(prePostEnabled = true) public class Bootstrap { public static void main(String[] args) { SpringApplication.run(Bootstrap.class, args); } @RequestMapping("/") public String home (){ return "hello spring boot"; } @RequestMapping("/hello") public String hello (){ return "hello spring boot"; } @PreAuthorize("hasRole('ROLE_ADMIN')") @RequestMapping("/roleAuth") public String role1(){ return "roleAuth"; } }

添加注解后

使用 demo 進行登錄:

         

使用admin進行登錄:

         

(3)數據庫管理實現

通過數據庫獲取用戶信息進行登錄認證

創建 MyUserService.java類,並裝載 Bean 到 容器中,在SpringSecurityConfig.java 中進行配置:

@Component public class MyUserService implements UserDetailsService{ @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { return null; } }

SpringSecurityConfig.java 中 configure(AuthenticationManagerBuilder auth)方法修改為通過注入MyUserService 的Bean實現數據庫查詢

    @Autowired private MyUserService myUserService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(myUserService); // 通過注入 MyUserService 的方式實現數據庫查詢用戶信息的登錄認證 }

密碼的自定義驗證:

創建 MyPasswordEncoder 類,實現 PasswordEncoder 接口類;重寫 encode(CharSequence obj和 matches(CharSequence obj,String str) 方法:

@Component public class MyPasswordEncoder implements PasswordEncoder { private final static String SALT = "wenmin"; @Override public String encode(CharSequence charSequence) { return MD5Util.MD5Encode(charSequence.toString(),SALT); } @Override public boolean matches(CharSequence rawPassword, String encodedPassword) { return MD5Util.isPasswordValid(encodedPassword,rawPassword.toString(),SALT); } }

說明:第一個是加密的方法,第二個是匹配密碼,具體操作是加密方法加密后與原始密碼在匹配方法中進行匹配

底層邏輯解析:

passwordEncoder. isPasswordValid(userDetails.getPassword(), presentedPassword, salt) 這個實現是在接口PasswordEncoder的實現類MessageDigestPasswordEncoder中實現的。 

MessageDigestPasswordEncoder類: 
public boolean isPasswordValid(String encPass, String rawPass, Object salt) { String pass1 = "" + encPass; String pass2 = encodePassword(rawPass, salt); return pass1.equals(pass2); } 其中 encodePassword(rawPass, salt)方法如下: public String encodePassword(String rawPass, Object salt) { String saltedPass = mergePasswordAndSalt(rawPass, salt, false); MessageDigest messageDigest = getMessageDigest(); byte[] digest; try { digest = messageDigest.digest(saltedPass.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { throw new IllegalStateException("UTF-8 not supported!"); } // "stretch" the encoded value if configured to do so for (int i = 1; i < iterations; i++) { digest = messageDigest.digest(digest); } if (getEncodeHashAsBase64()) { return new String(Base64.encode(digest)); } else { return new String(Hex.encode(digest)); } } 使用的是MD5加密方法。 

引入自定義的登錄信息驗證器

auth.userDetailsService(myUserService).passwordEncoder(new MyPasswordEncoder());

相關注解說明:

@PreAuthorize("hasRole('ROLE_ADMIN')")  方法調用前進行權限檢查
@PostAuthorize("hasRole('')")           方法調用后進行權限檢查
@PreFilter("")                          方法調用前針對集合類參數或返回值進行過濾
@PostFilter("") 方法調用后針對集合類參數或返回值進行過濾

6、Spring Security 的優缺點

優點:

提供了一套安全框架,而且這個框架是可以用的;

提供了很多用戶認證的功能,實現相關接口即可,節約大量開發工作;

基於spring,易於集成到 spring 項目中,且封裝了許多方法。

缺點:

配置文件多,角色被“編碼”到配置文件盒源文件中,RBAC 不明顯;

對於系統中用戶、角色、權限之間的關系,沒有可操作的界面;

大數據量情況下,幾乎不可用。

https://github.com/wushaopei/SPRING_BOOT/tree/master/Spring-boot-Security


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM