SpringBoot整合SpringSecurity實現(xiàn)認證攔截的教程
一. SpringSecurity簡介
1. SpringSecurity概述
Spring Security 是 Spring 社區(qū)的一個頂級項目,也是 Spring Boot 官方推薦使用的安全框架。
除了常規(guī)的認證(Authentication)和授權(quán)(Authorization)之外,Spring Security還提供了諸如ACLs,LDAP,JAAS,CAS等高級特性以滿足復雜場景下的安全需求。
另外,就目前而言,Spring Security和Shiro也是當前廣大應用使用比較廣泛的兩個安全框架。
Spring Security 應用級別的安全主要包含兩個主要部分,即登錄認證(Authentication)和訪問授權(quán)(Authorization),首先用戶登錄的時候傳入登錄信息,登錄驗證器完成登錄認證并將登錄認證好的信息存儲到請求上下文,然后再進行其他操作,如在進行接口訪問、方法調(diào)用時,權(quán)限認證器從上下文中獲取登錄認證信息,然后根據(jù)認證信息獲取權(quán)限信息,通過權(quán)限信息和特定的授權(quán)策略決定是否授權(quán)。
2. SpringSecurity的特征
- 對身份驗證和授權(quán)的全面和可擴展的支持;
- 防止會話固定,點擊劫持,跨站點請求偽造等攻擊;
- Servlet API集成;
- 可選與Spring Web MVC集成。
二. SpringBoot整合SpringSecurity實現(xiàn)步驟
1. 需求分析
當用戶來訪問接口時,根據(jù)用戶攜帶的Authorization去查詢此用戶的角色,再根據(jù)設置好的角色所具有的權(quán)限進行判斷,如果訪問的接口是該角色下的接口,則進行接口放行。
2. 創(chuàng)建web項目
我們按照之前的經(jīng)驗,創(chuàng)建一個web程序,并將之改造成Spring Boot項目,具體過程略。

3. 測試未添加SpringSecurity時的情況
我們可以先測試一下項目中不添加Spring Security依賴包的情況,在這種情況下,我直接創(chuàng)建一個Controller接口,然后啟動項目進行測試。
package com.yyg.boot.web;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author
* @Date Created in 2020/5/18
* @Description Description
*/
@RestController
public class IndexController {
@GetMapping("/hello")
public String hello() {
return "Hello SpringSecurity!";
}
}4. 創(chuàng)建入口類
package com.yyg.boot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @Author 一一哥Sun
* @Date Created in 2020/5/18
* @Description Description
*/
@SpringBootApplication
public class SecurityApplication {
public static void main(String[] args){
SpringApplication.run(SecurityApplication.class,args);
}
}5. 訪問接口
此時我們可以看到,在瀏覽器中可以直接訪問這個接口方法。

6. 添加Spring Security依賴包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>7. 重啟項目測試
當添加了spring-boot-starter-security依賴包之后,我們此時再重新啟動項目進行測試。此時會發(fā)現(xiàn)控制臺中有一行日志信息:也就是Spring Boot會自動產(chǎn)生一個隨機的密碼。

然后我們再訪問剛才的接口,會發(fā)現(xiàn)接口會自動重定向到login登錄頁面。
然后在這個登錄頁面中,我們可以輸入用戶名和密碼:
- 用戶名:user
- 密碼:控制臺的隨機密碼

然后此時才可以看到剛才的接口內(nèi)容。

這是因為當Spring項目中引入了Spring Security依賴的時候,項目會默認開啟如下配置:
security.basic.enabled=true
這個配置開啟了一個表單認證,所有服務的訪問都必須先過這個認證,默認的用戶名為user,密碼由Sping Security自動生成,回到IDE的控制臺,可以找到密碼信息:
Using generated security password: 078db2a5-ae07-4a10-a85c-cf0162a7e966
8. 修改登錄表單的用戶名和密碼
我們可以在application.yml文件中,通過spring.security屬性進行用戶名和密碼的配置。
server:
port: 8080
spring:
security:
user:
name: admin
password: 123此時可以看到,新的登錄名和密碼變成了我們設置好的。

