引言:除了常规的用户名密码登录,微信登录现在也变得常用起来,整理流程如下。
1. 引入依赖
<!--httpclient-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!--commons-io-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<!--gson-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
2. 修改配置文件
为了获取对应的信息,需要先到链接: 微信平台申请,申请后会生成一个id和secret:
修改配置文件,id和secret和上面申请的需要一致:
# 微信开放平台 appid
wx:
open:
app_id: wx1292a69b2cd3c5b7
# 微信开放平台 appsecret
app_secret: dd776adcd6bb79a9a05a30e3a67ed7b3
# 微信开放平台重定向url,即扫码登录后回调的后端api,中间的ip地址是内网穿透的
redirect_url: http://17bbf738.r10.cpolar.top/buildBaseFrame/api/v1/user/wechat/login/wx
在控制类中使用@Value注解获取配置文件中的值:
3. 具体流程
完整流程如下:
3.1 用户发起微信登录请求
这里我们给出一种发起请求获取二维码的方法:
- jsp页面
@RequestMapping("/doLogin")
public ModelAndView doLogin(){
ModelAndView modelAndView = new ModelAndView("admin/wechatLogin");
log.info("前往登入页面");
return modelAndView; // 跟页面模板名字一样才能跳转
}
编写一个admin/wechatLogin.jsp文件用于后续展示扫码登录的二维码,其中redirect_url是扫码后的回调api:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js">
</script>
</head>
<body>
<div id="weixin"></div>
<script>
var obj = new WxLogin({
self_redirect:true,
id:"weixin", // 要与你在那里生成二维码的div id 一致
appid: "xxxxxxxxxxxxx", // 你申请的appid
redirect_uri: encodeURIComponent("http://17bbf738.r10.cpolar.top/buildBaseFrame/api/v1/user/wechat/login/wx"),// 回调url,ip地址一定是内网穿透过的,然后你扫码后,微信官方返回一些参数(code,state)
scope: "snsapi_login,snsapi_userinfo",// 你的权限
state: "STATE",// 微信官方接口为了防止跨域攻击要加的参数 可以默认就这个
});
</script>
</body>
</html>
3.2 验证微信登录操作
在jsp页面申请二维码后,微信平台会发出验证信息。
需要先在微信平台上输入重定向地址,这个地址是指向我们的验证信息接口的:
其中URL为用户向平台申请登录二维码后,微信发送验证消息的地址。
注意还需要在下面的地方修改域名:
验证的代码为:
@ResponseBody
@GetMapping(value = "/check") // 等价于@RequestMapping(value="/check",method=RequestMethod.GET)
public String checkWxMsg(HttpServletRequest request) {
log.info("进入微信校验的接口了");
/**
* 微信加密签名
*/
String signature = request.getParameter("signature");
/**
* 随机字符串
*/
String echostr = request.getParameter("echostr");
/**
* 时间戳
*/
String timestamp = request.getParameter("timestamp");
/**
* 随机数
*/
String nonce = request.getParameter("nonce");
String[] str={timestamp,nonce, tmpToken};
//将token、timestamp、nonce三个参数进行字典序排序
Arrays.sort(str);
StringBuffer sb = new StringBuffer();
//将三个参数字符串拼接成一个字符串进行sha1加密
for (String param:str) {
sb.append(param);
}
//获取到加密过后的字符串
String encryptStr = DigestUtils.sha1Hex(sb.toString());
// String encryptStr = EncryptionUtil.encrypt("SHA1", sb.toString());
//判断加密后的字符串是否与微信的签名一致
if(signature.equalsIgnoreCase(encryptStr)){
log.info("签名一致可以通过");
return echostr;
}
log.error("这不是微信发来的消息!!");
return null;
}
微信收到验证通过的信息后会在上一步的jsp中显示登录的二维码。
3.3 扫码登录
扫码后调用api:
@RequestMapping(value = "/login/wx",name = "进入微信登录方法")
public CommonResult weixinlogin(String code, String state, HttpServletResponse response) {
// 查看获取到的code
log.info("进入微信登入方法,code为:{}, state为:{}",code,state);
// 根据code获取access_token和openId
// 微信请求地址标准格式url
String getAccessTokenAndOpenIdUrl="https://api.weixin.qq.com/sns/oauth2/access_token?appid="+ appId +"&secret="+appSecret+"&code="+code+"&grant_type=authorization_code";
// 向微信发送请求获取access_token与openid
// restTemplate.getForObject:向远程api发送get请求,String.class表示设置response的类型为String
Map map = JSON.parseObject(restTemplate.getForObject(getAccessTokenAndOpenIdUrl, String.class), Map.class);
// 开始遍历参数
map.forEach(
(key,value)->{
log.info("根据code请求微信获取的值,key:{}, value:{}",key,value);
}
);
// 获取accessToken
String access_token = map.get("access_token").toString();
// 获取openid
String openid = map.get("openid").toString();
// 再次请求微信平台,获取用户信息
String getUserInfoUrl="https://api.weixin.qq.com/sns/userinfo?access_token="+access_token+"&openid="+openid+"&lang=zh_CN";
// 发起请求
Map userInfoMap = JSON.parseObject(restTemplate.getForObject(getUserInfoUrl, String.class), Map.class);
userInfoMap.forEach((key, value)-> {
log.info("微信获取用户信息,key为:{}, value为:{}",key,value);
});
WechatLoginDto wechatLoginDto = wechatLoginService.findByOpenId(openid);
String name = (String) userInfoMap.get("nickname");
UserInfoDto userInfoDto = service.getUserByname(name);
// 首次登录则注册,否则就直接登录
if(wechatLoginDto == null){
wechatLoginDto = new WechatLoginDto();
if(userInfoDto==null){
userInfoDto = new UserInfoDto();
userInfoDto.setNickname((String) userInfoMap.get("nickname"));
userInfoDto.setAvatarUrl((String) userInfoMap.get("headimgurl"));
userInfoDto.setIntroduction("");
// 值为 1 时是男性,值为 2 时是女性,值为 0 时是未知
int gender = (int) userInfoMap.get("sex");
switch (gender){
case 1:
userInfoDto.setGender(GenderEnum.MALE);
break;
case 2:
userInfoDto.setGender(GenderEnum.FEMALE);
break;
default:
userInfoDto.setGender(GenderEnum.UNKNOWN);
}
Long userId = service.insertOneUser(userInfoDto);
userInfoDto.setId(userId);
}
wechatLoginDto.setUserId(userInfoDto.getId());
wechatLoginDto.setOpenId((String) userInfoMap.get("openid"));
wechatLoginDto = wechatLoginService.insertOneWechatLogin(wechatLoginDto);
}
String jwt = jwtGenerator.createJwt(wechatLoginDto.getUserId());
response.addHeader("Authorization", jwt);
UserInfoVo userInfoVo = userApiConverter.toUserInfoVo(userInfoDto);
// 把当前用户信息返回
return CommonResult.success(userInfoVo);
}
4. 注意事项
- 本地测试(8080)时需要内网穿透,这里使用cpolar工具:
打开cmd,在cmd中输入:
cpolar http 8080
用内网穿透的地址替换掉localhost:8080