1. 概述
本教程將演示如何創建一個將用户身份驗證委託給第三方以及自定義授權服務器的應用程序,使用 Spring Boot 和 Spring Security OAuth。
此外,我們還將演示如何使用 Spring 的 `PrincipalExtractor 和 AuthoritiesExtractor 接口,提取 Principal 和 Authorities。
有關 Spring Security OAuth2 的介紹,請參閲這些文章。
2. Maven 依賴
為了開始,我們需要將 <a href="https://mvnrepository.com/artifact/org.springframework.security.oauth.boot/spring-security-oauth2-autoconfigure">spring-security-oauth2-autoconfigure</a> 依賴添加到我們的 <em>pom.xml</em> 中:
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.6.8</version>
</dependency>3. 使用 GitHub 進行 OAuth 認證
接下來,讓我們為我們的應用程序創建安全配置:
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/login**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.disable()
.oauth2Login();
return http.build();
}
}簡而言之,我們説的是,任何人都可以訪問 /login 端點,而所有其他端點都需要用户身份驗證。
添加以下屬性至少一個,將啓用 Oauth2ClientAutoConfiguration 類,該類設置了所有必要的 Bean。
spring.security.oauth2.client.registration.github.client-id=89a7c4facbb3434d599d
spring.security.oauth2.client.registration.github.client-secret=
9b3b08e4a340bd20e866787e4645b54f73d74b6a
spring.security.oauth2.client.registration.github.scope=read:user,user:email
spring.security.oauth2.client.provider.github.token-uri=
https://github.com/login/oauth/access_token
spring.security.oauth2.client.provider.github.authorization-uri=
https://github.com/login/oauth/authorize
spring.security.oauth2.client.provider.github.user-info-uri=https://api.github.com/user我們不再處理用户賬户管理,而是將其委託給第三方——具體來説是Github,從而使我們能夠專注於應用程序的邏輯。
4. 提取主體和權限
當作為 OAuth 客户端並通過第三方認證用户時,我們需要考慮以下三個步驟:
- 用户認證 – 用户與第三方進行認證
- 用户授權 – 緊隨認證之後,當用户允許我們的應用程序代表其執行某些操作時;這時 權限範圍 就發揮作用
- 獲取用户數據 – 使用我們獲得的 OAuth 令牌來檢索用户的個人資料數據
一旦我們檢索到了用户的個人資料數據,Spring 能夠自動創建用户的 主體 和 權限。
雖然這種情況可能可以接受,但我們通常會遇到想要完全控制它們的情況。
要做到這一點,Spring 提供了兩個接口,我們可以使用它們來覆蓋默認行為:
- PrincipalExtractor主體">
- AuthoritiesExtractorPrincipalExtractor,但用於自定義 權限 提取">
默認情況下,Spring 提供了兩個組件 – FixedPrincipalExtractor 和 FixedAuthoritiesExtractor – 這兩個組件實現了這些接口,並且具有預定義的策略來為我們創建它們。
4.1. 自定義 Github 身份驗證
在我們的案例中,我們瞭解 Github 用户數據的結構 情況,以及如何根據我們的需求進行定製。
因此,為了覆蓋 Spring 的默認組件,我們只需要創建兩個 Bean,並且這些 Bean 也需要實現這些接口。
對於我們應用程序的 Principal,我們將簡單地使用用户的 Github 用户名:
public class GithubPrincipalExtractor
implements PrincipalExtractor {
@Override
public Object extractPrincipal(Map<String, Object> map) {
return map.get("login");
}
}根據我們的用户 GitHub 訂閲情況——免費或其它——我們將授予他們 GITHUB_USER_SUBSCRIBED 或 GITHUB_USER_FREE 權限:
public class GithubAuthoritiesExtractor
implements AuthoritiesExtractor {
List<GrantedAuthority> GITHUB_FREE_AUTHORITIES
= AuthorityUtils.commaSeparatedStringToAuthorityList(
"GITHUB_USER,GITHUB_USER_FREE");
List<GrantedAuthority> GITHUB_SUBSCRIBED_AUTHORITIES
= AuthorityUtils.commaSeparatedStringToAuthorityList(
"GITHUB_USER,GITHUB_USER_SUBSCRIBED");
@Override
public List<GrantedAuthority> extractAuthorities
(Map<String, Object> map) {
if (Objects.nonNull(map.get("plan"))) {
if (!((LinkedHashMap) map.get("plan"))
.get("name")
.equals("free")) {
return GITHUB_SUBSCRIBED_AUTHORITIES;
}
}
return GITHUB_FREE_AUTHORITIES;
}
}
然後,我們還需要使用這些類創建 Bean:
@Configuration
public class SecurityConfig {
// ...
@Bean
public PrincipalExtractor githubPrincipalExtractor() {
return new GithubPrincipalExtractor();
}
@Bean
public AuthoritiesExtractor githubAuthoritiesExtractor() {
return new GithubAuthoritiesExtractor();
}
}4.2. 使用自定義授權服務器
我們可以為我們的用户使用自己的授權服務器,而不是依賴第三方。
儘管我們決定使用哪種授權服務器,我們需要自定義的組件仍然相同:一個 PrincipalExtractor 和一個 AuthoritiesExtractor。
我們只需要意識到 user-info-uri 端點返回的數據,並根據需要使用它。
讓我們修改我們的應用程序,使用本文檔中描述的授權服務器來身份驗證我們的用户:
spring.security.oauth2.client.registration.baeldung.client-id=SampleClientId
spring.security.oauth2.client.registration.baeldung.client-secret=secret
spring.security.oauth2.client.provider.baeldung.token-uri=http://localhost:8081/auth/oauth/token
spring.security.oauth2.client.provider.baeldung.authorization-uri=
http://localhost:8081/auth/oauth/authorize
spring.security.oauth2.client.provider.baeldung.user-info-uri=http://localhost:8081/auth/user/me現在我們已經指向了我們的授權服務器,需要創建兩個提取器;在本例中,我們的PrincipalExtractor 將使用 Map 中的 name 鍵來提取 Principal:
public class BaeldungPrincipalExtractor
implements PrincipalExtractor {
@Override
public Object extractPrincipal(Map<String, Object> map) {
return map.get("name");
}
}至於權限驗證者,我們已經將這些權限驗證者置於其 user-info-uri 的數據中。
因此,我們將提取並豐富它們:
public class BaeldungAuthoritiesExtractor
implements AuthoritiesExtractor {
@Override
public List<GrantedAuthority> extractAuthorities
(Map<String, Object> map) {
return AuthorityUtils
.commaSeparatedStringToAuthorityList(asAuthorities(map));
}
private String asAuthorities(Map<String, Object> map) {
List<String> authorities = new ArrayList<>();
authorities.add("BAELDUNG_USER");
List<LinkedHashMap<String, String>> authz =
(List<LinkedHashMap<String, String>>) map.get("authorities");
for (LinkedHashMap<String, String> entry : authz) {
authorities.add(entry.get("authority"));
}
return String.join(",", authorities);
}
}然後我們將向我們的 SecurityConfig 類添加豆子:
@Configuration
public class SecurityConfig {
// ...
@Bean
public PrincipalExtractor baeldungPrincipalExtractor() {
return new BaeldungPrincipalExtractor();
}
@Bean
public AuthoritiesExtractor baeldungAuthoritiesExtractor() {
return new BaeldungAuthoritiesExtractor();
}
}5. 結論
本文介紹了將用户身份驗證委託給第三方服務以及自定義授權服務器,並演示瞭如何自定義Principal和Authorities的方法。
本地運行時,您可以在localhost:8082上運行和測試該應用程序。