1.互联网开放平台设计:
1.需求:现在A公司与B公司进行合作,B公司需要调用A公司开放的外网接口获取数据,
如何保证外网开放接口的安全性。
2.开放平台提供者:需要提供为每个合作机构提供对应的appId,appSecret (需要appId+appSecret生成对应的accessToken)
3.基于令牌方式实现
appId (第三方合作机构,区分不同的机构) 永远不能改变
appSecret (在传输中实现加密功能,密钥) 可以发生改变
(因为如果被人知道,可以伪造请求,当发现appSecret被人知道后,可以重新生成,此时伪造请求会报错)
2.使用令牌方式搭建搭建API开放平台:
1.原理:为每个合作机构创建对应的appId、appSecret,生成对应的accessToken(有效期2小时),在调用外网开放接口的时候,必须传递有效的accessToken。
2.数据库表设计:
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
3.获取AccessToken:
@RequestMapping("/accessToken")
public interface AccessTokenService {
@RequestMapping("/getAccessToken")
ResponseBase getAccessToken(String appId,String appSecret);
}
@RestController
public class AccessTokenServiceImpl extends BaseApiService implements AccessTokenService {
private long timeToken = 60 * 60 * 2;
@Autowired
private AccessTokenDao accessTokenDao;
@Override
public ResponseBase getAccessToken(String appId, String appSecret) {
//先查询是否存在该机构的认证信息
AppEntity appEntity = accessTokenDao.findApp(appId, appSecret);
if (null == appEntity){
return setResultError("没有存在该机构的认证信息");
}
//判断该机构现在是否有效
if (1 == appEntity.getIsFlag()){
return setResultError("您现在没有权限生成对应的AccessToken");
}
// ### 获取新的accessToken 之前删除之前老的accessToken
// 从redis中删除之前的accessToken
String accessToken = appEntity.getAccessToken();
if (null != accessToken){
baseRedisService.delKey(accessToken);
}
String newAccessToken = newAccessToken(appId);
JSONObject jsonObject = new JSONObject();
jsonObject.put("accessToken", newAccessToken);
return setResultSuccess(jsonObject);
}
private String newAccessToken(String appId) {
// 使用appid+appsecret 生成对应的AccessToken 保存两个小时
String accessToken = UUID.randomUUID().toString().replace("-", "");
// 保证在同一个事物redis 事物中
// 生成最新的token key为accessToken value 为 appid
baseRedisService.setString(accessToken, appId, timeToken);
// 表中保存当前accessToken
accessTokenDao.updateAccessToken(accessToken, appId);
return accessToken;
}
}
4.编写拦截器拦截请求,验证accessToken:
@Component
public class AccessTokenInterceptor extends BaseApiService implements HandlerInterceptor {
@Autowired
private RedisService redisService;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("---------------------开始进入请求地址拦截----------------------------");
String accessToken = httpServletRequest.getParameter("accessToken");
if (StringUtils.isEmpty(accessToken)){
resultError("this is parameter accessToken null",httpServletResponse);
return false;
}
String appId = redisService.getString(accessToken);
if (StringUtils.isEmpty(appId)){
//accessToken失效
resultError("this is accessToken Invalid",httpServletResponse);
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
public void resultError(String msg,HttpServletResponse httpServletResponse) throws IOException {
PrintWriter writer = httpServletResponse.getWriter();
writer.print(new JSONObject().toJSONString(setResultError(msg)));
}
}
@Configuration
public class WebAppConfig {
@Autowired
private AccessTokenInterceptor accessTokenInterceptor;
@Bean
public WebMvcConfigurer WebMvcConfigurer() {
return new WebMvcConfigurer() {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(accessTokenInterceptor).addPathPatterns("/openApi/*");
};
};
}
}
@RequestMapping("/testAccessToken")
public interface TestAccessTokenService {
@RequestMapping("/test")
public ResponseBase getTest();
}
@RestController
public class TestAccessTokenServiceImpl extends BaseApiService implements TestAccessTokenService {
@Override
public ResponseBase getTest() {
return setResultSuccess("调取接口成功");
}
}