OAuth2使用文檔

翻譯自:spring-security-oauth2-boot

寫在前面的話:
一直在學習Spring OAuth2 ,但是網上的講解材料讓我看了頭疼,而且一直也沒有能讓我一次就順利運行起來的例子。不得已只能翻看官方文檔。但是以我半吊子英語水平,很多句子的意思都是很模糊,甚至有時候猜測的意思完全相反。正好最近趁着在家,嘗試翻譯一下官方文檔。
受英語水平所限,儘管有有道翻譯和GOOGLE翻譯,難免有地方翻譯不準確或描述不準確,當然亦有可能有各種錯誤。如果你看到錯誤或有任何建議,歡迎留言反饋和討論。如果你想轉載,請註明出處: https://blog.csdn.net/levelmini

PS:

  1. 文章由我一個人翻譯完成,沒有校對。而且部分內容爲機翻,我沒有修改潤色。
  2. Google翻譯比有道翻譯好用太多啦。
  3. 翻譯對我這樣的人來說真是辛苦活。像以前我讀到所有的官方文檔翻譯人員致敬。
  4. CSDN的博客好多都是無腦轉載,而且沒有原文鏈接,說自己原創。希望這種情況儘快改善。要不然我就要轉戰其他博客啦。
  5. MD格式真好用。但是csdn的Markdown編輯器的章節內跳轉爲啥不好使呢?

OAuth2 Boot


如果你在classpath中引入了spring-security-oauth2,你可以利用一些自動配置來簡化授權和資源服務端的設置。想要獲取所有細節,訪問Spring Security OAuth 2 Developers Guide.

以下項目在正在持續維護中:

  • spring-security-oauth2
  • spring-security-oauth2-autoconfigure

歡迎使用這些項目,我們爲你提供幫助。
然而,在選擇spring-security-oauth2spring-security-oauth2-autoconfigure之前,你應該檢查Spring Security的特性矩陣,看看新的支持是否滿足您的需要。

(以下內容爲機翻)
該項目是Spring Boot 1.x附帶的Spring安全OAuth支持的一個端口。在Spring Boot 2中刪除了支持以支持Spring Security 5的一流OAuth支持
This project is a port of the Spring Security OAuth support that came with Spring Boot 1.x. Support was removed in Spring Boot 2.x in favor of Spring Security 5’s first-class OAuth support.

爲了簡化遷移,這個項目作爲舊Spring安全OAuth支持和Spring Boot 2.x之間的橋樑存在。
To ease migration, this project exists as a bridge between the old Spring Security OAuth support and Spring Boot 2.x.
(機翻完畢)

1. 授權服務端

Spring Security OAuth2 Boot 簡化了OAuth 2.0授權服務端的部署。

1.1. 我需要建立自己的授權服務端嗎?

你需要部署你自己的授權服務端,如果:

  • 您希望將登錄、退出和密碼恢復的操作委託給一個單獨的服務(也稱爲身份聯合),並由您自己管理。
  • 您希望對這個單獨的服務使用OAuth 2.0協議來與其他服務進行協調。

1.2. Dependencies

要使用此庫中的auto-configuration功能,你需要spring-security-oauth2,它具有OAuth 2.0基本功能和spring-security-autoconfigure。請注意你需要指定spring-security-autoconfigure的版本,因爲其不再由Spring Boot管理,但需要和Spring Boot的版本兼容。
如果需要支持JWT,你需要spring-security-jwt

1.3. 最小化的 OAuth2 Boot 配置

創建最小的Spring Boot 授權服務端需要包含三個基本步驟:

  1. 引入相關依賴。
  2. 引入@EnableAuthorizationServer註解。
  3. 指定至少一個 client id 和 secret對。

1.3.1. 激活授權服務端

像其他的Spring Boot @Enable註解一樣,你可以在包含main方法的類中添加@EnableAuthorizationServer註解,例:

@EnableAuthorizationServer
@SpringBootApplication
public class SimpleAuthorizationServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(SimpleAuthorizationServerApplication, args);
    }
}

添加這個註解會引入其他Spring的配置文件,這些文件會天界一些合理的默認值,比如Token如可被簽名、Token的持續時間、以及允許的授權範圍。

1.3.2. 指定Client和Secret

根據規範,許多OAuth 2.0端點都需要客戶端身份授權,因此您需要至少指定一個客戶端,以便任何人都可以與您的授權服務端進行通信。
下面這個例子展示瞭如何指定一個客戶端:

security:
  oauth2:
    client:
      client-id: first-client
      client-secret: noonewilleverguess

儘管這很方便,但是生產環境中你需要做更多事情。(比如將此配置移入數據庫中來更靈活的管理,譯者注)

