基于AccessToken方式实现API设计

互联网开放平台设计

1.需求:现在A公司与B公司进行合作,B公司需要调用A公司开放的外网接口获取数据,

如何保证外网开放接口的安全性。

2.常用解决办法:

2.1 使用加签名方式,防止篡改数据

2.2 使用Https加密传输

2.3 搭建OAuth2.0认证授权

2.4 使用令牌方式

2.5 搭建网关实现黑名单和白名单

使用令牌方式搭建搭建API开放平台

原理:为每个合作机构创建对应的appid、app_secret,生成对应的access_token(有效期2小时),在调用外网开放接口的时候,必须传递有效的access_token。 

App_Name       表示机构名称

App_ID          应用id

App_Secret      应用密钥  (可更改)

Is_flag           是否可用 (是否对某个机构开放)

access_token  上一次access_token

select * from m_app

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;

localhost:8080/auth/getAccessToken?appId=77c76f3a1eba44f189135d6c47781fb1&appSecret=3b189c084b844359b45ab1c932880d05

77c76f3a1eba44f189135d6c47781fb1

3b189c084b844359b45ab1c932880d05

insert into m_app values(null,'阿里巴巴','77c76f3a1eba44f189135d6c47781fb1','3b189c084b844359b45ab1c932880d05','0','abcd');

{
	rtnCode: 200,
	msg: "success",
	data: {
		accessToken: "598058b15ce04ad58e2f07262227c8df"
	}
}

localhost:8080/openApi/getUser?accessToken=bfbb527574a64c1688adf4c9c5d98b93

{
	rtnCode: 200,
	msg: "success",
	data: {
		accessToken: "bfbb527574a64c1688adf4c9c5d98b93"
	}
}

localhost:8080/openApi/getUser?accessToken=bfbb527574a64c1688adf4c9c5d98b93

{
	rtnCode: 200,
	msg: "获取会员信息接口",
	data: null
}

{
	msg: " this is accessToken Invalid ",
	rtnCode: 500
}

localhost:8080/openApi/getUser?accessToken=7b9f5c6aa09a4f898605f57adec26a7c

 

package com.learn.handler;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.alibaba.fastjson.JSONObject;
import com.learn.base.BaseApiService;
import com.learn.utils.BaseRedisService;

