自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

SpringBoot3 構(gòu)建Spring Authorization Server認(rèn)證服務(wù)!

開(kāi)發(fā) 前端
OAuth 2.0(Open Authorization 2.0)是一種授權(quán)框架,允許第三方應(yīng)用程序訪問(wèn)用戶在另一個(gè)服務(wù)提供者上托管的資源,而無(wú)需共享用戶的憑據(jù)(例如用戶名和密碼)。

在之前的SpringCloud微服務(wù)專(zhuān)欄中,我介紹了基于 Spring Security OAuth2 構(gòu)建的統(tǒng)一認(rèn)證服務(wù)器。隨著技術(shù)的不斷發(fā)展,Spring Security OAuth2 已于2022年6月5日宣布停止維護(hù)。為了應(yīng)對(duì)這一變化,Spring 官方推出了新產(chǎn)品——Spring Authorization Server。該組件實(shí)現(xiàn)了 OAuth 2.1協(xié)議 和 OpenID Connect 1.0 規(guī)范以及其他相關(guān)規(guī)范的實(shí)現(xiàn),它構(gòu)建在 Spring Security 之上,為構(gòu)建 OpenID Connect 1.0 Identity Provider 和 OAuth2 Authorization Server 產(chǎn)品提供安全、輕量級(jí)和可定制的基礎(chǔ)。

接下來(lái)我將使用 Spring Authorization Server (以下簡(jiǎn)稱(chēng)SAS) 來(lái)更新原專(zhuān)欄的認(rèn)證服務(wù),今天先讓我們來(lái)搭建一個(gè)簡(jiǎn)單的認(rèn)證服務(wù)認(rèn)識(shí)一下Spring Authorization Server。

概念理解

Oauth2.0

OAuth 2.0(Open Authorization 2.0)是一種授權(quán)框架,允許第三方應(yīng)用程序訪問(wèn)用戶在另一個(gè)服務(wù)提供者上托管的資源,而無(wú)需共享用戶的憑據(jù)(例如用戶名和密碼)。

在Oauth2.0中,定義了四種角色:

?資源所有者(Resource Owner),?客戶端(Client),?資源服務(wù)器(Resource Server),?授權(quán)服務(wù)器(Authorization Server)

以及四種授權(quán)模式:

?授權(quán)碼授權(quán)(Authorization Code Grant),?隱式授權(quán)(Implicit Grant),?密碼授權(quán)(Resource Owner Password Credentials Grant),?客戶端憑證授權(quán)(Client Credentials Grant)。

關(guān)于Oauth2.0的詳細(xì)概念及認(rèn)證流程網(wǎng)上已經(jīng)有大量的文章說(shuō)明,這里不再贅述。

Oauth2.1

OAuth 2.1 在 OAuth 2.0 的基礎(chǔ)上進(jìn)行了以下改進(jìn):

?推薦使用 Authorization Code+PKCE 模式授權(quán)

授權(quán)碼 (Authorization Code) 模式大家都很熟悉了,也是最安全的授權(quán)流程, 那 PKCE 又是什么呢? PKCE 全稱(chēng)是 Proof Key for Code Exchange, 在 2015 年發(fā)布為 RFC 7636, 我們知道, 授權(quán)碼模式雖好, 但是它不能給公開(kāi)的客戶端用, 因?yàn)楣_(kāi)的客戶端沒(méi)有能力保存好秘鑰(client_secret), 所以在此之前, 對(duì)于公開(kāi)的客戶端, 只能使用隱式模式和密碼模式, PKCE 就是為了解決這個(gè)問(wèn)題而出現(xiàn)的, 另外它也可以防范授權(quán)碼攔截攻擊, 實(shí)際上它的原理是客戶端提供一個(gè)自創(chuàng)建的證明給授權(quán)服務(wù)器, 授權(quán)服務(wù)器通過(guò)它來(lái)驗(yàn)證客戶端,把訪問(wèn)令牌(access_token) 頒發(fā)給真實(shí)的客戶端而不是偽造的,以下是其流程圖

圖片圖片

?移除隱式授權(quán)模式

?移除密碼模式

OpenID Connect(OIDC)

