本文介紹了如何使用Spring Security OAuth2構(gòu)建一個(gè)授權(quán)服務(wù)器來(lái)驗(yàn)證用戶身份以提供access_token昭躺,并使用這個(gè)access_token來(lái)從資源服務(wù)器請(qǐng)求數(shù)據(jù)耗美。
1.概述
OAuth2是一種授權(quán)方法魔吐,用于通過(guò)HTTP協(xié)議提供對(duì)受保護(hù)資源的訪問(wèn)祷愉。首先,OAuth2使第三方應(yīng)用程序能夠獲得對(duì)HTTP服務(wù)的有限訪問(wèn)權(quán)限湖雹,然后通過(guò)資源所有者和HTTP服務(wù)之間的批準(zhǔn)交互來(lái)讓第三方應(yīng)用程序代表資源所有者獲取訪問(wèn)權(quán)限咏闪。
1.1 角色
OAuth定義了四個(gè)角色
- 資源所有者 - 應(yīng)用程序的用戶。
- 客戶端 - 需要訪問(wèn)資源服務(wù)器上的用戶數(shù)據(jù)的應(yīng)用程序摔吏。
- 資源服務(wù)器 - 存儲(chǔ)用戶數(shù)據(jù)和http服務(wù)鸽嫂,可以將用戶數(shù)據(jù)返回給經(jīng)過(guò)身份驗(yàn)證的客戶端。
- 授權(quán)服務(wù)器 - 負(fù)責(zé)驗(yàn)證用戶的身份并提供授權(quán)令牌征讲。資源服務(wù)器接受此令牌并驗(yàn)證您的身份据某。
OAuth2的交互過(guò)程:
1.2 訪問(wèn)令牌與刷新令牌
訪問(wèn)令牌代表一個(gè)向客戶授權(quán)的字符串。令牌包含了由資源所有者授予的權(quán)限范圍和訪問(wèn)持續(xù)時(shí)間稳诚,并由資源服務(wù)器和授權(quán)服務(wù)器強(qiáng)制執(zhí)行。
授權(quán)服務(wù)器向客戶端發(fā)出刷新令牌瀑踢,用于在當(dāng)前訪問(wèn)令牌失效或過(guò)期時(shí)獲取新的訪問(wèn)令牌扳还,或獲取具有相同或更窄范圍的其他訪問(wèn)令牌,新的訪問(wèn)令牌可能具有比資源所有者授權(quán)的更短的生命周期和更少的權(quán)限橱夭。根據(jù)授權(quán)服務(wù)器的判斷氨距,發(fā)布刷新令牌是可選的。
訪問(wèn)令牌的責(zé)任是在數(shù)據(jù)到期之前訪問(wèn)它棘劣。
刷新令牌的責(zé)任是在現(xiàn)有訪問(wèn)令牌過(guò)期時(shí)請(qǐng)求新的訪問(wèn)令牌俏让。
2. OAuth2 - 授權(quán)服務(wù)器
要使用spring Security OAuth2模塊創(chuàng)建授權(quán)服務(wù)器,我們需要使用注解@EnableAuthorizationServer并擴(kuò)展AuthorizationServerConfigurerAdapter類。
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Override
public void configure(AuthorizationServerSecurityConfigurer security)
throws Exception {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.inMemory().withClient("clientapp")
.secret(passwordEncoder.encode("654321"))
.authorizedGrantTypes("password", "authorization_code",
"refresh_token")
.authorities("READ_ONLY_CLIENT").scopes("read_user_info")
.resourceIds("oauth2-resource")
.redirectUris("http://localhost:8081/login")
.accessTokenValiditySeconds(5000)
.refreshTokenValiditySeconds(50000);
}
}
Spring Security OAuth2會(huì)公開(kāi)了兩個(gè)端點(diǎn)首昔,用于檢查令牌(/oauth/check_token和/oauth/token_key)寡喝,這些端點(diǎn)默認(rèn)受保護(hù)denyAll()。tokenKeyAccess()和checkTokenAccess()方法會(huì)打開(kāi)這些端點(diǎn)以供使用勒奇。
-
ClientDetailsServiceConfigurer用于定義客戶端詳細(xì)的服務(wù)信息预鬓,它可以在內(nèi)存中或在數(shù)據(jù)庫(kù)中定義。
在本例中我們使用了內(nèi)存實(shí)現(xiàn)赊颠。它具有以下重要屬性:
- clientId - (必需)客戶端ID格二。
- secret - (可信客戶端所需)客戶端密鑰(可選)。
- scope - 客戶受限的范圍竣蹦。如果范圍未定義或?yàn)榭眨J(rèn)值)顶猜,則客戶端不受范圍限制。
- authorizedGrantTypes - 授權(quán)客戶端使用的授權(quán)類型痘括。默認(rèn)值為空长窄。
- authorities - 授予客戶的權(quán)限(常規(guī)Spring Security權(quán)限)。
- redirectUris - 將用戶代理重定向到客戶端的重定向端點(diǎn)远寸。它必須是絕對(duì)URL抄淑。
3. OAuth2 - 資源服務(wù)器
要?jiǎng)?chuàng)建資源服務(wù)器組件,請(qǐng)使用@EnableResourceServer注解并擴(kuò)展ResourceServerConfigurerAdapter類驰后。
@Configuration
@EnableResourceServer
public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/api/**").authenticated();
}
}
以上配置啟用/api下所有端點(diǎn)的保護(hù)肆资,但可以自由訪問(wèn)其他端點(diǎn)。
資源服務(wù)器還提供了一種對(duì)用戶自己進(jìn)行身份驗(yàn)證的機(jī)制灶芝。在大多數(shù)情況下郑原,它通常是基于表單的登錄。
@Configuration
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.requestMatchers()
.antMatchers("/oauth/authorize**", "/login**", "/error**")
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication().withUser("peterwanghao")
.password(passwordEncoder().encode("123456")).roles("USER");
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
4. OAuth2保護(hù)的REST資源
本例中只創(chuàng)建了一個(gè)RESTful API夜涕,它返回登錄用戶的姓名和電子郵件犯犁。
@Controller
public class RestResource {
@RequestMapping("/api/users/me")
public ResponseEntity<UserInfo> profile() {
User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String email = user.getUsername() + "@126.com";
UserInfo profile = new UserInfo();
profile.setName(user.getUsername());
profile.setEmail(email);
return ResponseEntity.ok(profile);
}
}
public class UserInfo {
private String name;
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User [name=" + name + ", email=" + email + "]";
}
}
5.演示
我們有一個(gè)API http://localhost:8080/api/users/me ,我們想通過(guò)第三方應(yīng)用程序來(lái)訪問(wèn)API需要OAuth2令牌女器。
5.1 從用戶獲取授權(quán)許可代碼
如上面的序列圖所示酸役,第一步是從URL獲取資源所有者的授權(quán):
http://localhost:8080/oauth/authorize?client_id=clientapp&response_type=code&scope=read_user_info
通過(guò)瀏覽器訪問(wèn)上面的URL地址,它將展現(xiàn)一個(gè)登錄頁(yè)面驾胆。提供用戶名和密碼涣澡。對(duì)于此示例,請(qǐng)使用“peterwanghao”和“123456”丧诺。
登錄后入桂,您將被重定向到授予訪問(wèn)頁(yè)面,您可以在其中選擇授予對(duì)第三方應(yīng)用程序的訪問(wèn)權(quán)限驳阎。
它會(huì)重定向到URL抗愁,如:http://localhost:8081/login?code=TUXuk9 馁蒂。這里'TUXuk9'是第三方應(yīng)用程序的授權(quán)代碼。
5.2 從授權(quán)服務(wù)器獲取訪問(wèn)令牌
現(xiàn)在蜘腌,應(yīng)用程序?qū)⑹褂檬跈?quán)碼來(lái)獲取訪問(wèn)令牌沫屡。在這里,我們需要提出以下請(qǐng)求逢捺。使用此處第一步中獲得的代碼谁鳍。
curl -X POST --user clientapp:654321 http://localhost:8080/oauth/token -H "content-type: application/x-www-form-urlencoded" -d "code=TUXuk9&grant_type=authorization_code&redirect_uri=http://localhost:8081/login&scope=read_user_info"
訪問(wèn)令牌響應(yīng)
{
"access_token": "168aad01-05dc-4446-9fba-fd7dbe8adb9e",
"token_type": "bearer",
"refresh_token": "34065175-1e92-4bb0-918c-a5a6ece1dc5f",
"expires_in": 4999,
"scope": "read_user_info"
}
5.3 從資源服務(wù)器訪問(wèn)用戶數(shù)據(jù)
一旦我們有了訪問(wèn)令牌,我們就可以轉(zhuǎn)到資源服務(wù)器來(lái)獲取受保護(hù)的用戶數(shù)據(jù)劫瞳。
curl -X GET http://localhost:8080/api/users/me -H "authorization: Bearer 168aad01-05dc-4446-9fba-fd7dbe8adb9e"
獲得資源響應(yīng)
{
"name":"peterwanghao",
"email":"peterwanghao@126.com"
}
總結(jié)
本文講解了OAuth2授權(quán)框架的實(shí)現(xiàn)機(jī)制倘潜,通過(guò)一個(gè)例子說(shuō)明了第三方應(yīng)用程序如何通過(guò)授權(quán)服務(wù)器的授權(quán)去資源服務(wù)器上獲取受保護(hù)的數(shù)據(jù)。本例的完整代碼在GitHub上志于。