1.3.3. 獲取Token

OAuth 2.0本質上是一個框架,它指定了將長期的令牌替換爲短期的令牌的策略。
默認情況下,@ EnableAuthorizationServer授予客戶端訪問客戶端憑據的權限,這意味着您可以執行以下操作:
curl first-client:noonewilleverguess@localhost:8080/oauth/token -dgrant_type=client_credentials -dscope=any
應用會響應一個token,類似:

{
    "access_token" : "f05a1ea7-4c80-4583-a123-dc7a99415588",
    "token_type" : "bearer",
    "expires_in" : 43173,
    "scope" : "any"
}

此令牌可以提供給任何支持不透明的OAuth 2.0令牌的資源服務端,並配置爲指向此授權服務端進行授權。
你可以從此跳到如下章節:

  • [章節1.4,如何關閉OAuth2 Boot 的自動配置](# 1.4. 如何關閉OAuth2 Boot 的自動配置)
  • [章節1.5, 如何創建Authorization Code授權模式](# 1.5. 如何創建Authorization Code授權模式)
  • [章節1.6, 如何創建Password授權模式](# 1.6. 如何創建Password授權模式)
  • [章節1.7,如何以及何時向授權服務端提供AuthenticationMamager](# 1.7. 如何以及何時向授權服務端提供AuthenticationMamager)
  • [章節1.8,授權服務端兼容Spring Security 5.1的資源服務端和客戶端嗎?](# 1.8. 授權服務端兼容Spring Security 5.1的資源服務端和客戶端嗎?)
  • [如何配置Jwt Tokens](# 2.2.2.1. JWT)

1.4. 如何關閉OAuth2 Boot 的自動配置

基本上,OAuth2 Boot項目會創建一個AuthorizationServerConfigurer實例,該實例帶有一些合理的默認值:

  • 註冊了一個NoOpPasswordEncoder(覆蓋SpringSeuciryt默認值
  • 使你提供的客戶端可以使用此授權服務端提供的任何一個授權模式:authorization_codepasswordclient_credentialsimplicitrefresh_token.
    另外,項目亦會嘗試創建一些有用的bean,他們的名字是:
  • AuthenticationManager:用於尋找終端用戶(非客戶端)
  • TokenStore:用於生成和檢索令牌。
  • AccessTokenConverter:用於將訪問令牌轉化成不同的格式,如JWT。

雖然本文檔簡單介紹了這些bean的作用,但Spring Security Oauth文檔更適合獲取他們的基礎信息。

如果你聲明瞭一個類型爲AuthorizationServerConfigurer的bean,那麼這些不會自動完成。
因此,如果您需要配置多個客戶端,更改其允許的授權類型,或使用比NoOpPasswordEncoder更好的方法(強烈建議!),那麼您需要定義自己的AuthorizationServerConfigurer,如:

@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired DataSource dataSource;

    protected void configure(ClientDetailsServiceConfigurer clients) {
        clients
            .jdbc(this.dataSource)
            .passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());
    }
}

上面的配置使OAuth2 Boot不再從環境屬性中檢索客戶端,並回退到Spring Security密碼編碼器的默認值。

讀到這,你可能想要了解更多信息比如:

  • [章節1.5 如何創建Authorization Code授權模式](# 1.5. 如何創建Authorization Code授權模式)
  • [章節1.6 如何創建Password授權模式](# 1.6. 如何創建Password授權模式)

1.5. 如何創建Authorization Code授權模式

使用默認配置時,從技術上講,Authorization Code模式已經可行,但實際上其仍然未配置完全。
因爲除了預先的配置外,Authorization Code模式還需要:

  • 終端用戶
  • 終端用戶的登錄流程
  • 在客戶端中註冊一個重定向URI

1.5.1. 添加終端用戶

在受Spring Security保護的典型Spring Boot應用程序中,用戶由UserDetailsService定義。而授權服務端也一樣:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        return new InMemoryUserDetailsManager(
            User.withDefaultPasswordEncoder()
                .username("enduser")
                .password("password")
                .roles("USER")
                .build());
    }
}

請注意,像典型的Spring Security Web應用一樣,用戶在WebSecurityConfigurerAdapter實例中定義。

1.5.2. 添加終端用戶的登錄流程

順便一提,添加WebSecurityConfigurerAdapter實例是我們現在爲終端用戶用戶添加表單登錄流程所需要的。但是,請注意,這是有關Web應用程序本身的所有其他配置(而不是OAuth 2.0 API)的地方。
如果你想自定義登錄頁,WebSecurityConfigurerAdapter不僅可以爲用戶提供表單登錄(form login),還可以提供其他支持(如密碼恢復,password recovery)。

1.5.3. 在客戶端中註冊一個重定向URI

OAuth2 Boot不支持將重定向URI配置爲屬性(例如,與client-idclient-secret一起使用)。
要添加重定向URI,您需要使用InMemoryClientDetailsServiceJdbcClientDetailsService指定客戶端。
使用任意一個都意味着你將AuthorizationServerConfigurer替換爲你自己定義的,如下例所示:

@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Bean
    PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    protected void configure(ClientDetailsServiceConfigurer clients) {
        clients
            .inMemory()
                .withClient("first-client")
                .secret(passwordEncoder().encode("noonewilleverguess"))
                .scopes("resource:read")
                .authorizedGrantTypes("authorization_code")
                .redirectUris("http://localhost:8081/oauth/login/client-app");
    }
}

1.5.4. 測試授權流程

OAuth的流程不好測試,因爲那需要不止一個服務端來測試完整的流程(這是來自官方的吐槽)。然而測試的第一步卻很簡單:

  1. 訪問 http://localhost:8080/oauth/authorize?grant_type=authorization_code&response_type=code&client_id=first-client&state=1234
  2. 如果用戶尚未登錄,會重定向到登錄頁面,如 http://localhost:8080/login
  3. 一旦用戶登錄成功,應用會生成code並重定向到註冊的重定向URI(在這個例子中是 http://localhost:8081/oauth/login/client-app)
    這樣,任何使用 Autnorization Code 授權模式的資源服務端就可以在指向授權服務端實例的情況下繼續授權流程。

1.6. 如何創建Password授權模式

使用默認配置時,Password授權模式從技術上講是可以運行的,但其同 Autnorization Code授權模式所面臨的問題一樣,缺少用戶。
也就是說,由於默認配置創建了一個具有用戶名和隨機密碼的user,你可以嘗試檢查日誌,找到password後執行一下操作:
curl first-client:noonewilleverguess@localhost:8080/oauth/token -dgrant_type=password -dscope=any -dusername=user -dpassword=the-password-from-the-logs
當你執行此命令,你會獲得一個token。
大多數情況,你想要設置多個用戶。
正如在[章節1.5,如何創建Authorization Code授權模式](# 1.5. 如何創建Authorization Code授權模式)中所述,在Spring Security中,用戶通常被UserDetailsService所指定,這並沒有什麼區別。例:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        return new InMemoryUserDetailsManager(
            User.withDefaultPasswordEncoder()
                .username("enduser")
                .password("password")
                .roles("USER")
                .build());
    }
}

這就是我們需要所的所有工作。我們不需要重寫AuthorizationServerConfigurer,因爲client ID和secret已經通過環境屬性(environment properties)所指定。
此時,下面這個指令應該就有用了。
curl first-client:noonewilleverguess@localhost:8080/oauth/token -dgrant_type=password -dscope=any -dusername=enduser -dpassword=password

1.7. 如何以及何時向授權服務端提供AuthenticationMamager

這是一個很常見的問題,因爲在AuthorizationServerEndpointsConfigurer並非很直觀的指定AuthenticationManager實例。簡單來說,答案是:僅在資源服務端[使用 Password 授權模式](# 1.6. 如何創建Password授權模式)時。
記住以下基本知識會幫你判斷:

  • AuthenticationManager對用戶來說是抽象的,他通常需要指定如UserDetailsService這樣的類型才能完成其功能。
  • 終端用戶通過WebSecurityConfigurerAdapter來指定。
  • OAuth2 Boot 默認自動獲取任何已經定義的AuthenticationManager.

然而,不是所有流程都需要AuthenticationManager,因爲不是所有流程都涉及到終端用戶。比如,客戶憑證流程只需要一個基於客戶端權限的token(而不是需要終端用戶)。此時 Refresh Token 流程需要一個僅基於 Refresh Token 的權限的Token。
當然,也不是所有流程都需要明確要求 OAuth 2.0 API 本身具有AuthenticationManager,舉個例子, Authorization Code 授權模式和 Implicit 授權模式在用戶登錄(應用本身的流程)時授權用戶,而不是在請求Token(OAuth 2.0 API)時授權用戶。

只有當資源服務端的流程返回一個基於code的終端用戶憑證,這才以爲這你的授權服務端需要一個AuthenticationManager
下面這個示例展示了資源服務端配置請求Password授權模式:
.authorizedGrantTypes("password", ...)
上述的例子中,你的授權服務端需要一個AuthenticationManager
有幾個辦法可以做到這點(還記得之前說的基本知識嗎):

  • 使用OAuth2 Boot的默認配置(不要手動配置AuthorizationServerConfigurer),並[定義UserDetailsService](# 1.7.1. 定義UserDetailsService)
  • 使用OAuth2 Boot的默認配置,並[定義AuthenticationManager](# 1.7.2. 定義AuthenticationManager)
  • 重寫AuthorizationServerConfigurerAdapter(使OAuth2 Boot’s默認配置失效)並[定義AuthenticationConfiguration](# 1.7.3. 定義AuthenticationConfiguration)
  • 重寫AuthorizationServerConfigurerAdapter並手動配置[手動配置AuthenticationManager](# 1.7.4. 手動配置AuthenticationManager)

1.7.1. 定義UserDetailsService

終端用戶被WebSecurityConfigurerAdapterUserDetailsService指定,所以如果你使用了OAuth2 Boot的默認配置(意思是你沒有實現AuthorizationServerConfigurer接口),你可以僅提供一個UserDetailsService然後結束配置工作。如:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired DataSource dataSource;

    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        return new JdbcUserDetailsManager(this.dataSource);
    }
}

1.7.2. 定義AuthenticationManager

如果你想對AuthenticationManager做一些個性的配置,可以在WebSecurityConfigurerAdapter中實施,並將其聲明爲一個bean。比如:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean(BeansId.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(customAuthenticationProvider());
    }
}

如果你用的是OAuth2 Boot默認配置,那麼這個bean會被自動創建。

1.7.3. 定義AuthenticationConfiguration

AuthenticationConfiguration中你可以做任何關於AuthenticationManager的配置。這意味着,如果你需要配置AuthorizationServerConfigurer(比如需要往其中注入一些自己的bean),你可以自定義配置類並使其繼承AuthorizationServerConfigurerAdapter,然後在其中獲取AuthenticationManager的bean,比如這樣:

@Component
public class CustomAuthorizationServerConfigurer extends
    AuthorizationServerConfigurerAdapter {

    AuthenticationManager authenticationManager;

    public CustomAuthorizationServerConfigurer(AuthenticationConfiguration authenticationConfiguration) {
        this.authenticationManager = authenticationConfiguration.getAuthenticationManager();
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) {
        // .. your client configuration that allows the password grant
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.authenticationManager(authenticationManager);
    }
}
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        return new MyCustomUserDetailsService();
    }
}

1.7.4. 手動配置AuthenticationManager

在最複雜的情況下,AuthenticationManager需要做一些特別配置,同時你有自己的AuthenticationServerConfigurer,那麼你需要同時創建你自己的AuthorizationServerConfigurerAdapterWebSecurityConfigurerAdapter

@Component
public class CustomAuthorizationServerConfigurer extends
    AuthorizationServerConfigurerAdapter {

    AuthenticationManager authenticationManager;

    public CustomAuthorizationServerConfigurer(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) {
        // .. your client configuration that allows the password grant
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.authenticationManager(authenticationManager);
    }
}
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean(BeansId.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(customAuthenticationProvider());
    }
}

1.8. 授權服務端兼容Spring Security 5.1的資源服務端和客戶端嗎?

不。Spring Security 5.1 只支持JWT編碼和JWK簽名授權。並且授權服務端不支持JWK集URI。
但其有一些基礎支持,
爲了將授權服務端和Spring Security 5.1的資源端進行適配,你需要做:

  • 配置授權服務端使用JWK
  • 添加JWK集URI終端

1.8.1. 配置授權服務端使用JWK

爲更改訪問和刷新令牌的格式,你可以變更AccessTokenConverterTokenStore。例:

@EnableAuthorizationServer
@Configuration
public class JwkSetConfiguration extends AuthorizationServerConfigurerAdapter {

	AuthenticationManager authenticationManager;
	KeyPair keyPair;

	public JwkSetConfiguration(AuthenticationConfiguration authenticationConfiguration,
			KeyPair keyPair) throws Exception {

		this.authenticationManager = authenticationConfiguration.getAuthenticationManager();
		this.keyPair = keyPair;
	}

    // ... client configuration, etc.

	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
		// @formatter:off
		endpoints
			.authenticationManager(this.authenticationManager)
			.accessTokenConverter(accessTokenConverter())
			.tokenStore(tokenStore());
		// @formatter:on
	}

	@Bean
	public TokenStore tokenStore() {
		return new JwtTokenStore(accessTokenConverter());
	}

	@Bean
	public JwtAccessTokenConverter accessTokenConverter() {
		JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
		converter.setKeyPair(this.keyPair);
		return converter;
	}
}

1.8.2. 添加JWK集URL終端

Spring Security OAuth不支持JWK,同時@EnableAuthorizationServer註解也不支持在初始化集中添加更多OAUTH 2.0 API。不過,我們可以通過添加少量代碼來實現。
首先,你需要添加另外一個依賴包:com.nimbusds:nimbus-jose-jwt。這個包給你提供了JWK的原生支持
然後,不要使用@EnableAuthorizationServer註解,直接使用兩個@Configuration類即可:

  • AuthorizationServerEndpointsConfiguration:配置OAUTH 2.0 API終端(如token要使用什麼格式)的@Configuration類。
  • AuthorizationServerSecurityConfiguration:配置終端的訪問權限的@Configuration類。這個類你需要像下例中一樣被其他類繼承:
@FrameworkEndpoint
class JwkSetEndpoint {
	KeyPair keyPair;

	public JwkSetEndpoint(KeyPair keyPair) {
		this.keyPair = keyPair;
	}

	@GetMapping("/.well-known/jwks.json")
	@ResponseBody
	public Map<String, Object> getKey(Principal principal) {
		RSAPublicKey publicKey = (RSAPublicKey) this.keyPair.getPublic();
		RSAKey key = new RSAKey.Builder(publicKey).build();
		return new JWKSet(key).toJSONObject();
	}
}
@Configuration
class JwkSetEndpointConfiguration extends AuthorizationServerSecurityConfiguration {
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		super.configure(http);
		http
			.requestMatchers()
				.mvcMatchers("/.well-known/jwks.json")
				.and()
			.authorizeRequests()
				.mvcMatchers("/.well-known/jwks.json").permitAll();
	}
}

