赞
踩
第三章 Redis队列
目录
1.BookListQuery 实现ParamSecret接口
接口如果是被我们前端项目调用,一般都是加了各种鉴权的,比如Spring Sercurity+token安全机制,shiro等框架都可以控制接口访问权限。但是如果接口是提供给外部调用,我们需要和第三方做个鉴权,比如常见的开放平台OpenApi。
应用的唯一标识,是用来标识客户端身份的。
appKey 和 appSecret 是一对出现的账号,同一个 appId 可以对应多个 appKey+appSecret,这样平台就可以分配你不一样的权限,比如 appKey1 + appSecect1 只有只读权限 但是 appKey2+appSecret2 有读写权限…,这样你就可以把对应的权限放给不同的开发者,其中权限的配置都是直接跟appKey 做关联的,appKey 也需要添加数据库检索,方便快速查找。而在实际开发中,都是简单的直接将 appId = appKey,然后外加一个appSecret就够了。
作用:返回需要参加 加密的参数名和参数值 的健值对。
鉴权处理器 会对请求参数进行拦截,判断入参是否实现ParamSecret接口,如果实现ParamSecret接口 则需要对方法进行鉴权处理。
- /**
- * 参数加密类定义
- *
- * @author yangyanping
- * @date 2023-03-14
- */
- public interface ParamSecret {
-
- String Timestamp_Key = "timestamp";
-
- Map<String, String> getParamSecretMap();
-
- /**
- * 时间戳
- */
- default Long getTimestamp() {
- Map<String, String> map = getParamSecretMap();
-
- if (map == null || map.isEmpty()) {
- return null;
- }
-
- String timestamp = map.get(Timestamp_Key);
- if (timestamp == null || Objects.equals(timestamp, "")) {
- return null;
- }
-
- return Long.valueOf(timestamp);
- }
- }
- /**
- * 鉴权处理器类定义
- * @author yangyanping
- * @date 2022-08-26
- */
- public abstract class AbstractSercurityCommandHandler<P extends Command, R> extends AbstractCommandHandler<P, R, Protocol> {
- @Resource
- protected AppSecretConfig appSecretConfig;
-
- /**
- * 验证权限
- */
- @Override
- protected void checkHeader(P param, Protocol header) {
- if (!(param instanceof ParamSecret)) {
- return;
- }
-
- //appId 和 appSecret 配置信息
- Map<String, String> appMaps = appSecretConfig.getAppMaps();
- if (MapUtil.isEmpty(appMaps)) {
- throw new BizException("appSecretConfig is not empty !");
- }
-
- String appId = header.getAppId();
- String appToken = header.getAppToken();
- String appSecret = appMaps.get(appId);
-
- if (StringUtils.isBlank(appSecret)) {
- throw new BizException("appSecret is not empty !");
- }
-
- ParamSecret paramSecret = (ParamSecret) param;
- Long timestamp = paramSecret.getTimestamp();
-
- if (timestamp != null) {
- Calendar calendar = Calendar.getInstance();
- calendar.setTimeInMillis(timestamp);
-
- //timestamp 和当前时间 不能大于5分钟
- if (DateUtil.between(calendar.getTime(), new Date(), DateUnit.MINUTE, false) > 5) {
- throw new BizException("timestamp is error !");
- }
- }
-
- Map<String, String> paramMap = paramSecret.getParamSecretMap();
- TreeMap<String, String> secretMap = Maps.newTreeMap();
-
- if (CollectionUtil.isNotEmpty(paramMap)) {
- paramMap.entrySet().stream().forEach(e -> {
- secretMap.put(e.getKey(), e.getValue());
- });
- }
-
- secretMap.put("appId", appId);
- secretMap.put("appSecret", appSecret);
-
- StringBuilder sb = new StringBuilder();
-
- for (Map.Entry<String, String> entry : secretMap.entrySet()) {
- sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
- }
-
- String str = sb.toString();
-
- if (str.length() > 0) {
- str = str.substring(0, sb.length() - 1);
- }
-
- String md5 = SecureUtil.md5(str);
-
- //参数加密后 和 appToken比较是否一致
- if (!Objects.equals(md5, appToken)) {
- throw new BizException("appToken is error !");
- }
- }
- }
- /**
- * 基础Controller
- *
- * @author yangyanping
- * @date 2022-09-01
- */
- @RestController
- public class BaseController {
- /**
- * 获取请求Protocol
- */
- protected Protocol getProtocol() {
- ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
- HttpServletRequest request = attributes.getRequest();
-
- String appId = request.getHeader("appId");
- if (StringUtils.isBlank(appId)) {
- appId = request.getParameter("appId");
- }
-
- String appToken = request.getHeader("appToken");
- if (StringUtils.isBlank(appToken)) {
- appToken = request.getParameter("appToken");
- }
-
- Protocol protocol = new Protocol();
- protocol.setAppId(appId);
- protocol.setAppToken(appToken);
-
- return protocol;
- }
- }
- /**
- * 书籍列表查询
- *
- * @author yangyanping
- * @date 2023-03-16
- */
- @Getter
- @Setter
- @ToString
- public class BookListQuery extends Query implements ParamSecret {
-
- @NotNull(message = "timestamp不能为空")
- @Positive(message = "需要合法的timestamp")
- private Long timestamp;
-
- @Override
- public Map<String, String> getParamSecretMap() {
- Map<String, String> map = Maps.newHashMapWithExpectedSize(1);
- map.put("timestamp", Long.toString(timestamp));
-
- return map;
- }
- }
代码如下(示例):
- /**
- * 开发平台api
- *
- * @author yangyanping
- * @date 2023-03-15
- */
- @RestController
- @RequestMapping("/openApi/v1/book/")
- public class ShareController extends BaseController {
-
- @Resource
- private BookShareImpl bookShareImpl;
-
- /**
- * 查询书籍列表
- */
- @RequestMapping("getBookList")
- public ApiResult<List<BookInfoDTO>> getBookList() {
- BookListQuery query = new BookListQuery();
-
- return new RpcExecutor<BookListQuery, List<BookInfoDTO>>().invokeMethod(
- "shareApi",
- "BookShareController.getBookList",
- (param) -> bookShareImpl.getBookList(getProtocol(), param),
- query);
- }
- }
代码如下(示例):
- /**
- * 开发平台-书籍服务
- *
- * @author yangyanping
- * @date 2023-03-15
- */
- @Service
- @Validated
- public class BookShareImpl {
- @Resource
- private BookListQueryHandler bookListQueryHandler;
-
-
- /**
- * 查询书籍列表
- */
- public ApiResult<List<BookInfoDTO>> getBookList(Protocol protocol, @Valid BookListQuery query){
- return bookListQueryHandler.doHandler(protocol,query);
- }
- }
- /**
- * 查询书籍列表
- *
- * @author yangyanping
- * @date 2023-03-16
- */
- @Component
- public class BookListQueryHandler extends AbstractSercurityCommandHandler<BookListQuery, List<BookInfoDTO>> {
- @Override
- protected void checkParam(BookListQuery param) {
-
- }
-
- @Override
- protected List<BookInfoDTO> doBusiness(BookListQuery param, Protocol header) {
- return null;
- }
-
- @Override
- protected String getUmp() {
- return "share.book.getBookList";
- }
- }
- /**
- * 开发平台单元测试
- *
- * @author yangyanping
- * @date 2023-03-16
- */
- public class ShareBookImplTest extends BaseTest {
-
- @Resource
- private BookShareImpl shareBookImpl;
-
- @Test
- public void getBookInfo() {
- Protocol protocol = new Protocol();
- protocol.setAppId("100000");
- Long timestamp = System.currentTimeMillis();
- String appToken = SecureUtil.md5("appId=100001&appSecret=12bc71&bookId=1×tamp=" + timestamp);
-
- protocol.setAppToken(appToken);
-
- BookListQuery query = new BookListQuery();
- query.setTimestamp(timestamp);
- ApiResult apiResult = shareBookImpl.getBookList(protocol, query);
-
- System.out.println(JSON.toJSONString(apiResult));
- }
- }
如果接口是提供给外部调用,肯定是不需要登录的,需要在自身的权限控制中放开该接口的token校验,这样就会造成安全问题,我们一般采取拦截器的方式,和第三方做个鉴权。鉴权采用固定参数同样存在安全问题,容易被抓包获取到。所以一般带入动态的时间戳来鉴权,常用的鉴权逻辑是:两边各存一 份appId和appSecret。向服务器请求授权时,在请求Header中传递appId 和 appToken。
参考:开放api接口平台:appid、appkey、appsecret_51CTO博客_appid appkey appsecret
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。