@
OAuth2.0系列博客:
- OAuth2.0系列之基本概念和運作流程(一)
- OAuth2.0系列之授權碼模式實踐教程(二)
- OAuth2.0系列之簡化模式實踐教程(三)
- OAuth2.0系列之密碼模式實踐教程(四)
- OAuth2.0系列之客戶端模式實踐教程(五)
- OAuth2.0系列之集成JWT實現單點登錄
1、簡化模式簡介
1.1 前言簡介
在上一篇文章中我們學習了OAuth2的一些基本概念,對OAuth2有了基本的認識,接着學習OAuth2.0授權模式中的簡化模式
ps:OAuth2.0的授權模式可以分為:
- 授權碼模式(authorization code)
- 簡化模式(implicit)
- 密碼模式(resource owner password credentials)
- 客戶端模式(client credentials)
簡化模式(implicit grant type)不通過第三方應用程序的服務器,直接在瀏覽器中向認證服務器申請令牌,跳過了"授權碼"這個步驟,因此稱簡化模式。簡化模式是相對於授權碼模式而言的
1.2 授權流程圖
官網圖片:
http://localhost:8084/api/userinfo?access_token=${accept_token}
- (A):客戶端攜帶client_id、redirect_uri,中間通過代理者訪問授權服務器,如果已經登錄過會直接返回redirect_uri,沒有登錄過就跳轉到登錄頁面
- (B)授權服務器對客戶端進行身份驗證(通過用戶代理,讓用戶輸入用戶名和密碼)
- (C)授權通過,會重定向到redirect_uri並攜帶授權碼token作為uri參數
- (D)客戶端攜帶授權碼訪問資源服務器
- (E)驗證token通過,返回資源
從調接口方面,簡單來說:
- 第一步:訪問授權,要傳client_id:客戶端id,redirect_uri:重定向uri,response_type為token,scope是授權范圍,state是其它自定義參數
- 第二步:授權通過,會重定向到redirect_uri,access_token碼會作為它的參數
- 第三步:拿到acceptToken之后,就可以直接訪問資源
2、例子實踐
2.1 實驗環境准備
- IntelliJ IDEA
- Maven3.+版本
新建SpringBoot Initializer項目,可以命名implicit
主要是想引入:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Cloud Oauth2-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<!-- Spring Cloud Security-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
2.2 OAuth2.0角色
前面的學習,我們知道了OAuth2.0主要包括如下角色,下面通過代碼例子加深對理論的理解
- 資源所有者(Resource Owner)
- 用戶代理(User Agent)
- 客戶端(Client)
- 授權服務器(Authorization Server)
- 資源服務器(Resource Server)
生產環境、資源服務器和授權服務器一般是分開的,不過學習的可以放在一起
定義資源服務器,用注解@EnableResourceServer;
定義授權服務器,用注解@EnableAuthorizationServer;
2.3 OAuth2.0配置類
package com.example.oauth2.implicit.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
/**
* <pre>
* OAuth2.0配置類
* </pre>
*
* <pre>
* @author mazq
* 修改記錄
* 修改后版本: 修改人: 修改日期: 2020/06/11 11:00 修改內容:
* </pre>
*/
@Configuration
//開啟授權服務
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
private static final String CLIENT_ID = "cms";
private static final String SECRET_CHAR_SEQUENCE = "{noop}secret";
private static final String SCOPE_READ = "read";
private static final String SCOPE_WRITE = "write";
private static final String TRUST = "trust";
private static final String USER ="user";
private static final String ALL = "all";
private static final int ACCESS_TOKEN_VALIDITY_SECONDS = 2*60;
private static final int FREFRESH_TOKEN_VALIDITY_SECONDS = 2*60;
// 密碼模式授權模式
private static final String GRANT_TYPE_PASSWORD = "password";
//授權碼模式
private static final String AUTHORIZATION_CODE = "authorization_code";
//refresh token模式
private static final String REFRESH_TOKEN = "refresh_token";
//簡化授權模式
private static final String IMPLICIT = "implicit";
//指定哪些資源是需要授權驗證的
private static final String RESOURCE_ID = "resource_id";
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
// 使用內存存儲
.inMemory()
//標記客戶端id
.withClient(CLIENT_ID)
//客戶端安全碼
.secret(SECRET_CHAR_SEQUENCE)
//為true 直接自動授權成功返回code
.autoApprove(true)
.redirectUris("http://127.0.0.1:8084/cms/login") //重定向uri
//允許授權范圍
.scopes(ALL)
//token 時間秒
.accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS)
//刷新token 時間 秒
.refreshTokenValiditySeconds(FREFRESH_TOKEN_VALIDITY_SECONDS)
//允許授權類型
.authorizedGrantTypes(IMPLICIT );
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// 使用內存保存生成的token
endpoints.authenticationManager(authenticationManager).tokenStore(memoryTokenStore());
}
/**
* 認證服務器的安全配置
*
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
//.realm(RESOURCE_ID)
// 開啟/oauth/token_key驗證端口認證權限訪問
.tokenKeyAccess("isAuthenticated()")
// 開啟/oauth/check_token驗證端口認證權限訪問
.checkTokenAccess("isAuthenticated()")
//允許表單認證
.allowFormAuthenticationForClients();
}
@Bean
public TokenStore memoryTokenStore() {
// 最基本的InMemoryTokenStore生成token
return new InMemoryTokenStore();
}
}
2.4 Security配置類
為了測試,可以進行簡單的SpringSecurity
package com.example.oauth2.implicit.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* <pre>
* SpringSecurity配置類
* </pre>
*
* <pre>
* @author mazq
* 修改記錄
* 修改后版本: 修改人: 修改日期: 2020/06/11 11:23 修改內容:
* </pre>
*/
@Configuration
@EnableWebSecurity
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception { //auth.inMemoryAuthentication()
auth.inMemoryAuthentication()
.withUser("nicky")
.password("{noop}123")
.roles("admin");
}
@Override
public void configure(WebSecurity web) throws Exception {
//解決靜態資源被攔截的問題
web.ignoring().antMatchers("/asserts/**");
web.ignoring().antMatchers("/favicon.ico");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http // 配置登錄頁並允許訪問
.formLogin().permitAll()
// 配置Basic登錄
//.and().httpBasic()
// 配置登出頁面
.and().logout().logoutUrl("/logout").logoutSuccessUrl("/")
.and().authorizeRequests().antMatchers("/oauth/**", "/login/**", "/logout/**").permitAll()
// 其余所有請求全部需要鑒權認證
.anyRequest().authenticated()
// 關閉跨域保護;
.and().csrf().disable();
}
}
寫個測試接口:
package com.example.oauth2.implicit.web.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.security.Principal;
/**
* <pre>
* 用戶信息控制類
* </pre>
*
* <pre>
* @author mazq
* 修改記錄
* 修改后版本: 修改人: 修改日期: 2020/06/11 14:09 修改內容:
* </pre>
*/
@RestController
@RequestMapping("/api")
public class UserController {
@GetMapping("/userinfo")
public Principal getCurrentUser(Principal principal) {
return principal;
}
}
2.5 功能簡單測試
訪問授權鏈接,在瀏覽器訪問就可以,簡化模式response_type參數傳token:
因為沒登錄,所以會返回SpringSecurity的默認登錄頁面,具體代碼是 http .formLogin().permitAll();
,如果要彈窗登錄的,可以配置http.httpBasic();
,這種配置是沒有登錄頁面的,自定義登錄頁面可以這樣配置http.formLogin().loginPage("/login").permitAll()
如圖,輸入SpringSecurity配置的靜態賬號密碼:nicky/123
登錄成功,返回redirect_uri,拿到token
http://127.0.0.1:8084/cms/login#access_token=9b021755-7df3-48a4-bf58-40815a4dcc9b&token_type=bearer&expires_in=119
拿到token直接去調業務接口:
http://localhost:8888/api/userinfo?access_token=9b021755-7df3-48a4-bf58-40815a4dcc9b
{
"authorities":[
{
"authority":"ROLE_admin"
}
],
"details":{
"remoteAddress":"0:0:0:0:0:0:0:1",
"sessionId":null
},
"authenticated":true,
"principal":{
"password":null,
"username":"nicky",
"authorities":[
{
"authority":"ROLE_admin"
}
],
"accountNonExpired":true,
"accountNonLocked":true,
"credentialsNonExpired":true,
"enabled":true
},
"credentials":null,
"name":"nicky"
}
例子代碼下載:code download