最後,如果你需要變更AuthorizationServerEndpointsConfiguration,你可以像下例中使用@Import來代替@EnableAuthorizationServer

@Import(AuthorizationServerEndpointsConfiguration.class)
@Configuration
public class JwkSetConfiguration extends AuthorizationServerConfigurerAdapter {

    // ... the rest of the configuration from the previous section
}

1.8.3. 針對Spring Security 5.1資源服務端進行測試

現在你可以使用POST訪問/oauth/token終端([像之前一樣](# 1.5.4. 測試授權流程))來獲取toke並將其授予Spring Security 5.1資源端

2. 資源服務端

Spring Security OAuth2 Boot 使用兩種 Bearer Token 的token格式:JWT 和Opaque.

2.1. 各種依賴

如果要使用 auto-configuration 功能,你需要spring-security-oauth2,它包含了 OAuth 2.0的基本功能和spring-security-oauth2-autoconfigure。需要注意的是你需要指定spring-security-oauth2-autoconfigure的版本,因爲它不再被 Spring Boot 所管理,並且使用的版本號需要和 Boot 的版本兼容。
如果要支持JWT,則需要spring-security-jwt

2.2. 最小化的OAuth2 Boot 配置

創建最小化的 Spring Boot 資源服務端包含三個基本步驟:

  1. 引入相關依賴。
  2. 引入@EnableResourceServer註解
  3. 指定校驗 bearer token 的策略。

2.2.1. 激活資源端

同其他 Spring Boot 的 @Enable註解一樣,你可以在包含main方法的類中添加@EnableResourceServer 註解。例:

@EnableResourceServer
@SpringBootApplication
public class SimpleAuthorizationServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(SimpleAuthorizationServerApplication, args);
    }
}

