通过appId和appSecret生成accessToken访问api后端接口(接口授权)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过appId和appSecret生成accessToken访问api后端接口(接口授权)相关的知识,希望对你有一定的参考价值。

  1. 功能点

(1)申请获取appId和appSecret

(2)通过appId和appSecret获取accessToken

(3)mysql和redis进行key-value键值对存储

(4)对需要的接口url进行请求地址拦截

效果图:

(1)正确的accessToken且没有过期(获取后端数据)

通过appId和appSecret生成accessToken访问api后端接口(接口授权)_redis

(2)没有填入的accessToken(提示为空)

通过appId和appSecret生成accessToken访问api后端接口(接口授权)_appId_02

(3)accessToken正确但是已经过了TTL时间(过期),redis进行数据删除。

(4)默认是7200s

通过appId和appSecret生成accessToken访问api后端接口(接口授权)_SpringBoot_03

具体实现:

(1) 数据库

通过appId和appSecret生成accessToken访问api后端接口(接口授权)_SpringBoot_04

/*
Navicat Premium Data Transfer

Source Server : 本机
Source Server Type : MySQL
Source Server Version : 50738 (5.7.38-log)
Source Host : localhost:3306
Source Schema : coupon

Target Server Type : MySQL
Target Server Version : 50738 (5.7.38-log)
File Encoding : 65001

Date: 20/03/2023 10:55:04
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for m_app
-- ----------------------------
DROP TABLE IF EXISTS `m_app`;
CREATE TABLE `m_app` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 主键id,
`app_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 所申请应用名称,
`app_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT appId,
`app_secret` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 秘钥,
`is_flag` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 是否可用代表状态,
`access_token` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 令牌,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 38 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of m_app
-- ----------------------------
INSERT INTO `m_app` VALUES (20, 测试, 4xMTxI9p, d1124a2acbc0074900e436505ccfa049f9abaa2f, 0, 95818324c9e34ff89d07f99772dee10c);
INSERT INTO `m_app` VALUES (21, 积分优惠券测试, W0wrjQ6C, 1bf37c433b627f9d2451d250916c866ee341de86, 0, 24e2fa591d9447c9b2745d84cda3c8da);
INSERT INTO `m_app` VALUES (22, test, WS09OO3s, cfe6b77fb68f213dedeb59f51b1a4808bfdec394, 0, a48e4e98314d4fa390ab31d49dd7757e);
INSERT INTO `m_app` VALUES (23, 123, tw9PDmTA, d35dd6ad355d6d85e8e9bb1682ea99952ca14d85, 0, 0c4734a413b54c86b8864e3f7aa87d60);
INSERT INTO `m_app` VALUES (24, 12345, OchZx9yE, 18e5892a2b92c016078eac4d41b25c765588073477c25b42e2c6b75859960e59, 0, 11c624f4e5394f948542673c393a820b);
INSERT INTO `m_app` VALUES (25, 1, qiKL6XKK, 9676fd0a49381cbdb3e86ad6547e58328594e349, 0, 7f15452ea17e4b9b9bddb3750952f787);
INSERT INTO `m_app` VALUES (26, 2121, hrGqnHC7, 4b684d90573d4bbeb4e2c504b219b3429eacc4d4, 0, 5682c190b8b54fd4904c8bf007e8708e);
INSERT INTO `m_app` VALUES (27, ghd, O5ELmevg, 0b45dead09e8195053eaeb8b7451a953572bfed5, 0, 3e80b55f73a54a3895be87fe3d334eb3);

SET FOREIGN_KEY_CHECKS = 1;

(2)实体类:

package com.coupon_test.coupon.model;

import com.baomidou.mybatisplus.annotation.TableName;

@TableName("m_app")
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 + "]";


(3)mapper层:

package com.coupon_test.coupon.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.coupon_test.coupon.model.AppEntity;

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


public interface AppMapper extends BaseMapper<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 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);



(4)工具类,main方法可以进行测试。

package com.coupon_test.coupon.utils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.UUID;

/**
* @Title: AppUtils
* @Description: 随机产生唯一的app_key和app_secret
* @date 2023-02-15
*/
public class AppUtils
//生成 app_secret 密钥
private final static String SERVER_NAME = "积分优惠券系统";
private final static String[] chars = new String[]"a", "b", "c", "d", "e", "f",
"g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
"t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I",
"J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
"W", "X", "Y", "Z";

/**
* @Description: <p>
* 短8位UUID思想其实借鉴微博短域名的生成方式,但是其重复概率过高,而且每次生成4个,需要随即选取一个。
* 本算法利用62个可打印字符,通过随机生成32位UUID,由于UUID都为十六进制,所以将UUID分成8组,每4个为一组,然后通过模62操作,结果作为索引取出字符,
* 这样重复率大大降低。
* 经测试,在生成一千万个数据也没有出现重复,完全满足大部分需求。
* </p>
* @date 2023-02-15
*/
public static String getAppId()
StringBuffer shortBuffer = new StringBuffer();
String uuid = UUID.randomUUID().toString().replace("-", "");
for (int i = 0; i < 8; i++)
String str = uuid.substring(i * 4, i * 4 + 4);
int x = Integer.parseInt(str, 16);
shortBuffer.append(chars[x % 0x3E]);

return shortBuffer.toString();



/**
* <p>
* 通过appId和内置关键词生成APP Secret
* </P>
*
* @date 2023-02-15
*/
public static String getAppSecret(String appId)
try
String[] array = new String[]appId, SERVER_NAME;
StringBuffer sb = new StringBuffer();
// 字符串排序
Arrays.sort(array);
for (int i = 0; i < array.length; i++)
sb.append(array[i]);

String str = sb.toString();
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(str.getBytes());
byte[] digest = md.digest();