OIDC是OpenID Connect的簡(jiǎn)稱(chēng),OIDC=(Identity, Authentication) + OAuth 2.0,它在原Oauth2.0的基礎(chǔ)上構(gòu)建了一個(gè)身份層,是一個(gè)基于OAuth2協(xié)議的身份認(rèn)證標(biāo)準(zhǔn)協(xié)議。我們都知道OAuth2是一個(gè)授權(quán)協(xié)議,它無(wú)法提供完善的身份認(rèn)證功能。OIDC使用OAuth2的授權(quán)服務(wù)器來(lái)為第三方客戶端提供用戶的身份認(rèn)證,并把對(duì)應(yīng)的身份認(rèn)證信息通過(guò)一個(gè)叫ID Token 的東西傳遞給客戶端,ID Token使用JWT格式來(lái)包裝,使得ID Token可以安全的傳遞給第三方客戶端程序并且容易被驗(yàn)證。如果ID Token返回的內(nèi)容不夠,授權(quán)服務(wù)器還提供一個(gè)UserInfo接口,可以獲取用戶更完整的信息。在可以選擇 OIDC 的情況下,應(yīng)該選擇 OIDC。

如下是一個(gè)ID_Token解析后的例子,包含不限于以下幾個(gè)字段信息

{
  "sub": "dailymart",                # 用戶ID
  "aud": "oidc-client",                # ID Token的受眾,即Client_ID            
  "auth_time": 1722780563,          # 完成認(rèn)證的時(shí)間
  "iss": "http://127.0.0.1:9090",   # 發(fā)行人,即認(rèn)證服務(wù)器
  "exp": 1722782868,                # 到期時(shí)間
  "iat": 1722781068,                # 發(fā)布時(shí)間
    ...
}

SAS上手體驗(yàn)

SpringBoot集成SAS

1、引入spring-boot-starter-oauth2-authorization-server

在SpringBoot3.1中提供了對(duì)SAS的支持,只需要引入依賴即可完成授權(quán)服務(wù)器的搭建

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
</dependency>

DDD項(xiàng)目當(dāng)前使用的SpringBoot版本是3.2.7,對(duì)應(yīng)SAS版本為1.2.5。

如果需要嘗試其他版本,也可以手動(dòng)引入,如:

<groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-authorization-server</artifactId>
    <version>1.3.1</version>
</dependency>

2、認(rèn)證服務(wù)器配置AuthorizationServerConfig

@Slf4j
@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfig {


    /**
     * Security過(guò)濾器鏈,用于協(xié)議端點(diǎn)
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SecurityFilterChain authorizationServerSecurityFilterChain (HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity (http);
        // 開(kāi)啟OIDC
        http.getConfigurer (OAuth2AuthorizationServerConfigurer.class)
                .oidc (Customizer.withDefaults ());


        http
            .exceptionHandling ((exceptions) -> exceptions
                    .defaultAuthenticationEntryPointFor (
                            new LoginUrlAuthenticationEntryPoint ("/login"),
                            new MediaTypeRequestMatcher (MediaType.TEXT_HTML)
                    )
            )
            //接受用戶信息和/或客戶端注冊(cè)的訪問(wèn)令牌
            .oauth2ResourceServer ((resourceServer) -> resourceServer
                    .jwt (Customizer.withDefaults ()));


        return http.build ();
    }


    /**
     * 配置密碼解析器,使用BCrypt的方式對(duì)密碼進(jìn)行加密和驗(yàn)證
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }






    /**
     * 管理客戶端
     * @param passwordEncoder 密碼管理器
     */
    @Bean
    public RegisteredClientRepository registeredClientRepository(PasswordEncoder passwordEncoder) {
        RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("oidc-client")
                .clientSecret(passwordEncoder.encode("123456"))
                //客戶端認(rèn)證基于請(qǐng)求頭
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
                .redirectUri("http://127.0.0.1:8080/login/oauth2/code/oidc-client") // 頁(yè)面地址需要跟這個(gè)保持一致
                .postLogoutRedirectUri("http://127.0.0.1:8080/")
                .scope(OidcScopes.OPENID)
                .scope(OidcScopes.PROFILE)
                .scope("user.info")
                .scope("all")
                // 客戶端設(shè)置,設(shè)置用戶需要確認(rèn)授權(quán),設(shè)置false后不需要確認(rèn)
                .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
                //設(shè)置accessToken有效期
                .tokenSettings(TokenSettings.builder().accessTokenTimeToLive(Duration.ofHours(2)).build())
                .build();


        return new InMemoryRegisteredClientRepository(oidcClient);
    }




    /**
     * 用于簽署訪問(wèn)令牌
     */
    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        KeyPair keyPair = generateRsaKey();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        RSAKey rsaKey = new RSAKey.Builder(publicKey)
                .privateKey(privateKey)
                .keyID(UUID.randomUUID().toString())
                .build();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return new ImmutableJWKSet<>(jwkSet);
    }


    /**
     * 創(chuàng)建RsaKey
     */
    private static KeyPair generateRsaKey() {
        KeyPair keyPair;
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048);
            keyPair = keyPairGenerator.generateKeyPair();
        }
        catch (Exception ex) {
            log.error ("generateRsaKey Exception", ex);
            throw new IllegalStateException(ex);
        }
        return keyPair;
    }




    /**
     * 解碼簽名訪問(wèn)令牌
     */
    @Bean
    public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
        return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
    }






    @Bean
    public AuthorizationServerSettings authorizationServerSettings() {
        return AuthorizationServerSettings.builder().build();
    }




}

