SpringBoot淺析安全管理之OAuth2框架
OAuth2簡(jiǎn)介
OAuth 是一個(gè)開(kāi)放標(biāo)準(zhǔn),該標(biāo)準(zhǔn)允許用戶(hù)讓第三方應(yīng)用訪問(wèn)該用戶(hù)在某一網(wǎng)站上存儲(chǔ)的私密資源(如頭像、照片、視頻等),而在這個(gè)過(guò)程中無(wú)須將用戶(hù)名和密碼提供給第三方應(yīng)用。
實(shí)現(xiàn)這一功能是通過(guò)一個(gè)令牌(token),而不是用戶(hù)名和密碼來(lái)訪問(wèn)他們存放在特定服務(wù)提供者的數(shù)據(jù)。每一個(gè)令牌授權(quán)一個(gè)特定的網(wǎng)站在特定的時(shí)間段內(nèi)訪問(wèn)特定的資源。
這樣 OAuth 讓用戶(hù)可以授權(quán)第三方網(wǎng)站靈活的訪問(wèn)存儲(chǔ)在另一些資源服務(wù)器的特定信息,而非所有內(nèi)容。例如,用戶(hù)想通過(guò) QQ 登錄知乎,這時(shí)知乎就是一個(gè)第三方應(yīng)用,知乎要訪問(wèn)用戶(hù)的一些基本信息就需要得到用戶(hù)的授權(quán),如果用戶(hù)把自己的 QQ 用戶(hù)名和密碼告訴知乎,那么知乎就能訪問(wèn)用戶(hù)的所有數(shù)據(jù),并且只有用戶(hù)修改密碼才能收回權(quán)限,這種授權(quán)方式安全隱患很大,如果使用 OAuth ,就能很好的解決這一問(wèn)題。
采用令牌的方式可以讓用戶(hù)靈活的對(duì)第三方應(yīng)用授權(quán)或者收回權(quán)限。OAuth 2 是 OAuth 協(xié)議的下一版本,但不向下兼容 OAuth 1.0 。
OAuth 2 關(guān)注客戶(hù)端開(kāi)發(fā)者的簡(jiǎn)易型,同時(shí)為Web應(yīng)用、桌面應(yīng)用,移動(dòng)設(shè)備、起居室設(shè)備提供專(zhuān)門(mén)的認(rèn)證流程。傳統(tǒng)的 Web 開(kāi)發(fā)登錄認(rèn)證一般都是基于 Session 的,但是前后端分離的架構(gòu)中繼續(xù)使用 Session 會(huì)有許多不便,因?yàn)橐苿?dòng)端(Android、IOS、微信小程序等)要么不支持 Cookie(微信小程序),要么使用非常不便,對(duì)于這些問(wèn)題,使用 OAuth 2 認(rèn)證都能解決。
OAuth2角色
先了解 OAuth 2 中幾個(gè)基本的角色
- 資源所有者:即用戶(hù),具有頭像、照片、視頻等資源
- 客戶(hù)端:即第三方應(yīng)用
- 授權(quán)服務(wù)器:用來(lái)驗(yàn)證用戶(hù)提供的信息是否正確,并返回一個(gè)令牌給第三方應(yīng)用
- 資源服務(wù)器:提供給用戶(hù)資源的服務(wù)器,例如頭像、照片、視頻等資源
一般來(lái)說(shuō),授權(quán)服務(wù)器和資源服務(wù)器可以是同一臺(tái)服務(wù)器。
OAuth2授權(quán)流程
步驟01:客戶(hù)端(第三方應(yīng)用)向用戶(hù)請(qǐng)求授權(quán)。
步驟02:用戶(hù)單擊客戶(hù)端所呈現(xiàn)的服務(wù)授權(quán)頁(yè)面上的同意授權(quán)按鈕后,服務(wù)端返回一個(gè)授權(quán)許可憑證給客戶(hù)端。
步驟03:客戶(hù)端拿著授權(quán)許可證去授權(quán)服務(wù)器申請(qǐng)令牌。
步驟04:授權(quán)服務(wù)器驗(yàn)證信息無(wú)誤后,發(fā)放令牌給客戶(hù)端。
步驟05:客戶(hù)端拿著令牌去資源服務(wù)器訪問(wèn)資源。
步驟06:資源服務(wù)器驗(yàn)證令牌無(wú)誤后開(kāi)放資源。
授權(quán)模式
OAuth 協(xié)議的授權(quán)模式共分為 4 種,如下
- 授權(quán)碼模式:授權(quán)碼(authorization code)是功能最完整、流程最嚴(yán)謹(jǐn)?shù)氖跈?quán)模式。它的特點(diǎn)就是通過(guò)客戶(hù)端的服務(wù)器與授權(quán)服務(wù)器進(jìn)行交互,國(guó)內(nèi)常見(jiàn)的第三方平臺(tái)登錄功能基本都是使用這種模式
- 簡(jiǎn)化模式:簡(jiǎn)化模式不需要客戶(hù)端服務(wù)器參與,直接在瀏覽器中向授權(quán)服務(wù)器申請(qǐng)令牌,一般若是純靜態(tài)頁(yè)面,則可以采用這種方式
- 密碼模式:用戶(hù)把用戶(hù)名密碼直接告訴客戶(hù)端,客戶(hù)端使用這些信息向授權(quán)服務(wù)器申請(qǐng)令牌。這需要用戶(hù)對(duì)客戶(hù)端高度信息,例如客戶(hù)端應(yīng)用和服務(wù)提供商是同一家公司
- 客戶(hù)端模式:客戶(hù)端使用自己的名義而不是用戶(hù)的名義想服務(wù)提供者申請(qǐng)授權(quán)。嚴(yán)格來(lái)說(shuō),客戶(hù)端模式并不能算作 OAuth 協(xié)議要解決的問(wèn)題的一種解決方案,但是,對(duì)于開(kāi)發(fā)者而言,在一些前后端分離應(yīng)用或者為移動(dòng)端提供的認(rèn)證授權(quán)服務(wù)器上使用這種模式還是非常方便的
4 種模式各有千秋,分別適用于不同的開(kāi)發(fā)場(chǎng)景,開(kāi)發(fā)者根據(jù)實(shí)際情況進(jìn)行選擇
實(shí)踐
此處介紹的是在前后端分離應(yīng)用(或?yàn)橐苿?dòng)端、微信小程序等)提供的認(rèn)證服務(wù)器中如何搭建 OAuth 服務(wù),因此主要介紹密碼模式。
1. 創(chuàng)建項(xiàng)目添加依賴(lài)
創(chuàng)建 Spring Boot Web 項(xiàng)目,添加如下依賴(lài)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.3.RELEASE</version>
</dependency>由于 Spring Boot 中的 OAuth 協(xié)議是在 Spring Security 的基礎(chǔ)上完成的,因此首先要添加 Spring Security 依賴(lài),要用到 OAuth 2,因此添加 OAuth 2 相關(guān)依賴(lài),令牌可以存儲(chǔ)在 Redis 緩存服務(wù)器上,同時(shí) Redis 具有過(guò)期等功能,很適合令牌的存儲(chǔ),因此也加入 Redis 依賴(lài)。
配置 application.properties
spring.redis.database=0
spring.redis.host=ip地址
spring.redis.port=6379
spring.redis.password=root
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.max-wait=-1ms
spring.redis.jedis.pool.min-idle=0
2. 配置授權(quán)服務(wù)器
授權(quán)服務(wù)器和資源服務(wù)器可以是同一臺(tái)服務(wù)器,也可以是不同服務(wù)器,此處假設(shè)是同一臺(tái)服務(wù)器,通過(guò)不同的配置分別開(kāi)啟授權(quán)服務(wù)器和資源服務(wù)器,首先是授權(quán)服務(wù)器:
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
AuthenticationManager authenticationManager;
@Autowired
RedisConnectionFactory redisConnectionFactory;
@Autowired
UserDetailsService userDetailsService;
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("password")
.authorizedGrantTypes("password", "refresh_token")
.accessTokenValiditySeconds(1800)
.resourceIds("rid")
.scopes("all")
.secret("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory))
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients();
}
}代碼解釋?zhuān)?/p>
- 自定義類(lèi)繼承自 AuthorizationServerConfigurerAdapter ,完成對(duì)授權(quán)服務(wù)器的配置,然后通過(guò) @EnableAuthorizationServer 注解開(kāi)啟授權(quán)服務(wù)器
- 注入 AuthenticationManager 用來(lái)支持 password 模式
- 注入 RedisConnectionFactory 用來(lái)完成 Redis 緩存,將令牌信息儲(chǔ)存到 Redis 緩存中
- 注入 UserDetailsService 該對(duì)象為刷新 token 提供支持
- 在 configure(ClientDetailsServiceConfigurer clients) 方法中配置 password 授權(quán)模式,authorizedGrantTypes 表示 OAuth 2 中的授權(quán)模式為 password 和 refresh_token 兩種,在標(biāo)準(zhǔn)的 OAuth 2 協(xié)議中,授權(quán)模式并不包括 refresh_token ,但是在 Spring Security 的實(shí)現(xiàn)中將其歸為一種,因此如果要實(shí)現(xiàn) access_token 的刷新,就需要添加這樣一種授權(quán)模式;accessTokenValiditySeconds 方法配置了 access_token 的過(guò)期時(shí)間;resourceIds 配置了資源 id;secret 方法配置了加密后的密碼,明文是 123
- configure(AuthorizationServerEndpointsConfigurer endpoints) 方法配置了令牌的存儲(chǔ),AuthenticationManager 和 UserDetailsService 主要用于支持 password 模式以及令牌的刷新
- configure(AuthorizationServerSecurityConfigurer security) 方法配置表示支持 client_id 和 client_secret 做登錄認(rèn)證
3. 配置資源服務(wù)器
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("rid").stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasRole("user")
.anyRequest().authenticated();
}
}代碼解釋?zhuān)?/p>
- 自定義類(lèi)繼承自 ResourceServerConfigurerAdapter ,并添加 @EnableResourceServer 注解開(kāi)啟資源服務(wù)器配置
- resources.resourceId(“rid”).stateless(true); 配置資源 id,這里的資源 id 和授權(quán)服務(wù)器中的資源 id 一直,然后設(shè)置這些資源僅基于令牌認(rèn)證
- configure(HttpSecurity http) 方法配置 HttpSecurity
4. 配置 Security
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
@Override
protected UserDetailsService userDetailsService() {
return super.userDetailsService();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin")
.password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq")
.roles("admin")
.and()
.withUser("sang")
.password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq")
.roles("user");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/oauth/**").authorizeRequests()
.antMatchers("/oauth/**").permitAll()
.and().csrf().disable();
}
}這里兩個(gè) Bean 將注入授權(quán)服務(wù)器配置類(lèi)中使用,另外,這里的 HttpSecurity 配置主要是配置 /oauth/** 模式的 URL ,這一類(lèi)的請(qǐng)求直接放行。在 Spring Security 配置和資源服務(wù)器配置中,一共涉及兩個(gè) HttpSecurity ,其中 Spring Security 中的配置優(yōu)先級(jí)高于資源服務(wù)器中的配置,即請(qǐng)求地址先經(jīng)過(guò) Spring Security 的 HttpSecurity ,再經(jīng)過(guò)資源服務(wù)器的 HttpSecurity。
5. 驗(yàn)證測(cè)試
首先創(chuàng)建三個(gè)簡(jiǎn)單的請(qǐng)求地址
@RestController
public class HelloController {
@GetMapping("/admin/hello")
public String admin() {
return "Hello admin!";
}
@GetMapping("/user/hello")
public String user() {
return "Hello user!";
}
@GetMapping("/hello")
public String hello() {
return "hello";
}
}根據(jù)前文的配置,要請(qǐng)求這三個(gè)地址,分別需要 admin 角色、user 角色以及登錄后訪問(wèn)。
所有都配置完成后,啟動(dòng) Redis 服務(wù)器,再啟動(dòng) Spring Boot 項(xiàng)目,首先發(fā)送一個(gè) POST 請(qǐng)求獲取 token,請(qǐng)求地址如下(注意這里是一個(gè) POST 請(qǐng)求,為了顯示方便,將參數(shù)寫(xiě)在地址欄中):http://localhost:8080/oauth/token?username=sang&password=123&grant_type=password&client_id=password&scope=all&client_secret=123
請(qǐng)求地址中包含的參數(shù)有用戶(hù)名、密碼、授權(quán)模式、客戶(hù)端 id 、scope 以及客戶(hù)端密碼,基本就是授權(quán)服務(wù)器中所配置的數(shù)據(jù),請(qǐng)求結(jié)果如圖

其中 access_token 是獲取其它資源時(shí)要用的令牌,refresh_token 用來(lái)刷新令牌,expires_in 表示 access_token 過(guò)期時(shí)間,當(dāng) access_token 過(guò)期后,使用 refresh_token 重新獲取新的 access_token (前提是 refresh_token 未過(guò)期),請(qǐng)求地址(注意也是POST請(qǐng)求):http://localhost:8080/oauth/token?grant_type=refresh_token&refresh_token=693b0e36-4515-442a-8c5d-90bade3c74d2&client_id=password&client_secret=123
獲取新的 access_token 時(shí)需要攜帶上 refresh_token ,同事授權(quán)模式設(shè)置為 refresh_token ,在獲取的結(jié)果中 access_token 會(huì)變化,同時(shí) access_token 有效期也會(huì)變化,如圖

接下來(lái)訪問(wèn)所有資源,攜帶上 access_token 參數(shù)即可,例如 /user/hello 接口:http://localhost:8080/user/hello?access_token=0497e4bc-df37-460e-8755-b813b9dbf36a,訪問(wèn)結(jié)果如圖

如果非法訪問(wèn)一個(gè)資源,例如 sang 用戶(hù)訪問(wèn) /admin/hello 接口,結(jié)果如圖

到此,一個(gè) password 模式的 OAuth 認(rèn)證體系就搭建成功了。
OAuth 中的認(rèn)證模式有 4 中,開(kāi)發(fā)者需要結(jié)合自己開(kāi)發(fā)的實(shí)際情況選擇其中一種,此處介紹的是在前后端分離應(yīng)用中常用的 password 模式,其它的授權(quán)模式也都有自己的使用場(chǎng)景。
整體來(lái)講,Spring Security OAuth 2 的使用還是較復(fù)雜的,配置也比較繁瑣,如果開(kāi)發(fā)者的應(yīng)用場(chǎng)景比較簡(jiǎn)單,完全可以按照此處介紹的授權(quán)流程自己搭建 OAuth 2 認(rèn)證體系。
到此這篇關(guān)于SpringBoot淺析安全管理之OAuth2框架的文章就介紹到這了,更多相關(guān)SpringBoot OAuth2框架內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot3.X配置OAuth的代碼實(shí)踐
- SpringBoot的Security和OAuth2的使用示例小結(jié)
- 使用Springboot實(shí)現(xiàn)OAuth服務(wù)的示例詳解
- springboot oauth2實(shí)現(xiàn)單點(diǎn)登錄實(shí)例
- springboot集成springsecurity 使用OAUTH2做權(quán)限管理的教程
- 基于SpringBoot整合oauth2實(shí)現(xiàn)token認(rèn)證
- springboot2.x實(shí)現(xiàn)oauth2授權(quán)碼登陸的方法
- 詳解Springboot Oauth2 Server搭建Oauth2認(rèn)證服務(wù)
- 使用Springboot搭建OAuth2.0 Server的方法示例
- SpringBoot集成OAuth2.0的實(shí)現(xiàn)示例
相關(guān)文章
spring整合JMS實(shí)現(xiàn)同步收發(fā)消息(基于ActiveMQ的實(shí)現(xiàn))
本篇文章主要介紹了spring整合JMS實(shí)現(xiàn)同步收發(fā)消息(基于ActiveMQ的實(shí)現(xiàn)),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10
JAVA中重寫(xiě)(Override)與重載(Overload)的相關(guān)示例
這篇文章主要給大家介紹了關(guān)于JAVA中重寫(xiě)(Override)與重載(Overload)的相關(guān)示例,重寫(xiě)(override)和重載(overload)是兩種不同的方法重用技術(shù),文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-10-10
springboot配置數(shù)據(jù)庫(kù)密碼特殊字符報(bào)錯(cuò)的解決
這篇文章主要介紹了springboot配置數(shù)據(jù)庫(kù)密碼特殊字符報(bào)錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(48)
下面小編就為大家?guī)?lái)一篇Java基礎(chǔ)的幾道練習(xí)題(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,希望可以幫到你2021-08-08
Spring Boot結(jié)成MyBatis-Plus最全配置指南
本文主要介紹了Spring Boot結(jié)成MyBatis-Plus最全配置指南,包括依賴(lài)引入、配置數(shù)據(jù)源、Mapper 掃描、基本CRUD操作等,具有一定的參考價(jià)值,感興趣的可以了解一下2025-03-03

