技术点:HttpClient(模拟浏览器的请求),Json转换工具
第一步:密钥ID的配置
wx.open.app_id=wxed9954c01bb89b47
wx.open.app_secret=a7482517235173ddb4083788de60b90e
wx.open.redirect_url=http://localhost:8160/api/ucenter/wx/callback
#wx.open.redirect_url=http://guli.shop/api/ucenter/wx/callback
第二步:整合一个工具类方便读取配置文件属性值,InitializingBean这里很细节,利用初始化将数据加载
package com.guli.ucenter.util;
@Component
//@PropertySource("classpath:application.properties")
public class ConstantPropertiesUtil implements InitializingBean {
@Value("${wx.open.app_id}")
private String appId;
@Value("${wx.open.app_secret}")
private String appSecret;
@Value("${wx.open.redirect_url}")
private String redirectUrl;
public static String WX_OPEN_APP_ID;
public static String WX_OPEN_APP_SECRET;
public static String WX_OPEN_REDIRECT_URL;
@Override
public void afterPropertiesSet() throws Exception {
WX_OPEN_APP_ID = appId;
WX_OPEN_APP_SECRET = appSecret;
WX_OPEN_REDIRECT_URL = redirectUrl;
}
}
第三步:controller层
package com.guli.ucenter.controller.api;
@CrossOrigin
@Controller//注意这里没有配置 @RestController
@RequestMapping("/api/ucenter/wx")
public class WxApiController {
@GetMapping("login")
public String genQrConnect(HttpSession session) {
// 微信开放平台授权baseUrl
String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
"?appid=%s" +
"&redirect_uri=%s" +
"&response_type=code" +
"&scope=snsapi_login" +
"&state=%s" +
"#wechat_redirect";
// 回调地址
String redirectUrl = ConstantPropertiesUtil.WX_OPEN_REDIRECT_URL; //获取业务服务器重定向地址
try {
redirectUrl = URLEncoder.encode(redirectUrl, "UTF-8"); //url编码
} catch (UnsupportedEncodingException e) {
throw new GuliException(20001, e.getMessage());
}
// 防止csrf攻击(跨站请求伪造攻击)
//String state = UUID.randomUUID().toString().replaceAll("-", "");//一般情况下会使用一个随机数
String state = "imhelen";//为了让大家能够使用我搭建的外网的微信回调跳转服务器,这里填写你在ngrok的前置域名
System.out.println("state = " + state);
// 采用redis等进行缓存state 使用sessionId为key 30分钟后过期,可配置
//键:"wechar-open-state-" + httpServletRequest.getSession().getId()
//值:satte
//过期时间:30分钟
//生成qrcodeUrl
String qrcodeUrl = String.format(
baseUrl,
ConstantPropertiesUtil.WX_OPEN_APP_ID,
redirectUrl,
state);
return "redirect:" + qrcodeUrl;
}
}
第四步:如何生成微信二维码?
请求微信的固定地址,然后向地址后面拼接参数
扫描完后,通过请求地址进行回调(需要按照指定规则进行请求),将端口改为8150,创建一个接口callback做回调得到扫码人的信息
第五步:callback回调接口的实现
1.之前第一步扫描二维码callback接口得到的code值——>2.通过code和ID密钥信息去请求微信提供的固定地址得到token访问凭证+openid微信的标识(通过HttpClients访问)——>3.最后拿token和openid再次请求一次微信地址https://api.weixin.qq.com/sns/userinfo
得到用户信息
添加的依赖:
<!--httpclient-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!--commons-io-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<!--gson-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
通过code,密钥和id得到的用户url是一个String字符串,里面包含token和openid——>我们利用gson将字符串转为map,然后从map中得到指定的数据
第六步:最后将信息提取判断数据库中是否有重复数据,然后插入(先根据openid在数据库中查询,如果已经有了说明之前就扫了一次)
/**
* 2.获取扫描人的信息(扫码将人物信息存入数据库),添加数据,返回code类似与手机验证码,随机唯一值->请求微信固定地址
* 获得access-token:访问凭证
* openid:每个微信唯一的标识
* 然后再拿这俩个信息请求获取我们的微信信息->获取扫码人的信息
*/
@GetMapping("callback")
public String callback(String code,String state){
System.out.println("code="+code);
try {
//1.拿code值请求wx固定地址,得到access_token和openid
String baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +
"?appid=%s" +
"&secret=%s" +
"&code=%s" +
"&grant_type=authorization_code";
String accessTokenUrl = String.format(baseAccessTokenUrl,
ConstantWxUtils.WX_OPEN_APP_ID, //id和密钥注入
ConstantWxUtils.WX_OPEN_APP_SECRET,
code);
//2.使用httpclient发送请求,得到返回结果
String accessTokenInfo = HttpClientUtils.get(accessTokenUrl);
//3.将accessTokenInfo中的信息取出来,(将里面字符串转为map集合,根据map里面key获取对应值)
Gson gson = new Gson();
HashMap mapAcessToken = gson.fromJson(accessTokenInfo, HashMap.class);
String access_token = (String) mapAcessToken.get("access_token");
String openid = (String) mapAcessToken.get("openid");
//4.根据openid对数据库进行查询
UcenterMember member=memberService.getOpenIdMember(openid);
if(member==null){
//5.拿着access_token访问凭证和openid去请求微信提供的固定地址,获取扫描人的信息
String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
"?access_token=%s" +
"&openid=%s";
String userInfoUrl = String.format(
baseUserInfoUrl,
access_token,
openid
);
//6.发送请求得到用户结果
String userInfo = HttpClientUtils.get(userInfoUrl);
System.out.println("userInfo:"+userInfo);
//7.捕获userInfo中人物信息:名字和头像
HashMap userInfoMap = gson.fromJson(userInfo, HashMap.class);
String nickname = (String) userInfoMap.get("nickname");
String headimgurl = (String) userInfoMap.get("headimgurl");
//8.说明表中没有相同数据
member = new UcenterMember();
member.setOpenid(openid);
member.setNickname(nickname);
member.setAvatar(headimgurl);
memberService.save(member);
}
//9.使用jwt根据member的id和name生成token字符串->解决跨域问题,之后的验证直接判断请求头token就好
String jwtToken = JwtUtils.getJwtToken(member.getId(), member.getNickname());
return "redirect:http://localhost:3000?token="+jwtToken;
} catch (Exception e) {
throw new GuliException(20001,"登陆失败");
}
}
第七步:我们利用memberId和memberName生成token,利用token进行访问(不用cookie是因为无法实现跨域,前端给cookie值设置了domain,所以前台拿不到后台的数据,因为有两个域名一个前台的服务一个后台的服务)
利用token我们再将token解析就能找到我们要的数据了
第八步:我们从header中将token取出来然后解析放到cookie中,因为前端我们这里有cookie拦截器
根据this.$route.query.token取得token值,根据token获得用户信息回显到前端
/**
* 3.根据token获取用户信息
*/
@GetMapping("getMemberInfo")
public R getMemberInfo(HttpServletRequest request){
//getMemberIdByJwtToken根据请求头中的token得到用户信息(token得到userid->取出信息)
String memberId = JwtUtils.getMemberIdByJwtToken(request);
//根据用户id查询用户信息
UcenterMember member = memberService.getById(memberId);
return R.ok().data("userInfo",member);
}