添加這個註解同時會添加OAuth2AuthenticationProcessingFilter,不過你仍然需要配置來使得應用知道如何適當處理和授權 token。

2.2.2. 指定Token授權策略

Bearer Token通常有一或兩種形式:JWT-encoded 或 opaque。你需要爲你的資源服務端配置一種策略。

2.2.2.1. JWT

要指定JWT,只需要在授權服務端上指定 jwk 的 key-set-uri:

security:
  oauth2:
    resource:
      jwk:
        key-set-uri: https://idp.example.com/.well-known/jwks.json

你也可以指定一個key來代替 key-set-uri。
需要注意的是,如果使用這種配置,授權服務端需要在啓動資源服務端之前啓動授權服務端。

2.2.2.2. Opaque

要指定Opaque,只需要在授權服務端配置如何解碼 token:

security:
  oauth2:
    resource:
      token-info-uri: https://idp.example.com/oauth2/introspect

(機翻)
此端點可能需要與令牌本身不同的某種授權,例如客戶端身份授權。
It’s likely this endpoint requires some kind of authorization separate from the token itself, for example, client authentication.
(機翻完畢)

That’s it! But, what do you do with it? We cover that next.

2.2.3. 訪問資源

爲確保資源服務端正確處理 token ,你可以添加一個Controller,如:

@RestController
public class SimpleController
	@GetMapping("/whoami")
	public String whoami(@AuthenticationPrincipal(expression="name") String name) {
		return name;
    }
}

然後,從您的授權服務端獲取活動訪問令牌,並將其提供給資源服務端:
curl -H "Authorization: $TOKEN" http://localhost:8080/whoami
你會在 token 中看到user_name的值。
讀到此處,你可能會想要了解更多有關使用 bearer token 進行身份授權的三種替代方法:

  • [章節2.3. 如何通過單一鍵使用JWK](#2.3. 如何通過單一鍵使用JWK)
  • [章節2.4. 如何配置Token信息終端](#2.4. 如何配置Token信息終端)
  • [章節2.5. 如何配置User Info終端](#2.5. 如何配置User Info終端)

2.3. 如何通過單一鍵使用JWK

如果不想使用 JWK Set 終端,你或許會配置自己本地的key來做授權。儘管這種功能比較弱(大概是指安全度不夠高。譯者注),不過你仍然可以按需選擇此方式。
給資源服務端配置適當的對稱密鑰或PKCS#8 PEM編碼的公共密鑰很簡單,如下所示:

security:
  oauth2:
    resource:
      jwt:
        key-value: |
          -----BEGIN PUBLIC KEY-----
          MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC...
          -----END PUBLIC KEY-----

配置中的分隔符號 | 表示屬性值有多行

你也可以改爲提供 key-storekey-store-passwordkey-aliaskey-password 屬性。
或者你亦可以使用key-uri來從授權服務端遠程獲取key,這是一種介於靜態、本地配置和bearer token之間的很好的方式。

2.4. 如何配置Token信息終端

token 信息終端有時也被稱作內省終端(introspection endpoint)。它可能需要某種客戶端授權,即 Basic 或 Bearer。一般來說,Security Context 中的 bearer token 並不夠用,因爲它與用戶綁定。相反,你需要指定代表客戶端的憑據,如下所示:

security:
  oauth2:
    client:
      clientId: client-id
      clientSecret: client-secret
    resource:
      tokenInfoUri: https://idp.example.com/oauth2/check_token

默認採用 Basic 授權方式使用配置的憑據來針對 token信息終端進行身份授權。

2.5. 如何配置User Info終端

資源服務端通常調用用戶信息端點。因爲從根本上來講,資源服務端是是對 request 進行授權請求(authorizing a request),而不是對其進行授權(not authenticating it)。(資源服務端需要對request 向 授權服務端申請授權,而不是在內部直接對其進行授權。譯者注)
如果你像這樣指定 userinfo 端點:

security:
  oauth2:
    resource:
      userInfoUri: https://idp.example.com/oauth2/userinfo

那麼資源服務端會將 bearer token 作爲 request 的一部分發送出去,並使用結果來增強Authentication對象。

2.5.1. 自定義User Info 請求

在內部,資源服務端使用OAuth2RestTemplate調用/ userinfo端點。有時,可能需要爲此調用添加過濾器或執行其他自定義操作。要自定義此bean的創建,可以使用UserInfoRestTemplateCustomizer。例:

@Bean
public UserInfoRestTemplateCustomizer customHeader() {
	return restTemplate ->
			restTemplate.getInterceptors().add(new MyCustomInterceptor());
}

該bean將被傳遞到UserInfoTemplateFactory,後者將添加有助於與/ userinfo端點協調的其他配置。
當然,如果您需要完全控制OAuth2RestTemplate的配置,則可以完全替換UserInfoTemplateFactory

2.6. 自定義授權規則

與 Spring Security 工作方式類似,你可以通過 Spring Security OAuth 內的終端自定義授權規則,比如:

public class HasAuthorityConfig
		extends ResourceServerConfigurerAdapter {

	@Override
	public void configure(HttpSecurity http) throws Exception {
		// @formatter:off
		http
			.authorizeRequests()
				.antMatchers("/flights/**").hasAuthority("#oauth2.hasScope('message:read')")
				.anyRequest().authenticated();
		// @formatter:on
	}

但是請注意,如果將資源服務端和授權服務端配置在一起,有些端點需要特殊處理。爲了避免在這些端點(例如/token)頂部進行配置,最好將資源服務端端點使用目錄隔離開,如下所示:

public class ResourceServerEndpointConfig
		extends ResourceServerConfigurerAdapter {

	@Override
	public void configure(HttpSecurity http) throws Exception {
		// @formatter:off
		http
			.antMatchers("/resourceA/**", "/resourceB/**")
			.authorizeRequests()
				.antMatchers("/resourceA/**").hasAuthority("#oauth2.hasScope('resourceA:read')")
				.antMatchers("/resourceB/**").hasAuthority("#oauth2.hasScope('resourceB:read')")
				.anyRequest().authenticated();
		// @formatter:on
	}

這樣配置可以使目標指向你的資源端點,而不會作用於授權端點。

2.7. 不常見的功能

2.7.1. 變更Token類型

Google和某些其他第三方身份提供商對標頭中發送給用戶信息端點的令牌類型名稱更加嚴格。 默認值爲Bearer,它適合大多數提供商並符合規範。 但是,如果需要更改它,則可以設置security.oauth2.resource.token-type

2.7.2. 變更Filter順序

OAuth2 資源被 filter chain 以security.oauth2.resource.filter-order指定的順序保護。
默認情況下,AuthorizationServerConfigurerAdapter中的過濾器排在最前面,其次是ResourceServerConfigurerAdapter中的過濾器,然後是WebSecurityConfigurerAdapter中的過濾器。
這意味着應用中所有的端點都需要bearertoken,除非以下至少一個情況:

  1. filter chain 的順序發生改變
  2. ResourceServerConfigurerAdapter 授權請求集縮小範圍。

首先,可以通過將WebSecurityConfigurerAdapter移動到ResourceServerConfigurerAdapter的前面來更改過濾器鏈的順序,如下所示:

@Order(2)
@EnableWebSecurity
public WebSecurityConfig extends WebSecurityConfigurerAdapter {
	// ...
}

資源服務端的@Order值默認爲3,所以示例中的@Order值爲2能夠確保其優先級更高。

儘管這種辦法可行,但有一點奇怪的是,我們可能需要面臨一個問題:
ResourceServerConfigurerAdapter會處理不應被其處理的請求。
另一個問題:
WebSecurityConfigurerAdapter正處理不應被其處理的請求。
更可靠的解決方案是向ResourceServerConfigurerAdapter指示應通過 bearer token 授權保護哪些端點。
比如,以下將資源服務端配置爲保護以/ rest開頭的Web應用程序端點:

@EnableResourceServer
public ResourceServerConfig extends ResourceServerConfigurerAdapter {
	@Override
    protected void configure(HttpSecurity http) {
        http
            .requestMatchers()
                .antMatchers("/rest/**")
            .authorizeRequests()
                .anyRequest().authenticated();
    }
}

2.7.3. 允許 /error 終端

如果資源服務端同時被配置成客戶端,資源服務端在身份授權過程中可能依賴於 request-scoped OAuth2ClientContext bean。在有些錯誤情景中,資源服務端將轉發到 ERROR servlet 處理器。
request-scoped 的bean默認情況下在 ERROR 處理器中不可用。因此,你可能會看到OAuth2ClientContext的反饋信息。
最簡單的方法可能是允許/error端點,以使Resource Server不會嘗試對其進行身份授權:

public class PermitErrorConfig extends ResourceServerConfigurerAdapter {
    @Override
	public void configure(HttpSecurity http) throws Exception {
		// @formatter:off
		http
			.authorizeRequests()
				.antMatchers("/error").permitAll()
				.anyRequest().authenticated();
		// @formatter:on
	}
}

其他解決方案是配置 Spring,即在錯誤處理器中註冊RequestContextFilter或註冊RequestContextListener bean。

3. 客戶端

要將你的 web 應用作爲 OAuth2 的客戶端,你可以添加@EnableOAuth2Client註解,Spring Boot 就會創建OAuth2ClientContextOAuth2ProtectedResourceDetails這兩個對於創建OAuth2RestOperations來說是必需的東西。Spring Boot不會自動創建這個bean,但可以通過下例的方法很容易手動創建:

@Bean
public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext oauth2ClientContext,
        OAuth2ProtectedResourceDetails details) {
    return new OAuth2RestTemplate(details, oauth2ClientContext);
}

您可能想要添加一個注入並查看您的配置,因爲您的應用程序中可能定義了多個RestTemplate。

此配置使用security.oauth2.client.*作爲依據(與授權服務端中可能使用的配置相同)。此外,它還需要知道授權服務端中的授權和 tokenURI,如以下示例所示:
application.yml

security:
  oauth2:
    client:
      clientId: bd1c0a783ccdd1c9b9e4
      clientSecret: 1a9030fbca47a5b2c28e92f19050bb77824b5ad1
      accessTokenUri: https://github.com/login/oauth/access_token
      userAuthorizationUri: https://github.com/login/oauth/authorize
      clientAuthenticationScheme: form

當你嘗試使用OAuth2RestTemplate時,應用程序會使用此配置重定向至 Github 尋求授權。如果你已經在 Github 登錄,你甚至不會感覺到那已經被授權。僅當您的應用程序在端口8080上運行時,這些特定的憑據纔有效(您可以在Github或其他提供程序中註冊自己的客戶端應用程序,以提高靈活性)。
要限制客戶端獲取訪問令牌時要求的範圍,可以設置security.oauth2.client.scope(逗號分隔或YAML中的數組)。 默認情況下,作用域爲空,由授權服務端決定默認值是什麼(通常取決於它所擁有的客戶端註冊中的設置)。

還有一個security.oauth2.client.client-authentication-scheme的設置,默認爲在header中(但是如果您的OAuth2提供者,如 Github,不喜歡標頭授權,則可能需要將其設置爲form)。 實際上,security.oauth2.client.*屬性綁定到AuthorizationCodeResourceDetails的實例,因此可以指定其所有屬性。

在非Web應用程序中,您仍然可以創建OAuth2RestOperations,並且仍將其連接到security.oauth2.client.*配置中。 在這種情況下,您要使用的是“客戶端憑據令牌授權(client credentials token grant)”(並且不需要使用@ EnableOAuth2Client@ EnableOAuth2Sso)。 爲防止基礎組件生效,請從您的配置中刪除security.oauth2.client.client-id(或使其爲空字符串)。

4. 單點登錄

你可以使用 OAuth2 Client 從保護容器(provider)中獲取 user details(如果這個功能可用),然後將其轉化成 Spring Security 的Authenticationtoken。資源服務端([之前描述的](# 2. 資源服務端))可以通過設置user-info-uri來支持此功能。這是基於 OAuth2 的單點登錄協議的基礎,Spring Boot 通過使用@EnableOAuth2Sso註解使其更容易實現。上一節中展示的Github客戶端可以通過使用Github /user/端點,添加該註釋並聲明在何處查找端點(除了已經列出的security.oauth2.client.*配置),可以保護其所有資源並進行身份授權。
示例 4.1. application.yml

security:
  oauth2:
# ...
  resource:
    userInfoUri: https://api.github.com/user
    preferTokenInfo: false

由於所有路徑都默認被保護,在未被授權情況下,你無法展示“home”頁(通過訪問/login路徑或security.oauth2.sso.login-path指定的路徑)。
要自定義訪問規則或保護路徑(如添加“home”頁面),你可以可以在WebSecurityConfigurerAdapter中添加@EnableOAuth2Sso註解。此註解可以使需要被保護的資源通過/login得到保護。下面的例子僅允許未經授權的用戶對主頁進行訪問,其他部分使用默認配置:

@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .mvcMatchers("/").permitAll()
                .anyRequest().authenticated();
    }
}

請注意,所有終端都默認被保護,包括任何錯誤處理器(如/error)。也就是說,如果在使用單點登錄過程中出現任何問題需要重定向到/error頁面,這會在授權服務端和資源端之間引發無限循環調用(類似無法跳出的遞歸調用。譯者注)。
首先,仔細思考不需要被保護的端點。你可能會發現這只是另一個問題的起因。不過你可以像下例一樣將/error端點設置爲允許訪問,

@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/error").permitAll()
                .anyRequest().authenticated();
    }
}

附錄A. 常見的應用屬性

你可以在application.properties文件或application.yml文件中定義各種屬性,你也可以使用命令行來開啓或關閉他們。本節提供了常見的Spring Boot屬性列表以及對使用它們的基礎類的引用。

屬性可以來自 classpath 上的其他 jar 文件,因此您不應將其視爲詳盡的列表。 定義自己的屬性也是完全合法的。

此樣本文件僅供參考。 不要將整個內容複製並粘貼到您的應用程序中。 而是僅選擇所需的屬性。

# SECURITY OAUTH2 CLIENT (OAuth2ClientProperties)
security.oauth2.client.client-id= # OAuth2 client id.
security.oauth2.client.client-secret= # OAuth2 client secret. 默認生成隨機Secret

# SECURITY OAUTH2 RESOURCES (ResourceServerProperties)
security.oauth2.resource.id= # resource的唯一標識符
security.oauth2.resource.jwt.key-uri= # JWT token 的URI. 如果值不可用且密鑰是公共的,則可以設置。
security.oauth2.resource.jwt.key-value= # JWT令牌的授權密鑰。 可以是對稱密鑰,也可以是PEM編碼的RSA公鑰。
security.oauth2.resource.jwk.key-set-uri= # 用於獲取可用於驗證令牌的密鑰集的URI。
security.oauth2.resource.prefer-token-info=true # 使用令牌信息,可以設置爲false以使用用戶信息。
security.oauth2.resource.service-id=resource #
security.oauth2.resource.token-info-uri= # 解碼 token 的 URI。
security.oauth2.resource.token-type= # 使用userInfoUri時發送的token的類型。 
security.oauth2.resource.user-info-uri= # URI of the user endpoint.

# SECURITY OAUTH2 SSO (OAuth2SsoProperties)
security.oauth2.sso.login-path=/login # 登錄頁面的路徑, 即觸發重定向到OAuth2授權服務端的地址
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章