淘東電商項目(70) -互聯網安全架構設計(搭建開放平臺-OAuth)

引言

本文代碼已提交至Github(版本號:7179531a807c32f1fbe15b17759063840052d161),有興趣的同學可以下載來看看:https://github.com/ylw-github/taodong-shop

在之前博客《淘東電商項目(67) -互聯網安全架構設計(方法論)》,主要講解了互聯網安全架構設計的方法,主要介紹瞭如下幾種:

  • 基於網關實現IP黑名單與名單攔截
  • API接口實現Token授權認證
  • 使用MD5實現API接口驗證簽名,防止抓包篡改數據
  • 實現API接口安全加密傳輸(公鑰和私鑰互換機制)
  • 基於Oauth2.0 實現API接口開放平臺
  • 接口參數使用網關實現防止XSS、SQL注入
  • 定期工具實現代碼健康掃描

上面的打勾代碼實現在前兩篇博客已經講解完,本文主要講解OAuth平臺及其搭建。

本文目錄結構:
l____引言
l____ 1.OAuth平臺的應用場景
l____ 2.爲什麼需要OAuth平臺 ?
l____ 3.搭建OAuth平臺
l________ 3.1 獲取appId和appSecret
l________ 3.2 獲取accessToken
l________ 3.3 獲取用戶信息
l____ 4.測試

1.OAuth平臺的應用場景

「淘東電商項目」前幾篇博客已經講解過微信公衆號開發,開發前我們都會登錄微信開放平臺去獲取appIdappSecret,其實這裏的微信公衆平臺就是一個OAuth平臺的一個應用場景。

在這裏插入圖片描述

2.爲什麼需要OAuth平臺?

當系統逐漸壯大後,如果有合作伙伴需要我們系統的一些資源,需要我們提供接口給他們,這個時候如果直接暴露接口是很危險的,他們可以把接口地址提供給他人,讓他人直接調用。因此在合作伙伴調用我們的接口時,我們更希望知道是誰調用了我們的接口、並控制他們調用接口的次數、以及控制他們能調哪些接口等等,這個時候,我們需要OAuth平臺。

下面附上一張畫好的OAuth原理圖:
在這裏插入圖片描述

對於上圖我們可能會提出一個疑問:

爲什麼appId一定要與appSecret搭配?

答:appId相當於是商戶id,類似於QQ號,而appSecret類似於QQ密碼,appSecret可能會被黑客非法利用,所以我們在後臺裏,當用戶發現自己的appSecret被非法利用時,可以做修改。而如果只使用appId的話(是用戶號,唯一識別),那被黑客非法利用了就無法修改了,因爲修改了就無法識別用戶的唯一性了,微信開放平臺也是這樣做的。

從原理圖,我們知道開放平臺需要提供幾個接口:

  • 獲取appId和appSecret接口(用戶需要提交個人信息,如:名稱、身份證、營業執照等,爲了方便演示,直接提供名稱即可獲取)
  • 根據appId和appSecret獲取AccessToken接口
  • 使用AccessToken接口獲取平臺的信息(下面將以獲取用戶信息爲例子)

好了,下面開始直接講解代碼。

3.搭建OAuth平臺

先貼上數據庫建表語句:

CREATE TABLE `app_info` (
  `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵id',
  `APP_NAME` varchar(100) DEFAULT NULL COMMENT '應用名稱',
  `APP_ID` varchar(200) DEFAULT NULL COMMENT '應用id',
  `APP_SECRET` varchar(255) DEFAULT NULL COMMENT '應用祕鑰',
  `AVAILABILITY` varchar(255) DEFAULT NULL COMMENT '是否可用',
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

3.1 獲取appId和appSecret

①定義接口:

/**
 * 機構申請 獲取appid 和appsecret
 *
 * @return
 */
@GetMapping("/applyAppInfo")
public BaseResponse<JSONObject> applyAppInfo(@RequestParam("appName") String appName);

②接口實現:

@Override
public BaseResponse<JSONObject> applyAppInfo(String appName) {
	// 1.驗證參數
	if (StringUtils.isEmpty(appName)) {
		return setResultError("機構名稱不能爲空!");
	}
	// 2.生成appid和appScrec
	Guid guid = new Guid();
	String appId = guid.getAppId();
	String appScrect = guid.getAppScrect();
	// 3.添加數據庫中
	AppInfo appInfo = new AppInfo();
	appInfo.setAppName(appName);
	appInfo.setAppId(appId);
	appInfo.setAppSecret(appScrect);
	int insertAppInfo = appInfoMapper.insertAppInfo(appInfo);
	if (!toDaoResult(insertAppInfo)) {
		return setResultError("申請失敗!");
	}
	// 4.返回給客戶端
	JSONObject data = new JSONObject();
	data.put("appId", appId);
	data.put("appScrect", appScrect);
	return setResultSuccess(data);
}

3.2 獲取accessToken

①定義接口:

/*
    * 使用appid 和appsecret密鑰獲取AccessToken
    */
   @GetMapping("/getAccessToken")
   public BaseResponse<JSONObject> getAccessToken(@RequestParam("appId") String appId,
                                                  @RequestParam("appSecret") String appSecret);

②接口實現:

@Override
public BaseResponse<JSONObject> getAccessToken(String appId, String appSecret) {
	// 使用appid+appSecret獲取AccessToken
	// 1.參數驗證
	if (StringUtils.isEmpty(appId)) {
		return setResultError("appId不能爲空!");
	}
	if (StringUtils.isEmpty(appSecret)) {
		return setResultError("appSecret不能爲空!");
	}
	// 2.使用appId+appSecret查詢數據庫
	AppInfo appInfo = appInfoMapper.selectByAppInfo(appId, appSecret);
	if (appInfo == null) {
		return setResultError("appId或者是appSecret錯誤");
	}
	// 3.獲取應用機構信息 生成accessToken
	String dbAppId = appInfo.getAppId();
	String accessToken = generateToken.createToken("auth", dbAppId);
	JSONObject data = new JSONObject();
	data.put("accessToken", accessToken);
	return setResultSuccess(data);
}

3.3 獲取用戶信息

①定義接口:

/*
 * 驗證Token是否失效
 */
@GetMapping("/getAppInfo")
public BaseResponse<JSONObject> getAppInfo(@RequestParam("accessToken") String accessToken);

②接口實現:

@Override
public BaseResponse<JSONObject> getAppInfo(String accessToken) {
	// 1.驗證參數
	if (StringUtils.isEmpty(accessToken)) {
		return setResultError("AccessToken cannot be empty ");
	}
	// 2.從redis中獲取accessToken
	String appId = generateToken.getToken(accessToken);
	if (StringUtils.isEmpty(appId)) {
		return setResultError("accessToken  invalid");
	}
	// 3.使用appid查詢數據庫
	AppInfo appInfo = appInfoMapper.findByAppInfo(appId);
	if (appInfo == null) {
		return setResultError("AccessToken  invalid");
	}
	// 4.返回應用機構信息
	JSONObject data = new JSONObject();
	data.put("appInfo", appInfo);
	return setResultSuccess(data);
}

4.測試

1.模擬合作伙伴提交個人信息,申請appIdappSecret,瀏覽器訪問:http://localhost:9500/applyAppInfo?appName=騰訊小馬
在這裏插入圖片描述
2.獲取AccessToken令牌,瀏覽器訪問:http://localhost:9500/getAccessToken?appId=7f38d645-032a-43e7-9f08-b7740288836d&appSecret=BF81CD9C70B597F88CF7794A7961F7FD
在這裏插入圖片描述
3.通過令牌去獲取信息獲取商戶信息,瀏覽器訪問:http://localhost:9500/getAppInfo?accessToken=authfdc563ec2ec049ea8fc66ab777215bb5
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章