Java中OAuth2.0第三方授權(quán)原理與實(shí)戰(zhàn)
RFC6749
OAuth2的官方文檔在RFC6749:https://datatracker.ietf.org/doc/html/rfc6749
以王者榮耀請(qǐng)求微信登錄的過(guò)程為例

- A:Client申請(qǐng)?jiān)L問(wèn)用戶資源
- B:用戶授權(quán)(過(guò)程較復(fù)雜)一次有效
- C:Client向Server請(qǐng)求一個(gè)長(zhǎng)時(shí)間有效的token
- D:返回token
- E:使用token訪問(wèn)資源
OAuth 2.0授權(quán)4大模式
授權(quán)碼模式:完整、嚴(yán)密,第三方認(rèn)證和授權(quán)的最主流實(shí)現(xiàn)

- A:APP請(qǐng)求微信登錄
- B:用戶信息驗(yàn)證
- C:返回只能使用一次的授權(quán)碼
- D:APP把授權(quán)碼發(fā)給后臺(tái)服務(wù),服務(wù)請(qǐng)求微信登錄請(qǐng)求一個(gè)長(zhǎng)期有效的token
- E:返回長(zhǎng)期有效的token
token只有APP的后臺(tái)服務(wù)和微信的后臺(tái)才有,二者之間使用token通信,保證數(shù)據(jù)不易泄露到后臺(tái)黑客
簡(jiǎn)化模式:令牌用戶可見(jiàn),移動(dòng)端的常用實(shí)現(xiàn)手段

- A:APP請(qǐng)求微信登錄
- B:用戶信息驗(yàn)證
- C:直接返回一個(gè)長(zhǎng)期有效的token
密碼模式:用戶名密碼都返回

- A:用戶把用戶名 + 密碼發(fā)送給APP
- B:APP可以直接用賬號(hào) + 密碼訪問(wèn)微信后臺(tái)
客戶端模式:后臺(tái)內(nèi)容應(yīng)用之間進(jìn)行訪問(wèn)

微信登錄會(huì)為APP分配一個(gè)內(nèi)部的特定用戶名和密碼,APP用這個(gè)賬號(hào) + 密碼和微信溝通,微信返回一個(gè)token,APP可以用這個(gè)token做很多部門自身的事情
合同到期后的續(xù)約機(jī)制
通常拿到Access token時(shí)還會(huì)拿到一個(gè)Refresh token,可以用來(lái)延長(zhǎng)token的有效期

- F:token已過(guò)期
- G:使用Refresh token和微信登錄溝通,嘗試延長(zhǎng)token
- H:重新返回一個(gè)新的token
OAuth2.0第三方授權(quán)實(shí)戰(zhàn)
oauth-client
表示王者榮耀端
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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.example</groupId>
<artifactId>oauth-client</artifactId>
<version>1.0-SNAPSHOT</version>
<description>Demo project for Spring Boot</description>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring-cloud.version>2020.0.0-M5</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
SpringBoot的經(jīng)典啟動(dòng)類:
package com.wjw.oauthclient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class OauthClientApplication {
public static void main(String[] args) {
SpringApplication.run(OauthClientApplication.class, args);
}
}
創(chuàng)建OAuth2的配置類:
package com.wjw.oauthclient.config;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
//激活OAuth2 SSO客戶端
@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.csrf()
.disable();
}
}

配置文件application.yaml:
security.oauth2.client.client-secret=my_client_secret security.oauth2.client.client-id=my_client_id security.oauth2.client.user-authorization-uri=http://localhost:9090/oauth/authorize security.oauth2.client.access-token-uri=http://localhost:9090/oauth/token security.oauth2.resource.user-info-uri=http://localhost:9090/user server.port=8080 server.servlet.session.cookie.name=ut
- 定義用戶id為my_client_id,密碼為my_client_secret
- 定義“微信登錄服務(wù)”為:http://localhost:9090/oauth/authorize
- 請(qǐng)求長(zhǎng)期有效token的地址為:http://localhost:9090/oauth/token
- 拿到token后請(qǐng)求用戶信息的地址為:http://localhost:9090/user
oauth-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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>oauth-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>oauth-server</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2020.0.0-M5</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
創(chuàng)建SpringBoot啟動(dòng)類:
package com.wjw.oauthserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
@SpringBootApplication
@EnableResourceServer
public class OauthServerApplication {
public static void main(String[] args) {
SpringApplication.run(OauthServerApplication.class, args);
}
}