StringBuffer hexstr = new StringBuffer();
String shaHex = "";
for (int i = 0; i < digest.length; i++)
shaHex = Integer.toHexString(digest[i] & 0xFF);
if (shaHex.length() < 2)
hexstr.append(0);

hexstr.append(shaHex);

return hexstr.toString();
catch (NoSuchAlgorithmException e)
e.printStackTrace();
throw new RuntimeException();



public static void main(String[] args)
String appId = getAppId();
System.out.println(appId);
System.out.println(getAppSecret(appId));




(5)请求拦截

package com.coupon_test.coupon.config;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.coupon_test.coupon.service.BaseApiService;
import com.coupon_test.coupon.service.BaseRedisService;
import org.apache.commons.lang3.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;

//验证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("---------------视图渲染之后的操作-------------------------");


// 返回错误提示
public void resultError(String errorMsg, HttpServletResponse httpServletResponse) throws IOException
PrintWriter printWriter = httpServletResponse.getWriter();
printWriter.write(new JSONObject().toJSONString(setResultError(errorMsg)));


(6)配置类

package com.coupon_test.coupon.config;

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)
// /openApi 下的所有接⼝
registry.addInterceptor(accessTokenInterceptor).addPathPatterns("/api/*");
registry.addInterceptor(accessTokenInterceptor).addPathPatterns("/api/*/*");
registry.addInterceptor(accessTokenInterceptor).addPathPatterns("/api/*/*/*");
registry.addInterceptor(accessTokenInterceptor).addPathPatterns("/api/*/*/*/*");
registry.addInterceptor(accessTokenInterceptor).addPathPatterns("/api/*/*/*/*/*");


;
;

package com.coupon_test.coupon.service;
import com.coupon_test.coupon.common.constants.Constants;
import com.coupon_test.coupon.model.system.ResponseBase;
import org.springframework.stereotype.Component;


@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.coupon_test.coupon.service;


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;

/**
* TODO 添加/更新
* @param key 键
* @param data 值
* @param timeout 时间(秒)
* @return void
*/
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);



/**
* TODO 读取
* @param key 键
* @return java.lang.Object
*/
public Object getString(String key)
return stringRedisTemplate.opsForValue().get(key);


/**
* TODO 删除
* @param key 键
* @return void
*/
public void delKey(String key)
stringRedisTemplate.delete(key);

(7)controller层

package com.coupon_test.coupon.controller;

import com.coupon_test.coupon.dao.AppMapper;
import com.coupon_test.coupon.model.AppEntity;
import com.coupon_test.coupon.model.system.ResponseBase;
import com.coupon_test.coupon.service.BaseApiService;
import com.coupon_test.coupon.service.BaseRedisService;
import com.coupon_test.coupon.utils.AppUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.alibaba.fastjson.JSONObject;

import java.util.*;


// 创建获取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(value = "/getAccessToken", method = RequestMethod.GET)
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);



// 使用appId+appSecret 生成AccessToke
@RequestMapping(value = "/insertApp", method = RequestMethod.POST)
public ResponseBase save(AppEntity appEntity)
String appId = AppUtils.getAppId();
String appSecret = AppUtils.getAppSecret(appId);
AppEntity appEntity1 = new AppEntity();
appEntity1.setIsFlag(0);
appEntity1.setAppName(appEntity.getAppName());
appEntity1.setAppId(appId);
appEntity1.setAppSecret(appSecret);
appEntity1.setAccessToken("");
int insert = appMapper.insert(appEntity1);
if (insert == 1)
return setResultSuccessData("appId是" + appId + "-----" + "appSecret是" + appSecret);
else
return setResultError("添加失败");



private String newAccessToken(String appId)
// 使用appid+appsecret 生成对应的AccessToken 保存两个小时
// String accessToken = TokenUtils.getAccessToken();
String accessToken = UUID.randomUUID().toString().replace("-", "");
// 保证在同一个事物redis 事物中
// 生成最新的token key为accessToken value 为 appid
baseRedisService.setString(accessToken, appId, timeToken);
// 表中保存当前accessToken
appMapper.updateAccessToken(accessToken, appId);
return accessToken;



(8)依赖

<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
</dependency>
<!--字符串操作-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.2.1</version>
</dependency>

先使用controller获取appId及appSecret, 接着申请accessToken并带至请求中,获取返回数据。

(二)、将请求的http协议改为https

效果图:postman测试

通过appId和appSecret生成accessToken访问api后端接口(接口授权)_SpringBoot_05

function getToken(){
    $appid=‘你的appid‘;
    $appsecret=‘你的appsecret‘;
    $file = file_get_contents("./access_token.json",true);
    $result = json_decode($file,true);
    echo time() - $result[‘expires‘].":";
    if (time() > $result[‘expires‘]){
       $data = array();
        $data[‘access_token‘] = getNewToken($appid,$appsecret);
        $data[‘expires‘]=time()+7000;
        $jsonStr =  json_encode($data);
        $fp = fopen("./access_token.json", "w");
        fwrite($fp, $jsonStr);
        fclose($fp);
        return $data[‘access_token‘];
    }else{
        return $result[‘access_token‘];
    }
}
function getNewToken($appid,$appsecret){
    $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$appid}&secret={$appsecret}";
    $access_token_Arr =  https_request($url);
    return $access_token_Arr[‘access_token‘];
}
function https_request ($url){
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        $out = curl_exec($ch);
        curl_close($ch);
        return  json_decode($out,true);
}

 

以上是关于通过appId和appSecret生成accessToken访问api后端接口(接口授权)的主要内容,如果未能解决你的问题,请参考以下文章

getaccesstoken方法

小程序参数二维码生成

小程序 二维码 实战

微信公众号自定义菜单创建

小程序获取access_token

接入微信分享接口步骤