1. Spring Security簡介
Spring Security是Spring的一個核心項目,它是一個功能強大且高度可自訂的認證和存取控制框架。它提供了認證和授權功能以及抵禦常見的攻擊,它已經成為保護基於spring的應用程式的事實標準。
Spring Boot提供了自動配置,引入starter依賴即可使用。
Spring Security特性摘要:
使用簡單,提供Spring Boot starter依賴,極易與Spring Boot專案整合。
專業,提供CSRF防護、點擊劫持防護、XSS防護等,並提供各種安全頭整合(X-XSS-Protection,X-Frame-Options等)。
密碼加密存儲,支援多種加密演算法
#可擴展性和可自訂性極強
##OAuth3 JWT認證支援2. JWT簡介
… …
##JWT(Json web token ),是為了在網路應用環境間傳遞聲明而執行的一種基於JSON的開放標準(RFC 7519).該token被設計為緊湊且安全的,特別適用於分佈式站點的單點登錄(SSO)場景。 JWT的聲明一般被用來在身分提供者和服務提供者間傳遞被認證的使用者身分訊息,以便於從資源伺服器取得資源,也可以增加一些額外的其它業務邏輯所必須的聲明資訊(例如,權限資訊).一旦用戶被授予token,用戶即可透過該token存取伺服器上的資源。
3. Spring Boot整合Spring Security
在Spring Boot專案的pom.xml檔案中加入下面的依賴:注意這篇文章示範使用JDK和Spring Boot的版本如下:Spring Boot:2.7.2JDK: 11
不同的Spring Boot版本配置不同,但是原理相同。
<!-- Spring Security的Spring boot starter,引入后将自动启动Spring Security的自动配置 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- 下面的依赖包含了OAuth3 JWT认证实现 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth3-resource-server</artifactId> </dependency>以上兩個依賴即可。 4. 設定Spring Security使用JWT認證
注意:
主要是設定HttpSecurity Bean產生SecurityFilterBean,設定如下:
import com.nimbusds.jose.jwk.JWK; import com.nimbusds.jose.jwk.JWKSet; import com.nimbusds.jose.jwk.RSAKey; import com.nimbusds.jose.jwk.source.ImmutableJWKSet; import com.nimbusds.jose.jwk.source.JWKSource; import com.nimbusds.jose.proc.SecurityContext; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.oauth3.jwt.JwtDecoder; import org.springframework.security.oauth3.jwt.JwtEncoder; import org.springframework.security.oauth3.jwt.NimbusJwtDecoder; import org.springframework.security.oauth3.jwt.NimbusJwtEncoder; import org.springframework.security.oauth3.server.resource.authentication.JwtAuthenticationConverter; import org.springframework.security.oauth3.server.resource.authentication.JwtGrantedAuthoritiesConverter; import org.springframework.security.oauth3.server.resource.web.BearerTokenAuthenticationEntryPoint; import org.springframework.security.oauth3.server.resource.web.access.BearerTokenAccessDeniedHandler; import org.springframework.security.web.SecurityFilterChain; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; /** * Spring Security 配置 * * @author cloudgyb * @since 2022/7/30 18:31 */ @Configuration(proxyBeanMethods = false) @EnableMethodSecurity public class WebSecurityConfigurer { //使用RSA对JWT做签名,所以这里需要一对秘钥。 //秘钥文件的路径在application.yml文件中做了配置(具体配置在下面)。 @Value("${jwt.public.key}") private RSAPublicKey key; @Value("${jwt.private.key}") private RSAPrivateKey priv; /** * 构建SecurityFilterChain bean */ @Bean SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { //"/login"是系统的登录接口,所以需要匿名可访问 http.authorizeRequests().antMatchers("/login").anonymous(); //其他请求都需认证后才能访问 http.authorizeRequests().anyRequest().authenticated() .and() //采用JWT认证无需session保持,所以禁用掉session管理器 .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() //login接口可能来自其他站点,所以对login不做csrf防护 .csrf((csrf) -> csrf.ignoringAntMatchers("/login")) //配置认证方式为JWT,并且配置了一个JWT认证装换器,用于去掉解析权限时的SCOOP_前缀 .oauth3ResourceServer().jwt().jwtAuthenticationConverter( JwtAuthenticationConverter() ); //配置认证失败或者无权限时的处理器 http.exceptionHandling((exceptions) -> exceptions .authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint()) .accessDeniedHandler(new BearerTokenAccessDeniedHandler()) ); //根据配置生成SecurityFilterChain对象 return http.build(); } /** * JWT解码器,用于认证时的JWT解码 */ @Bean JwtDecoder jwtDecoder() { return NimbusJwtDecoder.withPublicKey(this.key).build(); } /** * JWT编码器,生成JWT */ @Bean JwtEncoder jwtEncoder() { JWK jwk = new RSAKey.Builder(this.key).privateKey(this.priv).build(); JWKSource<SecurityContext> jwks = new ImmutableJWKSet<>(new JWKSet(jwk)); return new NimbusJwtEncoder(jwks); } /** * JWT认证解码时,去掉Spring Security对权限附带的默认前缀SCOOP_ */ @Bean JwtAuthenticationConverter JwtAuthenticationConverter() { final JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); jwtGrantedAuthoritiesConverter.setAuthorityPrefix(""); final JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter(); jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter); return jwtAuthenticationConverter; } }
上邊的設定需要在Spring Boot專案的Resource目錄下產生一對RSA秘鑰。application.yml
jwt: private.key: classpath:app.key public.key: classpath:app.pub
可以使用下面的網站進行產生:http://tools.jb51.net/password/rsa_encode/,
注意: 金鑰格式使用 PKCS#8,私鑰密碼為空。
還有一點要說明,我在程式碼中使用了Spring Boot的值注入:
@Value("${jwt.public.key}") private RSAPublicKey key; @Value("${jwt.private.key}") private RSAPrivateKey priv;有沒有很好奇Spring Boot是如何將yaml檔案中的字串對應的檔案轉換為RSAPublicKey和RSAPrivateKey ?
其實是Spring Security幫我們做了處理,在Spring Security中幫我們實作了一個轉換器ResourceKeyConverterAdapter,可以閱讀相關原始碼來更深入的了解。
至此我們的專案已經支援JWT認證了。
但是使用者需要在請求頭Authorization中攜帶合法的JWT才能通過認證,進而存取伺服器資源,那麼如何給使用者頒發一個合法的JWT呢?很簡單,可以提供一個登入接口,讓使用者輸入使用者名稱和密碼,匹配成功後頒發令牌即可。
其實並不是必須這樣做,還有其他方式,例如我們調用第三方接口,我們經常的做法是先去第三方申請,申請通過後我們就可以得到一個令牌。這個過程和上面的登入通過後頒發一個令牌是一樣的,都是透過合法的途徑獲得一個令牌!登入介面只有一個目的,就是要頒發給合法使用者令牌!
@RestController public class SysLoginController { private final SysLoginService sysLoginService; public SysLoginController(SysLoginService sysLoginService) { this.sysLoginService = sysLoginService; } @PostMapping("/login") public String login(@RequestBody LoginInfo loginInfo) { return sysLoginService.login(loginInfo); } }登入邏輯實作:
@Service public class SysLoginService { private final JwtEncoder jwtEncoder; private final SpringSecurityUserDetailsService springSecurityUserDetailsService; public SysLoginService(JwtEncoder jwtEncoder, SpringSecurityUserDetailsService springSecurityUserDetailsService) { this.jwtEncoder = jwtEncoder; this.springSecurityUserDetailsService = springSecurityUserDetailsService; } public String login(LoginInfo loginInfo) { //从用户信息存储库中获取用户信息 final UserDetails userDetails = springSecurityUserDetailsService.loadUserByUsername(loginInfo.getUsername()); final String password = userDetails.getPassword(); //匹配密码,匹配成功生成JWT令牌 if (password.equals(loginInfo.getPassword())) { return generateToken(userDetails); } //密码不匹配,抛出异常,Spring Security发现抛出该异常后会将http响应状态码设置为401 unauthorized throw new BadCredentialsException("密码错误!"); } private String generateToken(UserDetails userDetails) { Instant now = Instant.now(); //JWT过期时间为36000秒,也就是600分钟,10小时 long expiry = 36000L; String scope = userDetails.getAuthorities().stream() .map(GrantedAuthority::getAuthority) .collect(Collectors.joining(" ")); //将用户权限信息使用空格分割拼为字符串,放到JWT的payload的scope字段中,注意不要改变scope这个属性,这是Spring Security OAuth3 JWT默认处理方式,在JWT解码时需要读取该字段,转为用户的权限信息! JwtClaimsSet claims = JwtClaimsSet.builder() .issuer("self") .issuedAt(now) .expiresAt(now.plusSeconds(expiry)) .subject(userDetails.getUsername()) .claim("scope", scope) .build(); return this.jwtEncoder.encode(JwtEncoderParameters.from(claims)).getTokenValue(); } }

6. 測試
使用postman測試一下:
使用錯誤的密碼,會回傳401 Unauthorized的狀態碼,表示我們認證失敗!
使用正確的使用者名稱和密碼:
@RestController public class HelloController { @GetMapping("/") @PreAuthorize("hasAuthority('test')") public String hello(Authentication authentication) { return "Hello, " + authentication.getName() + "!"; } }###該介面需要使用者擁有"test"的權限,但登入使用者沒有該權限(只有一個app的權限),此時呼叫該介面:## #首先將上一步驟登入所獲得的令牌貼到token中:############
我们发送请求得到了403 Forbidden的响应,意思就是我们没有访问权限,此时我们将接口权限改为“app”:
@RestController public class HelloController { @GetMapping("/") @PreAuthorize("hasAuthority('app')") public String hello(Authentication authentication) { return "Hello, " + authentication.getName() + "!"; } }
重启项目。再次发起请求:
以上是怎麼使用SpringBoot+SpringSecurity+JWT實現系統認證與授權的詳細內容。更多資訊請關注PHP中文網其他相關文章!

JVM的工作原理是將Java代碼轉換為機器碼並管理資源。 1)類加載:加載.class文件到內存。 2)運行時數據區:管理內存區域。 3)執行引擎:解釋或編譯執行字節碼。 4)本地方法接口:通過JNI與操作系統交互。

