授權碼模式
創建父工程
cloud-oauth2-parent
pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wj</groupId>
<artifactId>cloud-oauth2-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>cloud-oauth2-base</module>
<module>cloud-oauth2-auth-server</module>
</modules>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/>
</parent>
<properties>
<spring-cloud.version>Hoxton.SR1</spring-cloud.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<mybatis-plus.version>3.2.0</mybatis-plus.version>
<druid.version>1.1.12</druid.version>
<kaptcha.version>2.3.2</kaptcha.version>
<fastjson.version>1.2.8</fastjson.version>
<commons-lang.version>2.6</commons-lang.version>
<commons-collections.version>3.2.2</commons-collections.version>
<commons-io.version>2.6</commons-io.version>
<!-- 定義版本號, 子模塊直接引用-->
<oauth-security.version>1.0-SNAPSHOT</oauth-security.version>
</properties>
<!--依賴聲明-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<!--maven不支持多繼承,使用import來依賴管理配置-->
<scope>import</scope>
</dependency>
<!--mybatis-plus啟動器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!--druid連接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- kaptcha 用於圖形驗證碼 -->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>${kaptcha.version}</version>
</dependency>
<!-- 工具類依賴 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commons-lang.version}</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commons-collections.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!--springboot 打包插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.2.4.RELEASE</version>
</plugin>
</plugins>
</build>
</project>
創建子工程
cloud-oauth2-base
pom依賴
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud-oauth2-parent</artifactId>
<groupId>com.wj</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-oauth2-base</artifactId>
<dependencies>
<!--類中setter/getter,使用注解-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 工具類依賴 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
</dependencies>
</project>
創建通用返回類:com.wj.base.result.R
@Data
public class R implements Serializable {
// 響應業務狀態
private Integer code;
// 響應消息
private String message;
// 響應中的數據
private Object data;
public R() {
}
public R(Object data) {
this.code = 200;
this.message = "OK";
this.data = data;
}
public R(String message, Object data) {
this.code = 200;
this.message = message;
this.data = data;
}
public R(Integer code, String message, Object data) {
this.code = code;
this.message = message;
this.data = data;
}
public static R ok() {
return new R(null);
}
public static R ok(String message) {
return new R(message, null);
}
public static R ok(Object data) {
return new R(data);
}
public static R ok(String message, Object data) {
return new R(message, data);
}
public static R build(Integer code, String message) {
return new R(code, message, null);
}
public static R build(Integer code, String message, Object data) {
return new R(code, message, data);
}
public String toJsonString() {
return JSON.toJSONString(this);
}
/**
* JSON字符串轉成 R 對象
*/
public static R format(String json) {
try {
return JSON.parseObject(json, R.class);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
cloud-oauth2-auth-server
基本配置
pom依賴:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud-oauth2-parent</artifactId>
<groupId>com.wj</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-oauth2-auth-server</artifactId>
<dependencies>
<dependency>
<artifactId>cloud-oauth2-base</artifactId>
<groupId>com.wj</groupId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--spring mvc相關的-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Security、OAuth2 和JWT等 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<!-- 注冊到 Eureka
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
-->
<!-- redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--mybatis-plus啟動器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.2.4.RELEASE</version>
<configuration>
<mainClass>com.wj.oauth2.AuthServerApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.yml
server:
port: 8090
servlet:
context-path: /auth
spring:
datasource:
username: root
password: 1234
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/study-security?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8
springboot主啟動類com.wj.oauth2.AuthServerApplication:
@SpringBootApplication
public class AuthServerApplication {
public static void main(String[] args) {
SpringApplication.run(AuthServerApplication.class, args);
}
}
配置類
統一管理Bean配置類: SpringSecurityBean
@Configuration
public class SpringSecurityBean {
@Bean //引入PasswordEncoder
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
SpringSecurity配置類:
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 內存方式存儲用戶信息,這里為了方便就不從數據庫中查詢了
auth.inMemoryAuthentication().withUser("admin")
.password(passwordEncoder.encode("1234"))
.authorities("product");
}
}
認證服務器配置類:AuthorizationServerConfig
@Configuration
@EnableAuthorizationServer//開啟認證服務器功能
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
/** 配置被允許訪問此認證服務器的客戶端詳情信息
* 方式1:內存方式管理
* 方式2:數據庫管理
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 使用內存方式
clients.inMemory()
// 客戶端id
.withClient("wj-pc")
// 客戶端密碼,要加密,不然一直要求登錄
.secret(passwordEncoder.encode("wj-secret"))
// 資源id, 如商品資源
.resourceIds("product-server")
// 授權類型, 可同時支持多種授權類型
.authorizedGrantTypes("authorization_code", "password", "implicit","client_credentials","refresh_token")
// 授權范圍標識,哪部分資源可訪問(all是標識,不是代表所有)
.scopes("all")
// false 跳轉到授權頁面手動點擊授權,true 不用手動授權,直接響應授權碼,
.autoApprove(false)
.redirectUris("http://www.baidu.com/");// 客戶端回調地址
}
}
配置說明:
withClient:允許訪問此認證服務器的客戶端id , 如:PC、APP、小程序各不同的的客戶端id。
secret:客戶端密碼,要加密存儲,不然獲取不到令牌一直要求登錄,, 而且一定不能被泄露。
authorizedGrantTypes: 授權類型, 可同時支持多種授權類型:可配置:"authorization_code", "password", "implicit","client_credentials","refresh_token"
scopes:授權范圍標識,如指定微服務名稱,則只能訪問指定的微服務。
autoApprove:false 跳轉到授權頁面手動點擊授權,true 不用手動授權,直接響應授權碼
redirectUris 當獲取授權碼后,認證服務器會重定向到這個URI,並且帶着一個授權碼code響應回來
令牌訪問端點
Spring Security 對 OAuth2 默認提供了可直接訪問端點,即URL:
/oauth/authorize:申請授權碼 code, 涉及的類AuthorizationEndpoint
/oauth/token:獲取令牌 token, 涉及的類TokenEndpoint/oauth/check_token:用於資源服務器請求端點來檢查令牌是否有效, 涉及的類CheckTokenEndpoint
/oauth/confirm_access:用戶確認授權提交, 涉及的類WhitelabelApprovalEndpoint
/oauth/error:授權服務錯誤信息, 涉及的類WhitelabelErrorEndpoint
/oauth/token_key:提供公有密匙的端點,使用 JWT 令牌時會使用 , 涉及的類TokenKeyEndpoint
測試
發送請求獲取授權碼code
訪問:localhost:8090/auth/oauth/authorize?client_id=wj-pc&response_type=code
這里的client_id是在AuthorizationServerConfig中配置的。
輸入賬號密碼進行登陸,賬號和密碼:admin/1234 ,是在SpringSecurityConfig中配置的
登陸成功后,選擇Approve,點擊Authorize,這里跳轉到www.baidu.com ,並且后面攜帶了code,這里的code就是授權碼,后面我們就可以通過授權碼來獲取令牌(access_token)
通過授權碼獲取令牌
使用postman測試:http://localhost:8090/auth/oauth/token
這里的username和password是在AuthorizationServerConfig中配置的
設置為post請求,並設置請求體
這里的grant_type是authorization_code,code是上一步獲取的code
發送請求后,獲得了access_token
注意,code只能獲取一次access_token,獲取后就會失效,第二次獲取就會失敗
密碼授權模式
密碼模式(Resource Owner Password Credentials Grant)中,用戶向客戶端提供自己在服務提供商(認證服務器)上的用戶名和密碼,然后客戶端通過用戶提供的用戶名和密碼向服務提供商(認證服務器)獲取令牌。
如果用戶名和密碼遺漏,服務提供商(認證服務器)無法判斷客戶端提交的用戶和密碼是否盜取來的,那意味着令牌就可隨時獲取,數據被丟失。
所以密碼授權模式適用於產品都是企業內部的,用戶名密碼共享不要緊。如果是第三方這種不太適合。也適用手機APP提交用戶名密碼。
配置密碼模式
修改SpringSecurityConfig配置類:
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 內存方式存儲用戶信息
auth.inMemoryAuthentication().withUser("admin")
.password(passwordEncoder.encode("1234"))
.authorities("product");
}
/**
* password密碼模式需要使用此認證管理器
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
修改AuthorizationServerConfig類:
@Configuration
@EnableAuthorizationServer//開啟認證服務器功能
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private AuthenticationManager authenticationManager;
/** 配置被允許訪問此認證服務器的客戶端詳情信息
* 方式1:內存方式管理
* 方式2:數據庫管理
* localhost:8090/auth/oauth/authorize?client_id=wj-pc&response_type=code
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 使用內存方式
clients.inMemory()
// 客戶端id
.withClient("wj-pc")
// 客戶端密碼,要加密,不然一直要求登錄, 獲取不到令牌, 而且一定不能被泄露
.secret(passwordEncoder.encode("wj-secret"))
// 資源id, 如商品資源
.resourceIds("product-server")
// 授權類型, 可同時支持多種授權類型
.authorizedGrantTypes("authorization_code", "password", "implicit","client_credentials","refresh_token")
// 授權范圍標識,哪部分資源可訪問(all是標識,不是代表所有)
.scopes("all")
// false 跳轉到授權頁面手動點擊授權,true 不用手動授權,直接響應授權碼,
.autoApprove(false)
.redirectUris("http://www.baidu.com/");// 客戶端回調地址
}
/**
* 重寫父類的方法
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//密碼模式需要設置此認證管理器
endpoints.authenticationManager(authenticationManager);
}
}
測試使用
使用postman測試:
訪問:http://localhost:8090/auth/oauth/token
填上請求參數:
發送請求即可獲取到access_token:
簡化模式
不通過第三方應用程序的服務器,直接在瀏覽器中向認證服務器申請令牌,不需要先獲取授權碼。直接可以一次請求就可得到令牌,在redirect_uri指定的回調地址中傳遞令牌( access_token )。該模式適合直接運行在瀏覽器上的應用,不用后端支持(例如 Javascript 應用)
注意:只需要客戶端id,客戶端密碼都不需要。
瀏覽器直接訪問:localhost:8090/auth/oauth/authorize?client_id=wj-pc&response_type=token
先登錄:
登陸成功並授權后,直接跳轉到指定頁面,並在uri中返回了access_token
注意
-
簡化模式不允許按照 OAuth2 規范發布刷新令牌(refresh token)。這種行為是有必要的,它要求在使用運行在瀏覽器中的程序時,用戶必須在場,這樣可以在任何需要的時候,給第三方應用授權。
-
當使用簡化模式時,第三方應用始終需要通過重定向URI來注冊,這樣能確保不會將token傳給不需要驗證的客戶端。如果不這樣做,一些心懷不軌的用戶可能先注冊一個應用,然后試圖讓其他的應用來頂替,接收這個 token,這樣可能導致災難性的結果
客戶端授權模式
客戶端模式(Client Credentials Grant)指客戶端以自己的名義,而不是以用戶的名義,向服務提供商(認證服務器)進行認證。嚴格地說,客戶端模式並不屬於OAuth框架所要解決的問題。在這種模式中,用戶直接向客戶端注冊,客戶端以自己的名義要求服務提供商(認證服務器)提供服務,其實不存在授權問題。
-
客戶端向認證服務器進行身份認證,並要求一個訪問令牌。
-
認證服務器確認無誤后,向客戶端提供訪問令牌
測試
post測試:http://localhost:8090/auth/oauth/token
grant_type等於client_credentials
發送請求返回了access_token,注意響應結果沒有刷新令牌的