欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot+SpringSecurity+JWT實現(xiàn)系統(tǒng)認證與授權(quán)示例

 更新時間:2022年08月07日 09:50:31   作者:Cloud-Future  
本文主要介紹了SpringBoot+SpringSecurity+JWT實現(xiàn)系統(tǒng)認證與授權(quán)示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

1. Spring Security簡介

Spring Security是Spring的一個核心項目,它是一個功能強大且高度可定制的認證和訪問控制框架。它提供了認證和授權(quán)功能以及抵御常見的攻擊,它已經(jīng)成為保護基于spring的應(yīng)用程序的事實標(biāo)準(zhǔn)。

Spring Boot提供了自動配置,引入starter依賴即可使用。
Spring Security特性總結(jié):

  • 使用簡單,提供Spring Boot starter依賴,極易與Spring Boot項目整合。
  • 專業(yè),提供CSRF防護、點擊劫持防護、XSS防護等,并提供各種安全頭整合(X-XSS-Protection,X-Frame-Options等)。
  • 密碼加密存儲,支持多種加密算法
  • 擴展性和可定制性極強
  • OAuth2 JWT認證支持
  • … …

2. JWT簡介

JWT(Json web token),是為了在網(wǎng)絡(luò)應(yīng)用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開放標(biāo)準(zhǔn)(RFC 7519).該token被設(shè)計為緊湊且安全的,特別適用于分布式站點的單點登錄(SSO)場景。JWT的聲明一般被用來在身份提供者和服務(wù)提供者間傳遞被認證的用戶身份信息,以便于從資源服務(wù)器獲取資源,也可以增加一些額外的其它業(yè)務(wù)邏輯所必須的聲明信息(例如,權(quán)限信息)。一旦用戶被授予token,用戶即可通過該token訪問服務(wù)器上的資源。

https://jwt.io/,該網(wǎng)站提供了一個debuggr,便于初學(xué)者學(xué)習(xí)理解JWT。

3. Spring Boot整合Spring Security

注意本篇文章演示使用JDK和Spring Boot的版本如下:
Spring Boot:2.7.2
JDK:11
不同的Spring Boot版本配置不同,但是原理相同。

在Spring Boot項目的pom.xml文件中加入下面的依賴:

<!-- Spring Security的Spring boot starter,引入后將自動啟動Spring Security的自動配置 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- 下面的依賴包含了OAuth2 JWT認證實現(xiàn) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

以上兩個依賴即可。

4. 配置Spring Security使用JWT認證

注意: 不同的Spring Boot版本配置不同,但是原理相同,本文使用的是Spring Boot:2.7.2。

主要是配置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.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.oauth2.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;

     /**
      * 構(gòu)建SecurityFilterChain bean
      */
    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        //"/login"是系統(tǒng)的登錄接口,所以需要匿名可訪問
        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認證裝換器,用于去掉解析權(quán)限時的SCOOP_前綴
                .oauth2ResourceServer().jwt().jwtAuthenticationConverter(
                        JwtAuthenticationConverter()
                );
        //配置認證失敗或者無權(quán)限時的處理器
        http.exceptionHandling((exceptions) -> exceptions
                .authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
                .accessDeniedHandler(new BearerTokenAccessDeniedHandler())
        );
         //根據(jù)配置生成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對權(quán)限附帶的默認前綴SCOOP_
     */
    @Bean
    JwtAuthenticationConverter JwtAuthenticationConverter() {
        final JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        jwtGrantedAuthoritiesConverter.setAuthorityPrefix("");
        final JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
        return jwtAuthenticationConverter;
    }
}

application.yml

jwt:
  private.key: classpath:app.key
  public.key: classpath:app.pub

上邊的配置需要在Spring Boot項目的Resource目錄下生成一對RSA秘鑰。
可以使用下面的網(wǎng)站進行生成: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文件中的字符串對應(yīng)的文件轉(zhuǎn)換為RSAPublicKey和RSAPrivateKey ?
其實是Spring Security幫我們做了處理,在Spring Security中幫我們實現(xiàn)了一個轉(zhuǎn)換器ResourceKeyConverterAdapter,具體可以閱讀相關(guān)源碼來更深入的了解。

至此我們的項目已經(jīng)支持JWT認證了。
但是用戶需要在請求頭Authorization中攜帶合法的JWT才能通過認證,進而訪問服務(wù)器資源,那么如何給用戶頒發(fā)一個合法的JWT呢?
很簡單,可以提供一個登錄接口,讓用戶輸入用戶名和密碼,匹配成功后頒發(fā)令牌即可。

其實并不是必須這樣做,還有其他方式,比如我們調(diào)用第三方接口,我們經(jīng)常的做法是先去第三方申請,申請通過后我們就可以得到一個令牌。這個過程和上面的登錄通過后頒發(fā)一個令牌是一樣的,都是通過合法的途徑獲得一個令牌!

5. 實現(xiàn)登錄接口

登錄接口只有一個目的,就是給合法用戶頒發(fā)令牌!
登錄API接口:

@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);
    }
}

登錄邏輯實現(xiàn):

@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發(fā)現(xiàn)拋出該異常后會將http響應(yīng)狀態(tài)碼設(shè)置為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(" "));
         //將用戶權(quán)限信息使用空格分割拼為字符串,放到JWT的payload的scope字段中,注意不要改變scope這個屬性,這是Spring Security OAuth2 JWT默認處理方式,在JWT解碼時需要讀取該字段,轉(zhuǎn)為用戶的權(quán)限信息!
        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();
    }
}

