准备开发环境
添加应用
1、注册微信开放平台
https://open.weixin.qq.com/
2、添加应用
进入网站应用,添加应用
3、添加应用需要指定一个外网域名作为微信回调域名
审核通过后,生成app密钥。
最终获取appID和AppSecret
内网穿透
我们的开发环境在局域网,微信回调指向一个公网域名。
如何让微信回调请求至我们的开发计算机上呢?
可以使用内网穿透技术,什么是内网穿透?
内网穿透简单来说就是将内网外网通过隧道打通,让内网的数据让外网可以获取。比如常用的办公室软件等,一般在办公室或家里,通过拨号上网,这样办公软件只有在本地的局域网之内才能访问,那么问题来了,如果是手机上,或者公司外地的办公人员,如何访问到办公软件呢?这就需要内网穿透工具了。开启隧道之后,网穿透工具会分配一个专属域名/端口,办公软件就已经在公网上了,在外地的办公人员可以在任何地方愉快的访问办公软件了~~
1、在内网穿透服务器上开通隧道,配置外网域名,配置穿透内网的端口即本地电脑上的端口。
这里我们配置认证服务端口,最终实现通过外网域名访问本地认证服务。
2、在本地电脑上安装内网穿透的工具,工具上配置内网穿透服务器隧道token。
市面上做内网穿透的商家很多,需要时可以查阅资料了解下。
接入微信登录
接入分析
根据OAuth2协议授权码流程,结合本项目自身特点,分析接入微信扫码登录的流程如下:
本项目认证服务需要做哪些事?
1、需要定义接口接收微信下发的授权码。
2、收到授权码调用微信接口申请令牌。
3、申请到令牌调用微信获取用户信息
4、获取用户信息成功将其写入本项目用户中心数据库。
5、最后重定向到浏览器自动登录。
定义接口
参考接口规范中“请求获取授权码” 定义接收微信下发的授权码接口,
定义WxLoginController类,如下:
@Slf4j
@Controller
public class WxLoginController {
@RequestMapping("/wxLogin")
public String wxLogin(String code, String state) throws IOException {
log.debug("微信扫码回调,code:{},state:{}",code,state);
//请求微信申请令牌,拿到令牌查询用户信息,将用户信息写入本项目数据库
XcUser xcUser = new XcUser();
//暂时硬编写,目的是调试环境
xcUser.setUsername("t1");
if(xcUser==null){
return "redirect:http://www.51xuecheng.cn/error.html";
}
String username = xcUser.getUsername();
return "redirect:http://www.51xuecheng.cn/sign.html?username="+username+"&authType=wx";
}
}
定义微信认证的service
package com.xuecheng.ucenter.service.impl;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* @author Mr.M
* @version 1.0
* @description 微信扫码认证
* @date 2022/9/29 12:12
*/
@Slf4j
@Service("wx_authservice")
public class WxAuthServiceImpl implements AuthService {
@Autowired
XcUserMapper xcUserMapper;
@Override
public XcUserExt execute(AuthParamsDto authParamsDto) {
//账号
String username = authParamsDto.getUsername();
XcUser user = xcUserMapper.selectOne(new LambdaQueryWrapper<XcUser>().eq(XcUser::getUsername, username));
if(user==null){
//返回空表示用户不存在
throw new RuntimeException("账号不存在");
}
XcUserExt xcUserExt = new XcUserExt();
BeanUtils.copyProperties(user,xcUserExt);
return xcUserExt;
}
}
接口环境测试
接口定义好下边进行测试下,主要目的是测试接口调度的环境。
1、启动内网穿透工具
2、在/wxLogin接口中打断点
3、打开前端微信扫码页面
点击微信图标打开二维码
用户扫码,确认授权
此时正常进入 /wxLogin 方法,最后跳转到http://www.51xuecheng.cn/sign.html?username=t1&authType=wx。
申请令牌
接下来请求微信申请令牌。
1、使用restTemplate请求微信,配置RestTemplate bean
在启动类配置restTemplate
@Bean
RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate(new OkHttp3ClientHttpRequestFactory());
return restTemplate;
}
定义与微信认证的service接口:
/**
* @author Mr.M
* @version 1.0
* @description 微信认证接口
* @date 2023/2/21 22:15
*/
public interface WxAuthService {
public XcUser wxAuth(String code);
}
继续在WxAuthServiceImpl类写WxAuthService 接口的实现,在其中定义申请令牌的私有方法并由wxAuth方法去调用:
@Slf4j
@Service("wx_authservice")
public class WxAuthServiceImpl implements AuthService, WxAuthService {
@Autowired
XcUserMapper xcUserMapper;
@Autowired
RestTemplate restTemplate;
@Value("${weixin.appid}")
String appid;
@Value("${weixin.secret}")
String secret;
public XcUser wxAuth(String code){
//收到code调用微信接口申请access_token
Map<String, String> access_token_map = getAccess_token(code);
if(access_token_map==null){
return null;
}
//获取用户信息
//添加用户到数据库
XcUser xcUser = null;
return xcUser;
}
/**
* 申请访问令牌,响应示例
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE",
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
*/
private Map<String,String> getAccess_token(String code) {
String wxUrl_template = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
//请求微信地址
String wxUrl = String.format(wxUrl_template, appid, secret, code);
log.info("调用微信接口申请access_token, url:{}", wxUrl);
ResponseEntity<String> exchange = restTemplate.exchange(wxUrl, HttpMethod.POST, null, String.class);
String result = exchange.getBody();
log.info("调用微信接口申请access_token: 返回值:{}", result);
Map<String,String> resultMap = JSON.parseObject(result, Map.class);
return resultMap;
}
....
下边在controller中调用wxAuth接口:
@Slf4j
@Controller
public class WxLoginController {
@Autowired
WxAuthService wxAuthService;
@RequestMapping("/wxLogin")
public String wxLogin(String code, String state) throws IOException {
log.debug("微信扫码回调,code:{},state:{}",code,state);
//请求微信申请令牌,拿到令牌查询用户信息,将用户信息写入本项目数据库
XcUser xcUser = wxAuthService.wxAuth(code);
if(xcUser==null){
return "redirect:http://www.51xuecheng.cn/error.html";
}
String username = xcUser.getUsername();
return "redirect:http://www.51xuecheng.cn/sign.html?username="+username+"&authType=wx";
}
}
测试获取用户信息
1、在wxAuthService中获取用户信息处打断点
2、进入http://www.51xuecheng.cn/wxsign.html
3、手机扫码并授权,观察是否成功申请到令牌
获取用户信息
在WxAuthServiceImpl类中定义获取用户信息方法:
/**获取用户信息,示例如下:
{
"openid":"OPENID",
"nickname":"NICKNAME",
"sex":1,
"province":"PROVINCE",
"city":"CITY",
"country":"COUNTRY",
"headimgurl": "https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
"privilege":[
"PRIVILEGE1",
"PRIVILEGE2"
],
"unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
*/
private Map<String,String> getUserinfo(String access_token,String openid) {
String wxUrl_template = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s";
//请求微信地址
String wxUrl = String.format(wxUrl_template, access_token,openid);
log.info("调用微信接口申请access_token, url:{}", wxUrl);
ResponseEntity<String> exchange = restTemplate.exchange(wxUrl, HttpMethod.POST, null, String.class);
//防止乱码进行转码
String result = new String(exchange.getBody().getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);
log.info("调用微信接口申请access_token: 返回值:{}", result);
Map<String,String> resultMap = JSON.parseObject(result, Map.class);
return resultMap;
}
调用获取用户信息。
public XcUser wxAuth(String code){
//收到code调用微信接口申请access_token
Map<String, String> access_token_map = getAccess_token(code);
if(access_token_map==null){
return null;
}
System.out.println(access_token_map);
String openid = access_token_map.get("openid");
String access_token = access_token_map.get("access_token");
//拿access_token查询用户信息
Map<String, String> userinfo = getUserinfo(access_token, openid);
if(userinfo==null){
return null;
}
//添加用户到数据库
XcUser xcUser = null;
return xcUser;
}
测试获取用户信息
1、在获取用户信息处打断点
2、进入http://www.51xuecheng.cn/wxsign.html
3、手机扫码授权
到这里微信扫码登录基本实现了