在 這段代碼中我們基于內(nèi)存模式(InMemory)構(gòu)建了一個(gè)oidc-client客戶端,客戶端通過(guò)請(qǐng)求頭的形式進(jìn)行認(rèn)證,并支持授權(quán)碼、刷新碼、客戶端三種認(rèn)證方式,通過(guò)tokenSettings將access_token的有效期設(shè)置成2小時(shí)。

3、Spring Security 安全配置

@EnableWebSecurity 
@Configuration(proxyBeanMethods = false) 
public class DefaultSecurityConfig {


    /**
     * 用于認(rèn)證的Spring Security過(guò)濾器鏈。
     */
    @Bean
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
            throws Exception {
        http
                .authorizeHttpRequests((authorize) ->
                        authorize
                        .requestMatchers("/assets/**","/webjars/**","/actuator/**","/oauth2/**","/login").permitAll()
                        .anyRequest().authenticated()
                )
                .cors(Customizer.withDefaults())
                .csrf(AbstractHttpConfigurer::disable)
                .formLogin(Customizer.withDefaults());


        return http.build();
    }




    /**
     * 配置內(nèi)存用戶
     * @param passwordEncoder 密碼管理器
     */
    @Bean
    public UserDetailsService users(PasswordEncoder passwordEncoder) {


        UserDetails userDetails = User.withUsername("dailymart")
                .password(passwordEncoder.encode("123456"))
                .roles("USER")
                .build();


        return new InMemoryUserDetailsManager(userDetails);
    }




}

在這里我們構(gòu)建了一個(gè)InMemory的dailymart用戶,這些代碼使用過(guò)Spring Security OAuth2的同學(xué)來(lái)說(shuō)肯定很熟悉。

通過(guò)上面的三步,我們就構(gòu)建了一個(gè)最基礎(chǔ)的認(rèn)證服務(wù)器。

授權(quán)碼模式演示

1、啟動(dòng)認(rèn)證服務(wù)器后(9090)我們?cè)L問(wèn)如下地址獲取token

http://127.0.0.1:9090/oauth2/authorize?client_id=oidc-client&response_type=code&scope=user.info+openid&redirect_uri=http://127.0.0.1:8080/login/oauth2/code/oidc-client

注意,SAS會(huì)校驗(yàn)redirect_url與客戶端中配置的是否一致,此參數(shù)不能亂配置。

2、SpringSecurity檢測(cè)到用戶未登錄,跳轉(zhuǎn)至登錄頁(yè)面

圖片圖片

3、登錄以后然后系統(tǒng)會(huì)跳轉(zhuǎn)至確認(rèn)授權(quán)頁(yè)面(ClientSettings.builder().requireAuthorizationConsent(true)),確認(rèn)授權(quán)以后再跳轉(zhuǎn)到redirect_url上,并在參數(shù)中返回code