三. 基于HttpBasic認證
1. HttpBasic認證實現(xiàn)
配置SpringSecurity認證方式。
創(chuàng)建一個配置類SpringSecurityConfig繼承org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter這個抽象類并重寫configure(HttpSecurity http)方法。
WebSecurityConfigurerAdapter是由Spring Security提供的Web應用安全配置的適配器:
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic() // HttpBasic
// http.formLogin() // 表單方式
.and()
.authorizeRequests() // 授權(quán)配置
.anyRequest() // 所有請求
.authenticated(); // 都需要認證
}
}HttpBasic登錄效果

2. HttpBasic基本原理
上面我們開啟了一個最簡單的Spring Security安全配置,下面我們來了解下Spring Security的基本原理。通過上面的的配置,代碼的執(zhí)行過程可以簡化為下圖表示:

如上圖所示,Spring Security包含了眾多的過濾器,這些過濾器形成了一條鏈,所有請求都必須通過這些過濾器后才能成功訪問到資源。
其中UsernamePasswordAuthenticationFilter過濾器用于處理基于表單方式的登錄認證,而BasicAuthenticationFilter用于處理基于HTTP Basic方式的登錄驗證,后面還可能包含一系列別的過濾器(可以通過相應配置開啟)。
在過濾器鏈的末尾是一個名為FilterSecurityInterceptor的攔截器,用于判斷當前請求身份認證是否成功,是否有相應的權(quán)限,當身份認證失敗或者權(quán)限不足的時候便會拋出相應的異常。ExceptionTranslateFilter捕獲并處理,所以我們在ExceptionTranslateFilter過濾器用于處理了FilterSecurityInterceptor拋出的異常并進行處理,比如需要身份認證時將請求重定向到相應的認證頁面,當認證失敗或者權(quán)限不足時返回相應的提示信息。
四. SpringBoot集成SpringSecurity實現(xiàn)權(quán)限管理
我們在上面案例的基礎(chǔ)上,進行本案例的開發(fā)講解。
1. 創(chuàng)建實體類
1.1 創(chuàng)建Admin實體類
package com.yyg.boot.domain;
import lombok.Data;
import java.io.Serializable;
/**
* @Author 一一哥Sun
* @Date Created in 2020/5/18
* @Description Description
*/
@Data
public class Admin implements Serializable {
private String username;
private String password;
}1.2 創(chuàng)建Member實體類
package com.yyg.boot.domain;
import lombok.Data;
import java.io.Serializable;
/**
* @Author 一一哥Sun
* @Date Created in 2020/5/18
* @Description Description
*/
@Data
public class Member implements Serializable {
private String id;
private String username;
private String password;
}2. 繼承WebSecurityConfigurerAdapter配置角色權(quán)限
package com.yyg.boot.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* @Author 一一哥Sun
* @Date Created in 2020/5/18
* @Description Description
*/
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
//@Override
//protected void configure(HttpSecurity http) throws Exception {
//http.httpBasic() // 簡單的HttpBasic登錄方式
//http.formLogin() // 提供一個登錄的表單
//.and()
//.authorizeRequests() // 授權(quán)配置
//.anyRequest() // 所有請求
//.authenticated(); // 都需要認證
//}
@Autowired
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
//內(nèi)存方式創(chuàng)建用戶
// @Override
// public void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.inMemoryAuthentication()
// .withUser("admin").password("123456").roles("ADMIN")
// .and()
// .withUser("member").password("123456").roles("MEMBER");
// }
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
//passwordEncoder是對密碼的加密處理,如果user中密碼沒有加密,則可以不加此方法。注意加密請使用security自帶的加密方式。
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
//禁用了 csrf 功能
.disable()
//限定簽名成功的請求
.authorizeRequests()
.antMatchers("/employee/*")
//對employee 下的接口,需要 MEMBER 或者 ADMIN 權(quán)限
.hasAnyRole("MEMBER","ADMIN")
//對employee/login接口直接放行,不限制
.antMatchers("/employee/login")
.permitAll()
//對admin下的接口 需要ADMIN權(quán)限
.antMatchers("/admin/**")
.hasRole("ADMIN")
//不攔截 oauth 開放的資源
.antMatchers("/oauth/**")
.permitAll()
//其他沒有限定的請求,允許訪問
.anyRequest()
.permitAll()
.and()
//對于沒有配置權(quán)限的其他請求允許匿名訪問
.anonymous()
.and()
//使用 spring security 默認登錄頁面
.formLogin()
.and()
//啟用http 基礎(chǔ)驗證
.httpBasic();
}
}3. 創(chuàng)建UserDetailsService
package com.yyg.boot.service;
import com.yyg.boot.domain.Admin;
import com.yyg.boot.domain.Member;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* @Author
* @Date Created in 2020/5/18
* @Description Description
*/
@Service
public class UserDetailServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
//生成環(huán)境是查詢數(shù)據(jù)庫獲取username的角色用于后續(xù)權(quán)限判斷(如:張三 admin)
//這里不做數(shù)據(jù)庫操作,給定假數(shù)據(jù),進行簡單模擬.
if ("member".equals(username)) {
Member member = new Member();
member.setUsername("member");
member.setPassword("123456");
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_MEMBER");
grantedAuthorities.add(grantedAuthority);
//創(chuàng)建一個用戶,用于判斷權(quán)限,請注意此用戶名和方法參數(shù)中的username一致;BCryptPasswordEncoder是用來演示加密使用。
return new User(member.getUsername(), new BCryptPasswordEncoder().encode(member.getPassword()), grantedAuthorities);
}
if ("admin".equals(username)) {
Admin admin = new Admin();
admin.setUsername("admin");
admin.setPassword("123456");
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_ADMIN");
grantedAuthorities.add(grantedAuthority);
return new User(admin.getUsername(), new BCryptPasswordEncoder().encode(admin.getPassword()), grantedAuthorities);
} else {
return null;
}
}
}4. 創(chuàng)建controller層代碼
4.1 創(chuàng)建AdminController
package com.yyg.boot.web;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author 一一哥Sun
* @Date Created in 2020/5/18
* @Description Description
*/
@RestController
@RequestMapping("/admin")
public class AdminController {
@GetMapping("/greeting")
public String greeting() {
return "Hello,Admin!";
}
@GetMapping("/login")
public String login() {
return "login success!";
}
}4.2 創(chuàng)建MemberController
package com.yyg.boot.web;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author 一一哥Sun
* @Date Created in 2020/5/18
* @Description Description
*/
@RestController
@RequestMapping("/member")
public class MemberController {
@GetMapping("/greeting")
public String greeting() {
return "Hello,Member!";
}
@GetMapping("/login")
public String login() {
return "login success!";
}
}5. 進行測試
此時我們可以利用postman,通過Basic認證方式,攜帶用戶名和密碼進行相關(guān)接口的訪問。
5.1 member身份的權(quán)限,只能訪問member相關(guān)的接口:

