最近做了一个关于商城的项目,B端选用若依的开源框架,C端还是vue前后端分离。其中C端主要是小程序的形式,所以想着来总结一下对接微信小程序登录中Java部分遇到的坑即代码分享!
废话不多说,直接上代码!
1、controller 层代码
入参我这边是直接使用request来接收,大家可以改成自己的DTO来接收入参
@GetMapping("/letsLogin")
@ApiOperation("微信授权登录")
public R letsLogin(HttpServletRequest request){
return remoteMallUserService.letsLogin(request);
}
2、service 层代码
其中涉及到几个基本配置参数,我这边单独列出,大家可以放入配置文件进行引用,也可以直接作为常量引用;
同时可以参考微信公众平台提供的开发文档(服务端):接口调用凭证 | 微信开放文档
// APP_ID 小程序注册时由微信提供
private final static String APP_ID = "....";
// APP_SERCRET 小程序注册时由微信提供
private final static String APP_SERCRET = ".....";
// 请求地址这边用了多个所以我们按照调用的顺序来说明
// 首先是这个TOKENURL 用于获取微信用户的授权认证 来拿到“accessToken”
private final static String TOKENURL = "https://api.weixin.qq.com/cgi-bin/token";
// 第二是用于请求获取用户手机号的地址(结尾的“access_token”需要拼接上TOKENURL返回的参数)
private final static String PHONENURL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=";
// 最后才是登录url了
private final static String URL = "https://api.weixin.qq.com/sns/jscode2session";
由于我自身的业务中存在通过手机号验证登录这个功能模块,所以我在入参的时候会做一个是否存在手机号的判断,不存在时才通过微信去请求;存在时直接调用登录URL即可;
这边还需要简单解释一下入参(红色为必传参数):
1、userPhone 用户手机号;
2、code:前端通过getPhoneNum() 获得的code;
3、loginCode:前端通过wx.login() 获得的code;
@Autowired
private RestTemplate restTemplate;
@Override
public R letsLogin(HttpServletRequest request){
// 先拿到前端通过wx.login()拿到的code -- 5分钟过期哦
String loginCode = request.getParameter("loginCode");
if(StringUtils.isBlank(loginCode)){
return R.fail(101,"loginCode不可以为空!");
}
String userPhone = request.getParameter("userPhone");
// 如果手机号为空则判断是否有code(该code是前端通过获取手机号方法拿到的)
if (StringUtils.isBlank(userPhone)){
String code = request.getParameter("code");
if(StringUtils.isBlank(code)){
return R.fail(101,"code为空,无法获取手机号!");
}
// 获取手机号 -- 这边需要定义一个传参的map
Map<String, String> params1 = new HashMap<>();
// 此处为固定值不需要修改
params1.put("grant_type", "client_credential");
// APP_ID和APP_SERCRET需要根据实际情况进行传参
params1.put("appid", APP_ID);
params1.put("secret", APP_SERCRET);
// 调用TOKENURL获取授权凭证access_token
// 我这边用的是Hultool的HTTP请求工具 -- 之后会附上maven依赖
String resultToken = HttpUtil.sendGet(TOKENURL, params1);
// fastjson 框架自带 大家可以选择自己的JSON转换工具
JSONObject tokenJson = JSONObject.parseObject(resultToken);
String accessToken = (String)tokenJson.get("access_token");
// 使用前端code获取手机号码(accessToken一定要以get的方式请求)其他参数为json格式
String url1 = PHONEURL + accessToken;
Map<String, String> paramMap = new HashMap<>();
paramMap.put("code", code);
HttpHeaders headers = new HttpHeaders();
HttpEntity<Map<String, String>> httpEntity = new HttpEntity<>(paramMap, headers);
ResponseEntity<Object> response = restTemplate.postForEntity(url1, httpEntity, Object.class);
Object body = response.getBody();
Map<String, Object> map = new ObjectMapper().convertValue(body, Map.class);
Object phoneInfo = map.get("phone_info");
Map<String, Object> map1 = new ObjectMapper().convertValue(phoneInfo, Map.class);
// 以上都是response参数的处理 最终拿到userPhone 可以进行下一步 微信登陆了
userPhone = (String)map1.get("phoneNumber");
}
// 构建 一个map传登录的参数
Map<String, String> params = new HashMap<>();
params.put("appid", APP_ID);
params.put("secret", APP_SERCRET);
// 这里的code是前端通过wx.login()获取到的
params.put("js_code", loginCode);
// 固定参数
params.put("grant_type", "authorization_code");
String resultJson = HttpUtil.sendGet(URL, params);
JSONObject json = JSONObject.parseObject(resultJson);
if (!json.containsKey("errcode")) {
// 拿到openid(用户在该小程序的唯一用户标识)
String openId = (String)json.get("openid");
// 之后的可以根据自己的业务进行处理 比如:新增用户...
return R.ok("","登录成功");
} else {
return R.fail(101,"登录失败!");
}
}
3、util 层代码。添加一个RestTemplateConfig配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setReadTimeout(5000);//ms
factory.setConnectTimeout(15000);//ms
return factory;
}
}
4、Hultool的依赖
<!-- hutool 工具包-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.2.4</version>
</dependency>
最后说一下在开发中踩到的坑,就是获取手机号返回的response这边进行了两次的map转换才拿到最终的手机号,但是官方文档中的返回格式是一个JSON对象,如果是一个JSON串的话就不需要这么麻烦了,所以各位大神可以在开发的时候优化一下这部分!