天天看點

從零搭建微服務-認證中心(二)

作者:Java大蝦
從零搭建微服務-認證中心(二)

寫在最前

如果這個項目讓你有所收獲,記得 Star 關注哦,這對我是非常不錯的鼓勵與支援。

源碼位址:gitee.com/csps/mingyu…

文檔位址:gitee.com/csps/mingyu…

建立新項目 MingYue

Idea 建立 maven 項目這兒就不多贅述了,相信各位大佬都是信手拈來~

MingYue Pom

父項目的依賴都放在這兒了,後續用到什麼再增加什麼
xml複制代碼<?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.csp</groupId>
    <artifactId>mingyue</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>

    <name>MingYue</name>
    <description>MingYue 微服務系統</description>

    <properties>
        <mingyue.version>1.0.0</mingyue.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <java.version>1.8</java.version>

        <!-- Spring 版本 -->
        <spring-boot.version>2.7.11</spring-boot.version>
        <spring-cloud.version>2021.0.6</spring-cloud.version>
        <spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version>

        <!-- 工具版本 -->
        <jasypt.version>3.0.5</jasypt.version>
        <hutool.version>5.8.18</hutool.version>
        <lombok.version>1.18.26</lombok.version>
        <spring.checkstyle.plugin>0.0.38</spring.checkstyle.plugin>
    </properties>

    <!-- 以下依賴 全局所有的子產品都會引入  -->
    <dependencies>
        <!--配置檔案處理器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <!--配置檔案加解密-->
        <dependency>
            <groupId>com.github.ulisesbocchio</groupId>
            <artifactId>jasypt-spring-boot-starter</artifactId>
            <version>${jasypt.version}</version>
        </dependency>
        <!--Lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
        <!--測試依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <!-- 依賴聲明 -->
    <dependencyManagement>
        <dependencies>
            <!-- spring boot 依賴 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- spring cloud 依賴 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- spring cloud alibaba 依賴 -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- hutool 的依賴配置-->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-bom</artifactId>
                <version>${hutool.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>
           

建立子項目 MingYue-Auth

Idea mingyue 右擊 -> New -> Module
從零搭建微服務-認證中心(二)

核心依賴

xml複制代碼<properties>
        <sa-token.version>1.34.0</sa-token.version>
</properties>

<dependencies>
  <!-- SpringBoot依賴 -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <!-- Sa-Token 權限認證, 線上文檔:https://sa-token.cc/ -->
  <dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-spring-boot-starter</artifactId>
    <version>${sa-token.version}</version>
  </dependency>

  <!-- Sa-Token-OAuth2.0 子產品 -->
  <dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-oauth2</artifactId>
    <version>${sa-token.version}</version>
  </dependency>

  <!-- Sa-Token 整合redis (使用jackson序列化方式) -->
  <dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-dao-redis-jackson</artifactId>
    <version>${sa-token.version}</version>
  </dependency>

  <!-- 提供Redis連接配接池 -->
  <dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
  </dependency>

  <!-- 視圖引擎(在前後端不分離模式下提供視圖支援) -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
  </dependency>
</dependencies>
           

SaOAuth2ServerController

java複制代碼/**
 * Sa-OAuth2 Server端 控制器
 * @author kong
 *
 */
@RestController
public class SaOAuth2ServerController {

	// 處理所有OAuth相關請求
	@RequestMapping("/oauth2/*")
	public Object request() {
		System.out.println("------- 進入請求: " + SaHolder.getRequest().getUrl());
		return SaOAuth2Handle.serverRequest();
	}

	// Sa-OAuth2 定制化配置
	@Autowired
	public void setSaOAuth2Config(SaOAuth2Config cfg) {
		cfg.
			// 未登入的視圖
			setNotLoginView(()->{
				return new ModelAndView("login.html");
			}).
			// 登入處理函數
			setDoLoginHandle((name, pwd) -> {
				if("sa".equals(name) && "123456".equals(pwd)) {
					StpUtil.login(10001);
					return SaResult.ok();
				}
				return SaResult.error("賬号名或密碼錯誤");
			}).
			// 授權确認視圖
			setConfirmView((clientId, scope)->{
				Map<String, Object> map = new HashMap<>();
				map.put("clientId", clientId);
				map.put("scope", scope);
				return new ModelAndView("confirm.html", map);
			})
			;
	}

	// 全局異常攔截
	@ExceptionHandler
	public SaResult handlerException(Exception e) {
		e.printStackTrace();
		return SaResult.error(e.getMessage());
	}


	// ---------- 開放相關資源接口: Client端根據 Access-Token ,置換相關資源 ------------

	// 擷取Userinfo資訊:昵稱、頭像、性别等等
	@RequestMapping("/oauth2/userinfo")
	public SaResult userinfo() {
		// 擷取 Access-Token 對應的賬号id
		String accessToken = SaHolder.getRequest().getParamNotNull("access_token");
		Object loginId = SaOAuth2Util.getLoginIdByAccessToken(accessToken);
		System.out.println("-------- 此Access-Token對應的賬号id: " + loginId);

		// 校驗 Access-Token 是否具有權限: userinfo
		SaOAuth2Util.checkScope(accessToken, "userinfo");

		// 模拟賬号資訊 (真實環境需要查詢資料庫擷取資訊)
		Map<String, Object> map = new LinkedHashMap<String, Object>();
		map.put("nickname", "shengzhang_");
		map.put("avatar", "http://xxx.com/1.jpg");
		map.put("age", "18");
		map.put("sex", "男");
		map.put("address", "山東省 青島市 城陽區");
		return SaResult.data(map);
	}

}
           

SaOAuth2TemplateImpl

java複制代碼/**
 * Sa-Token OAuth2.0 整合實作
 * @author kong
 */
@Component
public class SaOAuth2TemplateImpl extends SaOAuth2Template {

	// 根據 id 擷取 Client 資訊
	@Override
	public SaClientModel getClientModel(String clientId) {
		// 此為模拟資料,真實環境需要從資料庫查詢
		if("1001".equals(clientId)) {
			return new SaClientModel()
					.setClientId("1001")
					.setClientSecret("aaaa-bbbb-cccc-dddd-eeee")
					.setAllowUrl("*")
					.setContractScope("userinfo")
					.setIsAutoMode(true);
		}
		return null;
	}

	// 根據ClientId 和 LoginId 擷取openid
	@Override
	public String getOpenid(String clientId, Object loginId) {
		// 此為模拟資料,真實環境需要從資料庫查詢
		return "gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__";
	}
}
           

添加 templates

  • confirm.html
  • login.html

建立啟動類啟動項目

java複制代碼@SpringBootApplication
public class MingYueAuthApplication {

	public static void main(String[] args) {
		SpringApplication.run(MingYueAuthApplication.class, args);
		System.out.println("\nSa-Token-OAuth  Server端啟動成功: http://localhost:9000/oauth2/authorize?response_type=code&client_id=1001&redirect_uri=https://sa-token.cc&scope=userinfo");
	}

}
           

啟動項目,通路列印位址:

Sa-Token-OAuth Server端啟動成功: http://localhost:9000/oauth2/authorize?response_type=code&client_id=1001&redirect_uri=https://sa-token.cc&scope=userinfo

API 清單

共四種模式,分别是授權碼(Authorization Code)、隐藏式(Implicit)、密碼式(Password)、憑證式(Client Credentials),本文介紹了兩種常用的:授權碼(Authorization Code)、密碼式(Password)

1. 授權碼(Authorization Code)

base複制代碼http://localhost:9000/oauth2/authorize
?response_type=code
&client_id=1001
&redirect_uri=https://sa-token.cc&scope=userinfo
           

參數詳解:

參數 是否必填 說明
response_type 傳回類型,這裡請填寫:code
client_id 應用id
redirect_uri 使用者确認授權後,重定向的url位址
scope 具體請求的權限,多個用逗号隔開
state 随機值,此參數會在重定向時追加到url末尾,不填不追加

1.1 擷取授權碼

Code授權碼具有以下特點:

每次授權産生的 Code 碼都不一樣Code碼用完即廢,不能二次使用一個Code的有效期預設為五分鐘,逾時自動廢棄每次授權産生新 Code 碼,會導緻舊 Code 碼立即廢棄,即使舊 Code 碼尚未使用

http://localhost:9000/oauth2/authorize?response_type=code&client_id=1001&redirect_uri=https://sa-token.cc&scope=userinfo

打開位址,登入并同意授權

從零搭建微服務-認證中心(二)
位址欄拿到 code sa-token.cc/?code=ct0RB…

1.2 根據授權碼擷取 Access-Token

浏覽器直接通路:
base複制代碼http://localhost:9000/oauth2/token
    ?grant_type=authorization_code
    &client_id=1001
    &client_secret=aaaa-bbbb-cccc-dddd-eeee
    &code=ktN6FvIfB7pKqp7Thvkm32EfUhbveoybwOtmvqCCbuLdxevmyr9FW09Kd6qL
           
參數 是否必填 說明
grant_type 授權類型,這裡請填寫:authorization_code
client_id 應用id
client_secret 應用秘鑰
code 步驟1.1中擷取到的授權碼
列印如下:
json複制代碼{
    "code": 200,
    "msg": "ok",
    "data": {
        "access_token": "E1ZLgNXLiUIz8DmAxmgPeegMPsUCEJJLjUV9uwDQWCu4f6Tgg8U5JqKSrqSx",
        "refresh_token": "0IAWolcbMvoIpF8F1JcEuytfcoTe8nNVwsEdUHOy7rBX0V99vhPBvtFGfh62",
        "expires_in": 7199,
        "refresh_expires_in": 2591999,
        "client_id": "1001",
        "scope": "userinfo",
        "openid": "gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__"
    }
}
           

2. 密碼式(Password)

首先在 Client 端建構表單,讓使用者輸入 Server 端的賬号和密碼,然後在 Client 端通路接口
base複制代碼http://localhost:9000/oauth2/token
    ?grant_type=password
    &client_id=1001
    &client_secret=aaaa-bbbb-cccc-dddd-eeee
    &username=sa
    &password=123456
           
參數 是否必填 說明
grant_type 傳回類型,這裡請填寫:password
client_id 應用id
client_secret 應用秘鑰
username 使用者的Server端賬号
password 使用者的Server端密碼
scope 具體請求的權限,多個用逗号隔開
浏覽器直接通路,列印如下:
json複制代碼{
    "code": 200,
    "msg": "ok",
    "data": {
        "access_token": "BvU1yja6sy4WT3orCvIiYExIOhGMBwVps38ZywZWGaeYaM6nJbPkJRbHpmSC",
        "refresh_token": "TzSGmGhtPuAZUZ5ndfVETdmmpGr3ZwAH5N5kbrEYeeR2LKbLBXEysqsLxuV6",
        "expires_in": 7199,
        "refresh_expires_in": 2591999,
        "client_id": "1001",
        "scope": "",
        "openid": "gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__"
    }
}
           

至此,一個基于 OAuth2 的認證中心 Demo 就完成啦,接下來我們先優化一下代碼結構。

繼續閱讀