創(chuàng)建controller:
提供一個(gè)API接口,模擬資源服務(wù)器
package com.wjw.oauthserver.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.security.Principal;
@RestController
public class UserController {
@GetMapping("/user")
public Principal getCurrentUser(Principal principal) {
return principal;
}
}
創(chuàng)建oauth2的關(guān)鍵配置類:
package com.wjw.oauthserver.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.password.PasswordEncoder;
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;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
PasswordEncoder passwordEncoder;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory() // 在內(nèi)存里
.withClient("my_client_id")
.secret(passwordEncoder.encode("my_client_secret")) // 加密密碼
.autoApprove(true) // 允許所有子類用戶登錄
.redirectUris("http://localhost:8080/login") // 反跳會(huì)登錄頁(yè)面
.scopes("all")
.accessTokenValiditySeconds(3600)
.authorizedGrantTypes("authorization_code"); // 4大模式里的授權(quán)碼模式
}
}
現(xiàn)在需要提供一個(gè)登錄頁(yè)面讓用戶輸入用戶名和密碼,并設(shè)置一個(gè)管理員賬戶:
package com.wjw.oauthserver.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/login")
.antMatchers("/oauth/authorize")
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin()
.permitAll()
.and()
.csrf().disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("wjw")
.password(passwordEncoder().encode("123456"))
.roles("admin");
}
}
配置文件application.yaml:
server.port=9090
分別啟動(dòng)oauth-server和oauth-client進(jìn)行測(cè)試:
訪問(wèn)localhost:8080/hello

訪問(wèn)hello時(shí)會(huì)重定向到login

訪問(wèn)登錄時(shí)又會(huì)重定向到微信登錄:

之后又會(huì)重定向到:

輸入賬戶名+密碼:

繼續(xù)看控制臺(tái):
登錄成功后還是會(huì)被重定向

此時(shí)再訪問(wèn)就會(huì)有授權(quán)碼了(只能用一次):

此時(shí)瀏覽器拿著授權(quán)碼才去真正請(qǐng)求王者榮耀的hello接口(瀏覽器自動(dòng)使用授權(quán)碼交換token,對(duì)用戶透明):

王者榮耀使用授權(quán)碼請(qǐng)求微信得到一個(gè)token和用戶信息并返回給瀏覽器,瀏覽器再使用這個(gè)token調(diào)王者榮耀的hello接口拿用戶信息:

到此這篇關(guān)于Java中OAuth2.0第三方授權(quán)原理與實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)Java OAuth2.0第三方授權(quán)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解maven中profiles使用實(shí)現(xiàn)
本文主要介紹了maven中profiles使用實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02
運(yùn)用springboot搭建并部署web項(xiàng)目的示例
這篇文章主要介紹了運(yùn)用springboot搭建并部署web項(xiàng)目的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06
使用sharding-jdbc實(shí)現(xiàn)水平分庫(kù)+水平分表的示例代碼
本文主要介紹了使用sharding-jdbc實(shí)現(xiàn)水平分庫(kù)+水平分表,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12
SpringMVC的Body參數(shù)攔截的問(wèn)題
SpringMVC對(duì)出參和入?yún)⒂蟹浅S押玫耐卣怪С?方便你對(duì)數(shù)據(jù)的輸入和輸出有更大的執(zhí)行權(quán),我們?nèi)绾瓮ㄟ^(guò)SpringMVC定義的結(jié)果做一系列處理呢,需要的朋友可以參考下2018-06-06
Java學(xué)習(xí)關(guān)于循環(huán)和數(shù)組練習(xí)題整理
在本篇文章里小編給各位整理了關(guān)于Java學(xué)習(xí)關(guān)于循環(huán)和數(shù)組練習(xí)題相關(guān)內(nèi)容,有興趣的朋友們跟著參考學(xué)習(xí)下。2019-07-07
淺談Java代理(jdk靜態(tài)代理、動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理)
下面小編就為大家?guī)?lái)一篇淺談Java代理(jdk靜態(tài)代理、動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01
@Transactional和@DS怎樣在事務(wù)中切換數(shù)據(jù)源
這篇文章主要介紹了@Transactional和@DS怎樣在事務(wù)中切換數(shù)據(jù)源問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07