//验证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)));
	}

}
package com.learn.handler;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@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/*");
			};
		};
	}

}
package com.learn.api.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson.JSONObject;
import com.learn.base.BaseApiService;
import com.learn.base.ResponseBase;
import com.learn.entity.AppEntity;
import com.learn.mapper.AppMapper;
import com.learn.utils.BaseRedisService;
import com.learn.utils.TokenUtils;

// 创建获取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;
	}
}
package com.learn.api.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.learn.base.BaseApiService;
import com.learn.base.ResponseBase;

@RestController
@RequestMapping("/openApi")
public class MemberController extends BaseApiService {

	@RequestMapping("/getUser")
	public ResponseBase getUser() {
		return setResultSuccess("获取会员信息接口");
	}
}
package com.learn.base;

import org.springframework.stereotype.Component;

import com.learn.utils.Constants;

@Component
public class BaseApiService {

	public ResponseBase setResultError(Integer code, String msg) {
		return setResult(code, msg, null);
	}

	// 返回错误,可以传msg
	public ResponseBase setResultError(String msg) {
		return setResult(Constants.HTTP_RES_CODE_500, msg, null);
	}

	// 返回成功,可以传data值
	public ResponseBase setResultSuccessData(Object data) {
		return setResult(Constants.HTTP_RES_CODE_200, Constants.HTTP_RES_CODE_200_VALUE, data);
	}

	public ResponseBase setResultSuccessData(Integer code, Object data) {
		return setResult(code, Constants.HTTP_RES_CODE_200_VALUE, data);
	}

	// 返回成功,沒有data值
	public ResponseBase setResultSuccess() {
		return setResult(Constants.HTTP_RES_CODE_200, Constants.HTTP_RES_CODE_200_VALUE, null);
	}

	// 返回成功,沒有data值
	public ResponseBase setResultSuccess(String msg) {
		return setResult(Constants.HTTP_RES_CODE_200, msg, null);
	}

	// 通用封装
	public ResponseBase setResult(Integer code, String msg, Object data) {
		return new ResponseBase(code, msg, data);
	}

}
package com.learn.base;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

@Getter
@Setter
@Slf4j
public class ResponseBase {

	private Integer rtnCode;
	private String msg;
	private Object data;

	public ResponseBase() {

	}

	public ResponseBase(Integer rtnCode, String msg, Object data) {
		super();
		this.rtnCode = rtnCode;
		this.msg = msg;
		this.data = data;
	}
	
	public Integer getRtnCode() {
		return rtnCode;
	}

	public void setRtnCode(Integer rtnCode) {
		this.rtnCode = rtnCode;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	public Object getData() {
		return data;
	}

	public void setData(Object data) {
		this.data = data;
	}

	public static void main(String[] args) {
		ResponseBase responseBase = new ResponseBase();
		responseBase.setData("123456");
		responseBase.setMsg("success");
		responseBase.setRtnCode(200);
		System.out.println(responseBase.toString());
	}

	@Override
	public String toString() {
		return "ResponseBase [rtnCode=" + rtnCode + ", msg=" + msg + ", data=" + data + "]";
	}

}
package com.learn.entity;

public class AppEntity {

	private long id;
	private String appId;
	private String appName;
	private String appSecret;
	private String accessToken;
	private int isFlag;
	
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	public String getAppId() {
		return appId;
	}
	public void setAppId(String appId) {
		this.appId = appId;
	}
	public String getAppName() {
		return appName;
	}
	public void setAppName(String appName) {
		this.appName = appName;
	}
	public String getAppSecret() {
		return appSecret;
	}
	public void setAppSecret(String appSecret) {
		this.appSecret = appSecret;
	}
	public String getAccessToken() {
		return accessToken;
	}
	public void setAccessToken(String accessToken) {
		this.accessToken = accessToken;
	}
	public int getIsFlag() {
		return isFlag;
	}
	public void setIsFlag(int isFlag) {
		this.isFlag = isFlag;
	}
	@Override
	public String toString() {
		return "AppEntity [id=" + id + ", appId=" + appId + ", appName=" + appName + ", appSecret=" + appSecret
				+ ", accessToken=" + accessToken + ", isFlag=" + isFlag + "]";
	}

}
package com.learn.mapper;

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import com.learn.entity.AppEntity;

public interface AppMapper {

	@Select("SELECT ID AS ID ,APP_NAME AS appName, app_id as appId, app_secret as appSecret ,is_flag as isFlag , access_token as accessToken from m_app "
			+ "where app_id=#{appId} and app_secret=#{appSecret}  ")
	AppEntity findApp(AppEntity appEntity);

	@Select("SELECT ID AS ID ,APP_NAME AS appName, app_id as appId, app_secret as appSecret ,is_flag as isFlag  access_token as accessToken from m_app "
			+ "where app_id=#{appId} and app_secret=#{appSecret}  ")
	AppEntity findAppId(@Param("appId") String appId);

	@Update(" update m_app set access_token =#{accessToken} where app_id=#{appId} ")
	int updateAccessToken(@Param("accessToken") String accessToken, @Param("appId") String appId);
}
package com.learn.utils;

import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

@Component
public class BaseRedisService {

	@Autowired
	private StringRedisTemplate stringRedisTemplate;

	public void setString(String key, Object data, Long timeout) {
		if (data instanceof String) {
			String value = (String) data;
			stringRedisTemplate.opsForValue().set(key, value);
		}
		if (timeout != null) {
			stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
		}
	}

	public Object getString(String key) {
		return stringRedisTemplate.opsForValue().get(key);
	}

	public void delKey(String key) {
		stringRedisTemplate.delete(key);
	}

}
package com.learn.utils;

public interface Constants {
	// 响应请求成功
	String HTTP_RES_CODE_200_VALUE = "success";
	// 系统错误
	String HTTP_RES_CODE_500_VALUE = "fial";
	// 响应请求成功code
	Integer HTTP_RES_CODE_200 = 200;
	// 系统错误
	Integer HTTP_RES_CODE_500 = 500;
	// 未关联QQ账号
	Integer HTTP_RES_CODE_201 = 201;
	// 发送邮件
	String MSG_EMAIL = "email";
	// 会员token
	String TOKEN_MEMBER = "TOKEN_MEMBER";
	// 支付token
	String TOKEN_PAY = "TOKEN_pay";
	// 支付成功
	String PAY_SUCCESS = "success";
	// 支付白
	String PAY_FAIL = "fail";
	// 用户有效期 90天
	Long TOKEN_MEMBER_TIME = (long) (60 * 60 * 24 * 90);
	int COOKIE_TOKEN_MEMBER_TIME = (60 * 60 * 24 * 90);
	Long PAY_TOKEN_MEMBER_TIME = (long) (60 * 15);
	// cookie 会员 totoken 名称
	String COOKIE_MEMBER_TOKEN = "cookie_member_token";

}
package com.learn.utils;

import java.util.UUID;

import org.springframework.web.bind.annotation.RequestMapping;

public class TokenUtils {

	@RequestMapping("/getToken")
	public static String getAccessToken() {
		return UUID.randomUUID().toString().replace("-", "");
	}
	
	public static void main(String[] args) {
		String a = getAccessToken();
		System.out.println(a);
	}

}
package com.learn;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@MapperScan("com.learn.mapper")
@SpringBootApplication
public class AcessTokenA {

	public static void main(String[] args) {
		SpringApplication.run(AcessTokenA.class, args);
	}

}
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver


########################################################
###Redis (RedisConfiguration)
########################################################
spring.redis.database=0
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=123456
spring.redis.pool.max-idle=8
spring.redis.pool.min-idle=0
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
spring.redis.timeout=5000
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
  	<groupId>com.learn</groupId>
  	<artifactId>accesstoken_projectb</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.0.RELEASE</version>
	</parent>
	<dependencies>

		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.1.1</version>
		</dependency>
		<!-- mysql 依赖 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>

		<!-- SpringBoot 对lombok 支持 -->
		<dependency>
		    <groupId>org.projectlombok</groupId>
		    <artifactId>lombok</artifactId>
		</dependency>

		<!-- SpringBoot web 核心组件 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
		</dependency>
		<!-- SpringBoot 外部tomcat支持 -->
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-jasper</artifactId>
		</dependency>

		<!-- springboot-log4j -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-log4j</artifactId>
			<version>1.3.8.RELEASE</version>
		</dependency>
		<!-- springboot-aop 技术 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
		<dependency>
			<groupId>commons-lang</groupId>
			<artifactId>commons-lang</artifactId>
			<version>2.6</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
		</dependency>
		<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.47</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
		</dependency>
		<dependency>
			<groupId>taglibs</groupId>
			<artifactId>standard</artifactId>
			<version>1.1.2</version>
		</dependency>
	</dependencies>

</project>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明) Chatgpt学习资料项目资料 用于实战ChatgptAI技术 源代码 供参考(代码+使用说明)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值