引言
本文代码已提交至Github(版本号:
7179531a807c32f1fbe15b17759063840052d161
),有兴趣的同学可以下载来看看:https://github.com/ylw-github/taodong-shop
在之前博客《淘东电商项目(67) -互联网安全架构设计(方法论)》,主要讲解了互联网安全架构设计的方法,主要介绍了如下几种:
- 基于网关实现IP黑名单与名单拦截
- API接口实现Token授权认证
- 使用MD5实现API接口验证签名,防止抓包篡改数据
- 实现API接口安全加密传输(公钥和私钥互换机制)
- 基于Oauth2.0 实现API接口开放平台
- 接口参数使用网关实现防止XSS、SQL注入
- 定期工具实现代码健康扫描
上面的打勾代码实现在前两篇博客已经讲解完,本文主要讲解OAuth平台及其搭建。
本文目录结构:
l________ 3.1 获取appId和appSecret
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