5.2 admin身份的權(quán)限,可以訪問admin與member相關(guān)的接口:

后續(xù)我們可以關(guān)聯(lián)數(shù)據(jù)庫,從數(shù)據(jù)庫中查詢用戶的身份和角色信息,從而進一步給用戶分配權(quán)限信息。
結(jié)語
至此,我們就對Web項目添加了一個安全防護,而且實現(xiàn)起來簡直不要太easy!現(xiàn)在你知道該怎么保護自己的Web項目不被“傷害”了嗎?
以上就是SpringBoot整合SpringSecurity實現(xiàn)認證攔截的教程的詳細內(nèi)容,更多關(guān)于SpringBoot整合SpringSecurity實現(xiàn)認證攔截的資料請關(guān)注腳本之家其它相關(guān)文章!
- SpringSecurity自定義資源攔截規(guī)則及登錄界面跳轉(zhuǎn)問題
- SpringSecurity攔截器鏈的使用詳解
- springsecurity實現(xiàn)攔截器的使用示例
- Swagger2不被SpringSecurity框架攔截的配置及說明
- Spring Boot security 默認攔截靜態(tài)資源的解決方法
- SpringSecurity實現(xiàn)動態(tài)url攔截(基于rbac模型)
- Spring Security攔截器引起Java CORS跨域失敗的問題及解決
- SpringBoot+SpringSecurity 不攔截靜態(tài)資源的實現(xiàn)
- 淺談Spring Security 對于靜態(tài)資源的攔截與放行
- spring Security配置攔截規(guī)則小結(jié)
相關(guān)文章
如何解決java.util.zip.ZipFile解壓后被java占用問題
這篇文章主要介紹了如何解決java.util.zip.ZipFile解壓后被java占用問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06