其他非核心代碼這里就不貼出來了,我將代碼放到github上了,具體可以轉(zhuǎn)到https://github.com/cloudgyb/spring-security-study-jwt

6. 測試

使用postman測試一下:
使用錯誤的密碼,會返回401 Unauthorized的狀態(tài)碼,表示我們認證失敗!

在這里插入圖片描述

使用正確的用戶名和密碼:

在這里插入圖片描述

返回了JWT令牌。

此時客戶端拿到了合法的令牌,接下來就可以訪問服務(wù)器上有權(quán)訪問的資源了。
我寫了一個測試接口:

@RestController
public class HelloController {

    @GetMapping("/")
    @PreAuthorize("hasAuthority('test')")
    public String hello(Authentication authentication) {
        return "Hello, " + authentication.getName() + "!";
    }
}

該接口需要用戶擁有"test"的權(quán)限,但是登錄用戶沒有該權(quán)限(只有一個app的權(quán)限),此時調(diào)用該接口:
首先將上一步登錄獲得的令牌粘貼到token中:

在這里插入圖片描述

我們發(fā)送請求得到了403 Forbidden的響應(yīng),意思就是我們沒有訪問權(quán)限,此時我們將接口權(quán)限改為“app”:

@RestController
public class HelloController {

    @GetMapping("/")
    @PreAuthorize("hasAuthority('app')")
    public String hello(Authentication authentication) {
        return "Hello, " + authentication.getName() + "!";
    }
}

重啟項目。再次發(fā)起請求:

在這里插入圖片描述

我們已經(jīng)可以正常訪問了!

Spring Security專業(yè)性很強,有些術(shù)語對于初學(xué)者可能有點難度,但是一旦掌握這些概念,你會喜歡上Spring Security的!

7. 源碼

這兒有一個可直接運行的demo供參考:https://github.com/cloudgyb/spring-security-study-jwt

到此這篇關(guān)于SpringBoot+SpringSecurity+JWT實現(xiàn)系統(tǒng)認證與授權(quán)示例的文章就介紹到這了,更多相關(guān)SpringBoot SpringSecurity JWT認證授權(quán)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java消息隊列的簡單實現(xiàn)代碼

    Java消息隊列的簡單實現(xiàn)代碼

    本篇文章主要介紹了Java消息隊列的簡單實現(xiàn)代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • Java實現(xiàn)文件變化監(jiān)聽代碼實例

    Java實現(xiàn)文件變化監(jiān)聽代碼實例

    這篇文章主要介紹了Java實現(xiàn)文件變化監(jiān)聽代碼實例,通過定時任務(wù),輪訓(xùn)查詢文件的最后修改時間,與上一次進行對比,如果發(fā)生變化,則說明文件已經(jīng)修改,進行重新加載或?qū)?yīng)的業(yè)務(wù)邏輯處理,需要的朋友可以參考下
    2024-01-01
  • Springboot中@Value的使用詳解

    Springboot中@Value的使用詳解

    這篇文章主要介紹了Springboot中@Value的使用詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • Java基于LoadingCache實現(xiàn)本地緩存的示例代碼

    Java基于LoadingCache實現(xiàn)本地緩存的示例代碼

    本文主要介紹了Java基于LoadingCache實現(xiàn)本地緩存的示例代碼,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • MyBatis不同Mapper文件引用resultMap實例代碼

    MyBatis不同Mapper文件引用resultMap實例代碼

    這篇文章主要介紹了mybatis 不同Mapper文件引用resultMap的實例代碼,非常不錯具有參考借鑒價值,需要的朋友可以參考下
    2017-07-07
  • SpringMVC中攔截器的實現(xiàn)

    SpringMVC中攔截器的實現(xiàn)

    SpringMVC 中的 Interceptor 攔截器是非常重要和相當(dāng)有用的,它的主要作用是攔截指定的用戶請求,并進行相應(yīng)的預(yù)處理與后處理,這篇文章主要介紹了SpringMVC的攔截器相關(guān)知識,需要的朋友可以參考下
    2022-01-01
  • idea安裝jerbel及文件上傳下載的實現(xiàn)示例

    idea安裝jerbel及文件上傳下載的實現(xiàn)示例

    JRebel是一個Java開發(fā)工具,它是一款用于實時代碼重載的插件,本文主要介紹了idea安裝jerbel及文件上傳下載的實現(xiàn)示例,具有一定的參考價值,感興趣的可以了解下
    2023-09-09
  • Springmvc ResponseBody響應(yīng)json數(shù)據(jù)實現(xiàn)過程

    Springmvc ResponseBody響應(yīng)json數(shù)據(jù)實現(xiàn)過程

    這篇文章主要介紹了Springmvc ResponseBody響應(yīng)json數(shù)據(jù)實現(xiàn)過程,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-10-10
  • java向下轉(zhuǎn)型基礎(chǔ)知識點及實例

    java向下轉(zhuǎn)型基礎(chǔ)知識點及實例

    在本篇文章里小編給大家整理的是一篇關(guān)于java向下轉(zhuǎn)型基礎(chǔ)知識點及實例內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。
    2021-05-05
  • Java實現(xiàn)簡易Web服務(wù)器

    Java實現(xiàn)簡易Web服務(wù)器

    這篇文章主要為大家詳細介紹了Java實現(xiàn)簡易Web服務(wù)器的相關(guān)方法,想要制作Web服務(wù)器的朋友可以參考本文
    2016-02-02

最新評論