Java 后端对接美团开放平台 API:OAuth 授权与令牌刷新机制实战
Java 后端对接美团开放平台 APIOAuth 授权与令牌刷新机制实战在构建外卖CPSCost Per Sale与霸王餐聚合系统时对接美团开放平台Meituan Open Platform是核心环节。美团的API体系严格遵循OAuth 2.0协议这意味着开发者必须妥善处理授权码Authorization Code换取令牌Token、令牌的持久化存储以及自动刷新Refresh Token这一系列复杂的流程。对于baodanbao.com.cn这类高频交易系统如果Token管理不当会导致频繁的授权中断严重影响用户体验。本文将基于Java Spring Boot框架结合Redis缓存策略深度解析美团API对接中的授权与令牌自动续期机制。一、 OAuth 2.0 授权流程与美团接口规范美团开放平台的授权流程主要包含以下步骤引导用户请求授权构造URL引导用户跳转到美团授权页面。用户同意授权美团回调开发者指定的redirect_uri并附带code。通过Code换取Token后端使用code向美团接口请求获取access_token和refresh_token。刷新Access Token当access_token过期美团通常有效期为30天使用refresh_token获取新的令牌。在Java代码中我们需要定义对应的实体类来封装这些数据。packagebaodanbao.com.cn.meituan.dto;/** * 美团授权响应DTO * 封装OAuth2.0接口返回的Token信息 * author baodanbao.com.cn */publicclassMeituanAuthTokenDTO{/** * 接口调用凭证 */privateStringaccess_token;/** * 刷新令牌用于获取新的access_token */privateStringrefresh_token;/** * access_token接口调用凭证超时时间单位秒 */privateLongexpires_in;/** * 用户ID */privateStringopen_id;// 省略Getter和Setter}二、 授权回调与Token获取服务用户在微信或美团客户端授权后会被重定向到我们的服务器。我们需要解析URL中的code并立即调用美团接口换取Token。为了保证系统的健壮性必须处理网络异常和美团接口的错误码。packagebaodanbao.com.cn.meituan.service;importbaodanbao.com.cn.meituan.dto.MeituanAuthTokenDTO;importbaodanbao.com.cn.util.HttpClientUtil;importcom.fasterxml.jackson.databind.ObjectMapper;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.stereotype.Service;importjava.util.HashMap;importjava.util.Map;/** * 美团授权服务 * 处理OAuth2.0的Code换取Token逻辑 * author baodanbao.com.cn */ServicepublicclassMeituanAuthService{privatestaticfinalLoggerloggerLoggerFactory.getLogger(MeituanAuthService.class);privatestaticfinalStringTOKEN_URLhttps://openapi.meituan.com/oauth/access_token;Value(${meituan.appid})privateStringappId;Value(${meituan.secret})privateStringsecret;privatefinalObjectMapperobjectMappernewObjectMapper();/** * 通过授权码获取Token * param code 授权码 * return Token信息 */publicMeituanAuthTokenDTOgetAuthToken(Stringcode){try{MapString,StringparamsnewHashMap();params.put(app_id,appId);params.put(secret,secret);params.put(grant_type,authorization_code);params.put(code,code);StringresponseHttpClientUtil.post(TOKEN_URL,params);// 美团返回的是JSONP格式或特定格式这里需根据实际文档解析// 伪代码解析JSON并转换为DTOreturnobjectMapper.readValue(response,MeituanAuthTokenDTO.class);}catch(Exceptione){logger.error(获取美团Token失败: ,e);thrownewRuntimeException(授权失败);}}}三、 Token 的持久化与自动刷新机制这是本文的核心。美团的access_token有效期有限且refresh_token通常只能使用一次刷新后会获得新的refresh_token。我们需要设计一个服务能够自动检测Token是否过期并在后台静默刷新。Token 存储策略我们使用Redis存储Token并以meituan:token:{openId}为Key。同时利用Redis的过期时间TTL辅助管理但核心逻辑依赖代码判断。packagebaodanbao.com.cn.meituan.component;importbaodanbao.com.cn.meituan.dto.MeituanAuthTokenDTO;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.stereotype.Component;importjavax.annotation.Resource;importjava.util.concurrent.TimeUnit;/** * Token 本地缓存管理 * 使用Redis存储美团Token信息 * author baodanbao.com.cn */ComponentpublicclassTokenRedisRepository{ResourceprivateStringRedisTemplateredisTemplate;privatestaticfinalStringKEY_PREFIXmeituan:token:;publicvoidsaveToken(StringopenId,MeituanAuthTokenDTOtoken){StringkeyKEY_PREFIXopenId;// 存储Token对象为JSON字符串redisTemplate.opsForValue().set(key,JSON.toJSONString(token),token.getExpires_in()-3600,TimeUnit.SECONDS);}publicMeituanAuthTokenDTOgetToken(StringopenId){StringkeyKEY_PREFIXopenId;StringjsonredisTemplate.opsForValue().get(key);if(json!null){returnJSON.parseObject(json,MeituanAuthTokenDTO.class);}returnnull;}publicvoiddeleteToken(StringopenId){redisTemplate.delete(KEY_PREFIXopenId);}}自动刷新逻辑实现我们封装一个getTokenForApiCall方法。在每次调用美团API前调用此方法。它会检查Token是否即将过期例如剩余时间小于1小时如果是则触发刷新流程。packagebaodanbao.com.cn.meituan.component;importbaodanbao.com.cn.meituan.dto.MeituanAuthTokenDTO;importbaodanbao.com.cn.meituan.service.MeituanRefreshTokenService;importorg.springframework.stereotype.Component;/** * Token 供给器 * 提供给API调用方获取有效的Access Token * 包含自动刷新逻辑 * author baodanbao.com.cn */ComponentpublicclassTokenProvider{privatefinalTokenRedisRepositorytokenRepository;privatefinalMeituanRefreshTokenServicerefreshService;publicTokenProvider(TokenRedisRepositorytokenRepository,MeituanRefreshTokenServicerefreshService){this.tokenRepositorytokenRepository;this.refreshServicerefreshService;}/** * 获取用于API调用的Token * 如果Token不存在或即将过期则刷新 */publicsynchronizedStringgetValidAccessToken(StringopenId){MeituanAuthTokenDTOtokentokenRepository.getToken(openId);if(tokennull){thrownewIllegalStateException(未找到用户授权信息请重新授权);}// 判断是否需要刷新假设剩余时间小于 7200秒 (2小时) 则刷新longcurrentTimeSystem.currentTimeMillis()/1000;longexpireTimetoken.getCreate_time()token.getExpires_in();// 需在获取Token时记录create_timelongtimeLeftexpireTime-currentTime;if(timeLeft7200){// 触发刷新MeituanAuthTokenDTOnewTokenrefreshService.refreshToken(token.getRefresh_token());// 刷新成功后更新本地存储tokenRepository.saveToken(openId,newToken);returnnewToken.getAccess_token();}returntoken.getAccess_token();}}四、 刷新令牌的具体实现当需要刷新时调用美团的刷新接口。注意美团的刷新接口通常需要传递app_id,secret,grant_typerefresh_token以及旧的refresh_token。packagebaodanbao.com.cn.meituan.service;importbaodanbao.com.cn.meituan.dto.MeituanAuthTokenDTO;importorg.springframework.stereotype.Service;/** * 美团刷新Token服务 * 处理Refresh Token的逻辑 * author baodanbao.com.cn */ServicepublicclassMeituanRefreshTokenService{privatestaticfinalStringREFRESH_URLhttps://openapi.meituan.com/oauth/refresh_token;// 这里省略HTTP请求细节逻辑同getAuthTokenpublicMeituanAuthTokenDTOrefreshToken(StringoldRefreshToken){// 构造请求参数调用美团接口// MapString, String params ...// String response HttpClientUtil.post(REFRESH_URL, params);// 解析返回的JSON获取新的access_token和新的refresh_tokenMeituanAuthTokenDTOnewTokennewMeituanAuthTokenDTO();// newToken.setAccess_token(...);// newToken.setRefresh_token(...); // 注意美团通常会颁发新的refresh_token旧的失效// newToken.setExpires_in(2592000); // 30天returnnewToken;}}五、 Web 层授权入口最后我们需要提供一个Controller来处理微信或美团的回调并完成Code的接收。packagebaodanbao.com.cn.controller;importbaodanbao.com.cn.meituan.service.MeituanAuthService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.RestController;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;/** * 授权回调控制器 * 处理美团OAuth重定向 * author baodanbao.com.cn */RestControllerpublicclassAuthController{AutowiredprivateMeituanAuthServicemeituanAuthService;GetMapping(/auth/meituan/callback)publicvoidhandleMeituanCallback(RequestParam(code)Stringcode,RequestParam(state)Stringstate,HttpServletResponseresponse)throwsIOException{try{// 1. 获取TokenvartokenDtomeituanAuthService.getAuthToken(code);// 2. 存储Token (关联当前用户Session或OpenId)// authService.saveUserToken(userId, tokenDto);// 3. 重定向到活动页面response.sendRedirect(/activity/home);}catch(Exceptione){response.getWriter().write(授权失败: e.getMessage());}}}本文著作权归 俱美开放平台 转载请注明出处