農(nóng)行一面:OAuth2 如何做權(quán)限驗(yàn)證?它有哪些模式?
OAuth2是現(xiàn)代應(yīng)用開發(fā)中幾乎無處不在的認(rèn)證與授權(quán)協(xié)議,什么是OAuth2?它是如何工作的?OAuth2有哪些典型模式?這篇文章,我們來聊一聊。
一、什么是 OAuth2?
簡單來說,OAuth2是一種授權(quán)框架,用于讓應(yīng)用程序在不暴露用戶密碼的情況下,獲得訪問用戶受保護(hù)資源的權(quán)限。它廣泛應(yīng)用于第三方登錄、API 授權(quán)等場景。
二、OAuth2 的核心概念
在深入討論典型模式之前,我們先來簡單了解一下OAuth2的幾個核心角色:
- 資源擁有者(Resource Owner):通常是終端用戶。
- 客戶端(Client):希望訪問資源的應(yīng)用程序(比如你的Java應(yīng)用)。
- 資源服務(wù)器(Resource Server):存儲用戶資源的服務(wù)器。
- 授權(quán)服務(wù)器(Authorization Server):負(fù)責(zé)認(rèn)證和授權(quán)的服務(wù)器。
理解了這些基本概念,咱們就能更好地理解各種OAuth2的授權(quán)模式了。
三、OAuth2 的典型授權(quán)模式
OAuth2 定義了四種主要的授權(quán)模式,每種模式適用于不同的應(yīng)用場景。讓我們逐一來看:
1. 授權(quán)碼模式
授權(quán)碼模式(Authorization Code Grant)適用于服務(wù)器端應(yīng)用,尤其是需要訪問用戶資源的Web應(yīng)用。
流程簡述:
- 用戶訪問客戶端(你的Java應(yīng)用)并請求訪問受保護(hù)資源。
- 客戶端將用戶重定向到授權(quán)服務(wù)器,用戶在授權(quán)服務(wù)器登錄并授權(quán)。
- 授權(quán)服務(wù)器將用戶重定向回客戶端,并附帶一個授權(quán)碼。
- 客戶端使用這個授權(quán)碼向授權(quán)服務(wù)器請求訪問令牌。
- 授權(quán)服務(wù)器返回訪問令牌,客戶端使用該令牌訪問資源服務(wù)器上的資源。
示例演示:
假設(shè)你有一個Java Spring Boot應(yīng)用,需要訪問用戶的GitHub資源。流程如下:
@Configuration
@EnableOAuth2Client
publicclass OAuth2ClientConfig {
@Bean
public OAuth2RestTemplate githubRestTemplate(OAuth2ClientContext oauth2ClientContext) {
returnnew OAuth2RestTemplate(github(), oauth2ClientContext);
}
private OAuth2ProtectedResourceDetails github() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
details.setClientId("your-client-id");
details.setClientSecret("your-client-secret");
details.setAccessTokenUri("https://github.com/login/oauth/access_token");
details.setUserAuthorizationUri("https://github.com/login/oauth/authorize");
details.setScope(Arrays.asList("repo", "read:user"));
return details;
}
}
在這個配置中,我們定義了如何與GitHub的OAuth2服務(wù)交互,獲取訪問令牌并訪問資源。
2. 隱式模式
隱式模式(Implicit Grant)適用于單頁面應(yīng)用(SPA)或移動應(yīng)用,不適合存儲客戶端密鑰。
特點(diǎn):
- 直接在前端獲取訪問令牌,省去了授權(quán)碼的步驟。
- 安全性較低,不推薦用于高安全需求的場景。
示例演示:
前端JavaScript代碼片段:
const clientId = 'client-id';
const redirectUri = 'https://yuanjava.com/callback';
const authUrl = `https://authorization-server.com/auth?response_type=token&client_id=${clientId}&redirect_uri=${redirectUri}&scope=read`;
window.location.href = authUrl;
// 在回調(diào)頁面獲取access_token
const hash = window.location.hash.substring(1);
const params = new URLSearchParams(hash);
const accessToken = params.get('access_token');
這種方式適用于無需在后端存儲敏感信息的應(yīng)用,但要注意訪問令牌可能會暴露在前端。
3. 資源所有者密碼憑證模式
資源所有者密碼憑證模式(Resource Owner Password Credentials Grant)適用于高度信任的應(yīng)用,比如官方的移動應(yīng)用。
特點(diǎn):
- 用戶直接提供用戶名和密碼給客戶端。
- 客戶端使用這些憑據(jù)向授權(quán)服務(wù)器請求訪問令牌。
注意:這種模式下,客戶端需要處理用戶的敏感信息,風(fēng)險較高。
示例演示:
public OAuth2AccessToken getAccessToken(String username, String password) {
ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails();
resourceDetails.setUsername(username);
resourceDetails.setPassword(password);
resourceDetails.setAccessTokenUri("https://authorization-server.com/token");
resourceDetails.setClientId("your-client-id");
resourceDetails.setClientSecret("your-client-secret");
resourceDetails.setScope(Arrays.asList("read", "write"));
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails);
return restTemplate.getAccessToken();
}
在這個例子中,客戶端直接使用用戶的用戶名和密碼獲取訪問令牌。
4. 客戶端憑證模式
客戶端憑證模式(Client Credentials Grant)適用于應(yīng)用間的通信,或后臺服務(wù)。
特點(diǎn):
- 客戶端直接使用自身的憑證(無需用戶參與)獲取訪問令牌。
- 適合訪問屬于客戶端自身的資源。
示例演示:
public OAuth2AccessToken getClientCredentialsToken() {
ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();
resourceDetails.setAccessTokenUri("https://authorization-server.com/token");
resourceDetails.setClientId("your-client-id");
resourceDetails.setClientSecret("your-client-secret");
resourceDetails.setScope(Arrays.asList("read", "write"));
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails);
return restTemplate.getAccessToken();
}
這種模式下,客戶端無需用戶授權(quán),直接獲取訪問令牌進(jìn)行資源訪問。
四、OAuth2 的原理
了解了各種授權(quán)模式后,我們再來看看OAuth2背后的原理,幫助你更好地理解和應(yīng)用它。
- 授權(quán)碼交換: 在授權(quán)碼模式中,授權(quán)碼是一個中間步驟,它增加了安全性。因?yàn)樵L問令牌不直接暴露給用戶瀏覽器,防止惡意攻擊者截獲。
- 范圍(Scope)控制:OAuth2允許客戶端請求特定的權(quán)限范圍(Scope)。比如,你的應(yīng)用只需要讀取用戶的公開信息,就不需要請求寫入權(quán)限。這樣可以減少潛在的風(fēng)險。
- 刷新令牌(Refresh Token): 訪問令牌通常有有效期,當(dāng)過期時,客戶端可以使用刷新令牌獲取新的訪問令牌,而無需用戶重新授權(quán)。這提升了用戶體驗(yàn)和安全性。
五、實(shí)戰(zhàn)演示
為了更好地理解OAuth2,讓我們通過一個實(shí)際的例子,使用 Spring Security來實(shí)現(xiàn)OAuth2的授權(quán)碼模式。
1. 項(xiàng)目配置
首先,添加必要的依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
2. 配置 OAuth2 客戶端
在 application.yml 中配置OAuth2客戶端信息:
spring:
security:
oauth2:
client:
registration:
github:
client-id:client-id
client-secret:client-secret
scope:read:user,repo
redirect-uri:"{baseUrl}/login/oauth2/code/{registrationId}"
authorization-grant-type:authorization_code
client-name:GitHub
provider:
github:
authorization-uri:https://github.com/login/oauth/authorize
token-uri:https://github.com/login/oauth/access_token
user-info-uri:https://api.github.com/user
user-name-attribute:id
3. 創(chuàng)建安全配置
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(a -> a
.antMatchers("/", "/error").permitAll()
.anyRequest().authenticated()
)
.oauth2Login();
}
}
4. 創(chuàng)建控制器
@Controller
publicclass MainController {
@GetMapping("/")
public String home() {
return"home"; // 返回主頁視圖
}
@GetMapping("/user")
public String user(Model model, @AuthenticationPrincipal OAuth2User principal) {
model.addAttribute("user", principal);
return"user"; // 返回用戶信息視圖
}
}
六、總結(jié)
本文,我們深入淺出地介紹了 OAuth2的四種典型授權(quán)模式:授權(quán)碼模式、隱式模式、資源所有者密碼憑證模式以及客戶端憑證模式。OAuth2作為現(xiàn)代應(yīng)用中的核心認(rèn)證與授權(quán)框架,允許應(yīng)用在不暴露用戶密碼的情況下安全地訪問受保護(hù)資源。