微信扫码登录
微信登录-网站应用
一、准备工作
1、注册
2、邮箱激活
3、完善开发者资料
4、开发者资质认证
准备营业执照,1-2个工作日审批、300元
5、创建网站应用
提交审核,7个工作日审批
6、熟悉微信登录流程
获取access_token时序图
二、微信登录开发流程
1.说明
微信扫码登录主要包含下面几个步骤:
1.获取二维码并设置回调地址
2.通过回调接口得到授权临时票据code
3.通过临时票据调用微信接口获取access_token、openid(用户微信唯一标识)
4.通过access_token、openid调用微信接口获取用户信息
5.完成注册登录业务
下面贴出的一段配置信息是微信登录认证的资料,需要注意的时候,这里服务端口和重定向端口都是8160
# 服务端口
server.port=8160
# 微信开放平台 appid
wx.open.app_id=微信开放平台 appid
# 微信开放平台 appsecret
wx.open.app_secret=微信开放平台 appsecret
# 微信开放平台 重定向url
wx.open.redirect_url=http://localhost:8160/api/ucenter/wx/callback
构建工具类
@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;
}
}
2.获取二维码
微信登录二维码的生成是通过请求一个固定的接口地址并传入指定的参数
我这里项目的请求地址是:http://localhost:8160/api/ucenter/wx/login
显示效果如下图所示:
生成微信登录二维码
@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(ResultCode.ERROR, e.getMessage());
}
// 防止csrf攻击(跨站请求伪造攻击)
//String state = UUID.randomUUID().toString().replaceAll("-", "");//一般情况下会使用一个随机数
String state = "imhelen";//为了让大家能够使用我搭建的外网的微信回调跳转服务器,这里填写你在ngrok的前置域名
log.info("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;
}
回调接口
@GetMapping("callback")
public String callback(String code, String state){
//得到授权临时票据code
log.info(code);
log.info(state);
//从redis中将state获取出来,和当前传入的state作比较
//如果一致则放行,如果不一致则抛出异常:非法访问
//向认证服务器发送请求换取access_token
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,
ConstantPropertiesUtil.WX_OPEN_APP_ID,
ConstantPropertiesUtil.WX_OPEN_APP_SECRET,
code);
String result = null;
try {
result = HttpClientUtils.get(accessTokenUrl);
log.info("accessToken=============" + result);
} catch (Exception e) {
throw new GuliException(ResultCode.ERROR, "获取access_token失败");
}
//解析json字符串
Gson gson = new Gson();
HashMap map = gson.fromJson(result, HashMap.class);
log.info("map:{}",map);
String accessToken = (String)map.get("access_token");
String openid = (String)map.get("openid");
//查询数据库当前用用户是否曾经使用过微信登录
UcenterMember member = ucenterMemberService.getByOpenid(openid);
if(member == null){
log.info("新用户注册");
//访问微信的资源服务器,获取用户信息
String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
"?access_token=%s" +
"&openid=%s";
String userInfoUrl = String.format(baseUserInfoUrl, accessToken, openid);
String resultUserInfo = null;
try {
resultUserInfo = HttpClientUtils.get(userInfoUrl);
log.info("resultUserInfo==========" + resultUserInfo);
} catch (Exception e) {
throw new GuliException(20001, "获取用户信息失败");
}
//解析json
HashMap<String, Object> mapUserInfo = gson.fromJson(resultUserInfo, HashMap.class);
String nickname = (String)mapUserInfo.get("nickname");
String headimgurl = (String)mapUserInfo.get("headimgurl");
//向数据库中插入一条记录
member = new UcenterMember();
member.setNickname(nickname);
member.setOpenid(openid);
member.setAvatar(headimgurl);
ucenterMemberService.save(member);
}
// 生成jwt
String token = JwtUtils.getJwtToken(member.getId(),member.getNickname(),member.getAvatar());
//存入cookie
//CookieUtils.setCookie(request, response, "guli_jwt_token", token);
//因为端口号不同存在问题,cookie不能跨域,所以这里使用url重写
return "redirect:http://localhost:3000?token=" + token;
}
3.返回数据格式
#accessToken
{"access_token":"53_0f5YsGQuvBhsuYZVS2pU2JC8g_5NJdjBAAlMFQVEZarC36B0xxL3mxngIX55KVpQFPGQYXKBqrBmEzpf2O3HQ2VhYnzIR3FBMHscQOWWmOI","expires_in":7200,"refresh_token":"53_7g2glkoAojtkSJHaAwUUM6I6PVl1Wdry4BXHrOUcAGtGzlwD59uG3EDrrTlSqRC9sUb2bf1T8rby0Q8E7aI47YgfwmRVk7pYzv8uDFcDZbc","openid":"o3_SC5-0Nza23111I20To","scope":"snsapi_login","unionid":"oWgGz1Gq4HvXRH-HjxJ4Rc3x05BA"}
#用户信息
{"openid":"o3_SC5-0Nza23111I20To","nickname":"🍩","sex":0,"language":"","city":"","province":"","country":"","headimgurl":"https:\/\/thirdwx.qlogo.cn\/mmopen\/vi_32\/m25hunfY8OtxicHj0jasKSLwZ2AzRcU4micSrygIHbInVekRqC7krWztIZAWCibmFq9qKhibW4lvBbCLafibHBoTC8A\/132","privilege":[],"unionid":"oWgGz1Gq4HvXRH-HjxJ4Rc3x05BA"}