引言
本文代碼已提交至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平臺的應用場景
在「淘東電商項目」前幾篇博客已經講解過微信公衆號開發,開發前我們都會登錄微信開放平臺去獲取appId
和appSecret
,其實這裏的微信公衆平臺就是一個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.模擬合作伙伴提交個人信息,申請appId
和appSecret
,瀏覽器訪問: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