JVM使Java實現跨平台運行。 1)JVM加載、驗證和執行字節碼。 2)JVM的工作包括類加載、字節碼驗證、解釋執行和內存管理。 3)JVM支持高級功能如動態類加載和反射。

Java應用可通過以下步驟在不同操作系統上運行:1)使用File或Paths類處理文件路徑;2)通過System.getenv()設置和獲取環境變量;3)利用Maven或Gradle管理依賴並測試。 Java的跨平台能力依賴於JVM的抽象層,但仍需手動處理某些操作系統特定的功能。

Java在不同平台上需要進行特定配置和調優。 1)調整JVM參數,如-Xms和-Xmx設置堆大小。 2)選擇合適的垃圾回收策略,如ParallelGC或G1GC。 3)配置Native庫以適應不同平台,這些措施能讓Java應用在各種環境中發揮最佳性能。

Osgi,Apachecommonslang,JNA和JvMoptionsareeForhandlingForhandlingPlatform-specificchallengesinjava.1)osgimanagesdeppedendendencenciesandisolatescomponents.2)apachecommonslangprovidesitorityfunctions.3)

JVMmanagesgarbagecollectionacrossplatformseffectivelybyusingagenerationalapproachandadaptingtoOSandhardwaredifferences.ItemploysvariouscollectorslikeSerial,Parallel,CMS,andG1,eachsuitedfordifferentscenarios.Performancecanbetunedwithflagslike-XX:NewRa

Java代碼可以在不同操作系統上無需修改即可運行,這是因為Java的“一次編寫,到處運行”哲學,由Java虛擬機(JVM)實現。 JVM作為編譯後的Java字節碼與操作系統之間的中介,將字節碼翻譯成特定機器指令,確保程序在任何安裝了JVM的平台上都能獨立運行。

Java程序的編譯和執行通過字節碼和JVM實現平台獨立性。 1)編寫Java源碼並編譯成字節碼。 2)使用JVM在任何平台上執行字節碼,確保代碼的跨平台運行。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

SublimeText3 Linux新版
SublimeText3 Linux最新版

SublimeText3漢化版
中文版,非常好用

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具