圖片

4、通過(guò)postman調(diào)用oauth2接口獲取access_token

圖片圖片

在第一步的scope參數(shù)中我們申請(qǐng)了openid權(quán)限,這個(gè)時(shí)候SAS會(huì)啟用OIDC協(xié)議并返回ID_TOKEN,如果未申請(qǐng)openid則是默認(rèn)的oauth2協(xié)議。

圖片圖片

此時(shí)我們將id_token解開(kāi)即可獲得用戶信息。

5、 獲取用戶詳細(xì)信息

SAS提供一個(gè)userInfo接口用于獲取用戶的詳細(xì)信息,通過(guò)postman調(diào)用并在請(qǐng)求頭中設(shè)置上一步拿到的access_token

圖片圖片

6、我們還可以通過(guò)瀏覽器訪問(wèn)http://127.0.0.1:9090/.well-known/openid-configuration以獲取認(rèn)證服務(wù)器的詳細(xì)信息

{
    "issuer": "http://127.0.0.1:9090",
    "authorization_endpoint": "http://127.0.0.1:9090/oauth2/authorize",
    "device_authorization_endpoint": "http://127.0.0.1:9090/oauth2/device_authorization",
    "token_endpoint": "http://127.0.0.1:9090/oauth2/token",
    "token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post", "client_secret_jwt", "private_key_jwt"],
    "jwks_uri": "http://127.0.0.1:9090/oauth2/jwks",
    "userinfo_endpoint": "http://127.0.0.1:9090/userinfo",
    "end_session_endpoint": "http://127.0.0.1:9090/connect/logout",
    "response_types_supported": ["code"],
    "grant_types_supported": ["authorization_code", "client_credentials", "refresh_token", "urn:ietf:params:oauth:grant-type:device_code"],
    "revocation_endpoint": "http://127.0.0.1:9090/oauth2/revoke",
    "revocation_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post", "client_secret_jwt", "private_key_jwt"],
    "introspection_endpoint": "http://127.0.0.1:9090/oauth2/introspect",
    "introspection_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post", "client_secret_jwt", "private_key_jwt"],
    "code_challenge_methods_supported": ["S256"],
    "subject_types_supported": ["public"],
    "id_token_signing_alg_values_supported": ["RS256"],
    "scopes_supported": ["openid"]
}

小結(jié)

本篇文章我們先熟悉一下如何基于spring-boot-starter-oauth2-authorization-server構(gòu)建認(rèn)證服務(wù)器,后面幾篇文章我們將對(duì)其進(jìn)行改造讓其符合生產(chǎn)使用。

責(zé)任編輯:武曉燕 來(lái)源: JAVA日知錄
相關(guān)推薦

2023-08-07 14:28:07

SpringBoot工具

2021-11-10 05:00:30

服務(wù)器Spring授權(quán)

2023-08-09 08:29:51

SpringWeb編程

2021-08-18 10:36:43

Sping社區(qū)實(shí)驗(yàn)項(xiàng)目服務(wù)器

2023-08-11 08:59:49

分庫(kù)分表數(shù)據(jù)數(shù)據(jù)庫(kù)

2024-10-08 08:26:43

2024-07-31 14:03:00

Spring定時(shí)任務(wù)管理

2021-11-11 07:38:15

服務(wù)器過(guò)濾器框架

2023-08-08 08:23:08

Spring日志?線程池

2023-06-19 08:05:17

RFCwebSpring

2024-03-04 08:19:11

SpringURLHeader

2024-05-10 08:10:05

Spring虛擬線程JDK

2023-02-01 10:40:01

2017-06-26 09:06:10

Spring Clou微服務(wù)架構(gòu)

2025-03-28 09:33:11

2017-09-04 16:15:44

服務(wù)網(wǎng)關(guān)架構(gòu)

2022-10-10 08:00:00

微服務(wù)Spring Boo容器

2024-10-11 11:37:38

2014-11-18 11:20:51

webserviceCXFSpring

2024-01-31 08:26:44

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)