1.互聯網開放平臺設計
1.1.需求:
現在A公司與B公司進行合作,B公司需要調用A公司開放的外網接口獲取數據,
如何保證外網開放接口的安全性。
1.2.常用解決辦法:
2.1 使用加簽名方式,防止篡改數據
2.2 使用Https加密傳輸
2.3 搭建OAuth2.0認證授權
2.4 使用令牌方式
2.5 搭建網關實現黑名單和白名單
1.3.URL轉碼
1.3.1什麼是URL轉碼
不管是以何種方式傳遞url時,如果要傳遞的url中包含特殊字符,如想要傳遞一個+,但是這個+會被url會被編碼成空格,想要傳遞&,被url處理成分隔符。
尤其是當傳遞的url是經過Base64加密或者RSA加密後的,存在特殊字符時,這裏的特殊字符一旦被url處理,就不是原先你加密的結果了。
URLEncode和URLDecode
接受參數案例tranIndex
@RestController
public class TranController {
// 接受客戶端參數
@RequestMapping("/tranIndex")
public String tranIndex(String name) {
System.out.println("name:" + name);
return name;
}
}
客戶端訪問結果
傳入+參數變爲了空格。
解決辦法:將+變爲%2B
Java代碼處理轉碼
URLEncoder.encode和decode
String encode = URLEncoder.encode("1+1", "UTF-8");
String decode = URLDecoder.decode(encode, "UTF-8");
System.out.println("encode:" + encode + ",decode:" + decode);
Http接口參數編碼處理
String url = "http://127.0.0.1:8080/tranIndex?";
// 參數轉碼
String strParam = "name=" + URLEncoder.encode("1+1", "utf-8");
String newUrl = url + strParam;
String result = HttpClientUtils.httpGet(newUrl);
System.out.println("result:" + result);
2.使用令牌方式搭建搭建API開放平臺
原理:爲每個合作機構創建對應的appid、app_secret,生成對應的access_token(有效期2小時),在調用外網開放接口的時候,必須傳遞有效的access_token。
數據庫表設計
CREATE TABLE `m_app` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`app_name` varchar(255) DEFAULT NULL,
`app_id` varchar(255) DEFAULT NULL,
`app_secret` varchar(255) DEFAULT NULL,
`is_flag` varchar(255) DEFAULT NULL,
`access_token` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
App_Name 表示機構名稱
App_ID 應用id
App_Secret 應用密鑰 (可更改)
Is_flag 是否可用 (是否對某個機構開放)
access_token 上一次access_token
獲取AccessToken
// 創建獲取getAccessToken
@RestController
@RequestMapping(value = "/auth")
public class AuthController extends BaseApiService {
@Autowired
private BaseRedisService baseRedisService;
private long timeToken = 60 * 60 * 2;
@Autowired
private AppMapper appMapper;
// 使用appId+appSecret 生成AccessToke
@RequestMapping("/getAccessToken")
public ResponseBase getAccessToken(AppEntity appEntity) {
AppEntity appResult = appMapper.findApp(appEntity);
if (appResult == null) {
return setResultError("沒有對應機構的認證信息");
}
int isFlag = appResult.getIsFlag();
if (isFlag == 1) {
return setResultError("您現在沒有權限生成對應的AccessToken");
}
// ### 獲取新的accessToken 之前刪除之前老的accessToken
// 從redis中刪除之前的accessToken
String accessToken = appResult.getAccessToken();
baseRedisService.delKey(accessToken);
// 生成的新的accessToken
String newAccessToken = newAccessToken(appResult.getAppId());
JSONObject jsonObject = new JSONObject();
jsonObject.put("accessToken", newAccessToken);
return setResultSuccessData(jsonObject);
}
private String newAccessToken(String appId) {
// 使用appid+appsecret 生成對應的AccessToken 保存兩個小時
String accessToken = TokenUtils.getAccessToken();
// 保證在同一個事物redis 事物中
// 生成最新的token key爲accessToken value 爲 appid
baseRedisService.setString(accessToken, appId, timeToken);
// 表中保存當前accessToken
appMapper.updateAccessToken(accessToken, appId);
return accessToken;
}
}
編寫攔截器攔截請求,驗證accessToken
//驗證AccessToken 是否正確
@Component
public class AccessTokenInterceptor extends BaseApiService implements HandlerInterceptor {
@Autowired
private BaseRedisService baseRedisService;
/**
* 進入controller層之前攔截請求
*
* @param httpServletRequest
* @param httpServletResponse
* @param o
* @return
* @throws Exception
*/
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o)
throws Exception {
System.out.println("---------------------開始進入請求地址攔截----------------------------");
String accessToken = httpServletRequest.getParameter("accessToken");
// 判斷accessToken是否空
if (StringUtils.isEmpty(accessToken)) {
// 參數Token accessToken
resultError(" this is parameter accessToken null ", httpServletResponse);
return false;
}
String appId = (String) baseRedisService.getString(accessToken);
if (StringUtils.isEmpty(appId)) {
// accessToken 已經失效!
resultError(" this is accessToken Invalid ", httpServletResponse);
return false;
}
// 正常執行業務邏輯...
return true;
}
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o,
ModelAndView modelAndView) throws Exception {
System.out.println("--------------處理請求完成後視圖渲染之前的處理操作---------------");
}
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
Object o, Exception e) throws Exception {
System.out.println("---------------視圖渲染之後的操作-------------------------0");
}
// 返回錯誤提示
public void resultError(String errorMsg, HttpServletResponse httpServletResponse) throws IOException {
PrintWriter printWriter = httpServletResponse.getWriter();
printWriter.write(new JSONObject().toJSONString(setResultError(errorMsg)));
}
}
3.基於OAuth2.0協議方式
3.1什麼是OAuth
OAuth: OAuth(開放授權)是一個開放標準,允許用戶授權第三方網站訪問他們存儲在另外的服務提供者上的信息,而不需要將用戶名和密碼提供給第三方網站或分享他們數據的所有內容。
3.2 OAuth2.0
對於用戶相關的OpenAPI(例如獲取用戶信息,動態同步,照片,日誌,分享等),爲了保護用戶數據的安全和隱私,第三方網站訪問用戶數據前都需要顯式的向用戶徵求授權。
QQ登錄OAuth2.0採用OAuth2.0標準協議來進行用戶身份驗證和獲取用戶授權,相對於之前的OAuth1.0協議,其認證流程更簡單和安全。
OAuth2.0總體處理流程
1 第一步:用戶同意授權,獲取code
2 第二步:通過code換取網頁授權access_token
3 第三步:刷新access_token(如果需要)
4 第四步:拉取用戶信息(需scope爲 snsapi_userinfo)