欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

互联网安全架构--安全接口安全设计

程序员文章站 2024-03-23 22:10:58
...

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)