微信登录
OAuth2
OAuth主要角色
现代微服务中系统微服务化以及应用的形态和设备类型增多,不能用传统的登录方式
核心的技术不是用户名和密码,而是token,由AuthServer颁发token,用户使用token进行登录
前期准备
这个是微信登录的开发平台
微信开放平台:https://open.weixin.qq.com
授权的流程
https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=e547653f995d8f402704d5cb2945177dc8aa4e7e&lang=zh_CN ,
我们现在需要将二维码内嵌到网页当中
生成二维码
getLoginParam() {
return request({
url: `${api_name}/getLoginParam`,
method: `get`,
})
}
这是发送请求的js
后端写一个接口来接收这个
@GetMapping("getLoginParam")
@ResponseBody
public Result getQrConnect() {
try {
Map<String, Object> map = new HashMap<>();
map.put("appid", ConstantWxPropertiesUtils.WX_OPEN_APP_ID);
map.put("scope", "snsapi_login");
String wxOpenRedirectUrl = ConstantWxPropertiesUtils.WX_OPEN_REDIRECT_URL;
wxOpenRedirectUrl = URLEncoder.encode(wxOpenRedirectUrl, "utf-8");
map.put("redirectUri", wxOpenRedirectUrl);
map.put("state", System.currentTimeMillis() + "");
return Result.ok(map);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
Controller用到的帮助类
@Component
public class ConstantWxPropertiesUtils 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;
@Value("${yygh.baseUrl}")
private String yyghBaseUrl;
public static String WX_OPEN_APP_ID;
public static String WX_OPEN_APP_SECRET;
public static String WX_OPEN_REDIRECT_URL;
public static String YYGH_BASE_URL;
@Override
public void afterPropertiesSet() throws Exception {
WX_OPEN_APP_ID = appId;
WX_OPEN_APP_SECRET = appSecret;
WX_OPEN_REDIRECT_URL = redirectUrl;
YYGH_BASE_URL = yyghBaseUrl;
}
}
properties配置类
# 微信登录的配置文件
wx.open.app_id=wxed9954c01bb89b47
wx.open.app_secret=a7482517235173ddb4083788de60b90e
wx.open.redirect_url=http://localhost:8160/api/ucenter/wx/callback
yygh.baseUrl=http://localhost:3000
vue前端
weixinLogin() {
this.dialogAtrr.showLoginType = 'weixin'
weixinApi.getLoginParam().then(response => {
var obj = new WxLogin({
self_redirect:true,
id: 'weixinLogin',
appid: response.data.appid,
scope: response.data.scope,
redirect_uri: response.data.redirectUri,
state: response.data.state,
style: 'black',
href: ''
})
})
},
这样就可以生成二维码了
虽然说现在有二维码了但是现在在手机上登录之后也不好进行校验,会报错404
登录逻辑
第一步从回调url中取出code
wx.open.redirect_url=http://localhost:8160/api/ucenter/wx/callback
通过配置文件当中的这个描述我们可以知道现在在扫描完二维码之后会回调这个方法,于是我们来写一下这个方法.
我们的需求是扫描以后获得用户昵称,还有openid存放到数据库中
在官方文档当中我们知道了,回调函数是通过Get请求提交的,同时还附带了2个参数code和state
code=CODE&state=STATE
于是我们的方法就写成这样
//0.微信扫描后回调的方法
@GetMapping("callback")
public String callback(String code, String state) throws UnsupportedEncodingException {
}
第二步通过code获取access_token
第三步通过access_token获取登录人的信息
下面我们来在业务层中实现这些
@GetMapping("callback")
public String callback(String code, String state) throws UnsupportedEncodingException {
log.info("code为" + code);
log.info("state为" + state);
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={appid}&secret={secret}&code={code}&grant_type=authorization_code";
String accesstokenInfo = restTemplate.getForObject(url, String.class, ConstantWxPropertiesUtils.WX_OPEN_APP_ID, ConstantWxPropertiesUtils.WX_OPEN_APP_SECRET, code);
log.info("accesstokenInfo为" + accesstokenInfo);
JSONObject jsonObject = JSONObject.parseObject(accesstokenInfo);
String accessToken = jsonObject.getString("access_token");
String openid = jsonObject.getString("openid");
UserInfo userInfo = userInfoService.selectByOpenid(openid);
if (userInfo == null) {
String userInfoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token={access_token}&openid={openid}";
String userVoInfo = restTemplate.getForObject(userInfoUrl, String.class, accessToken, openid);
log.info(userVoInfo);
JSONObject userVoObject = JSONObject.parseObject(userVoInfo);
String nickname = userVoObject.getString("nickname");
nickname = new String(nickname.getBytes("windows-1256"),"UTF-8");
log.info("nickname为" + nickname);
String headimgurl = jsonObject.getString("headimgurl");
userInfo = new UserInfo();
userInfo.setNickName(nickname);
userInfo.setOpenid(openid);
userInfo.setStatus(1);
userInfoService.save(userInfo);
}
Map<String, Object> map = new HashMap<>();
String name = userInfo.getName();
if (StringUtils.isEmpty(name)) {
name = userInfo.getNickName();
}
if (StringUtils.isEmpty(name)) {
name = userInfo.getPhone();
}
map.put("name", name);
if (StringUtils.isEmpty(userInfo.getPhone())) {
map.put("openid", userInfo.getOpenid());
} else {
map.put("openid", "");
}
String token = JwtHelper.createToken(userInfo.getId(), name);
map.put("token", token);
return "redirect:" + ConstantWxPropertiesUtils.YYGH_BASE_URL + "/weixin/callback?token=" + map.get("token") + "&openid=" + map.get("openid") + "&name=" + URLEncoder.encode((String) map.get("name"), "utf-8");
}
userInfoservice也做了一定的调整
@Override
public UserInfo selectByOpenid(String openid) {
QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("openid", openid);
UserInfo userInfo = baseMapper.selectOne(queryWrapper);
return userInfo;
}
在进行登录接口保存用户时也需要根据openid来判断用户是微信用户还是邮箱用户如果是微信登录那么也是需要绑定微信的.
UserInfo userInfo = null;
if (!StringUtils.isEmpty(loginVo.getOpenid())) {
userInfo = this.selectByOpenid(loginVo.getOpenid());
if (null != userInfo) {
userInfo.setPhone(loginVo.getPhone());
this.updateById(userInfo);
} else {
throw new YyghException(ResultCodeEnum.DATA_ERROR);
}
}
这样就实现了我们的功能
|