对于网页的微信扫码登录,首要前提是企业申请的微信开放平台。 微信开放平台地址:open.weixin.qq.com/
微信扫码登录官方文档:developers.weixin.qq.com/doc/oplatfo…
本来想着自己写一个微信登录,然后记录的,哎,微信是又出钱,又需要企业才能认证的,只能拿我在公司做的微信登录做个记录了,以免后期再做,方便使用
效果:
思路: 扫码后通过redirect_uri 跳转到Controller里写业务,拿到openid后。先去这个表查询这个openID有没有关联用户
1、有关联了,就直接签发我们的token,然后带token重定向到我们的应用页完成登录,
2、没有关联,带openID重定向到绑定页, 让用户输入平台账号密码点击绑定,系统验证账号密码通过后将openid保存到用户表,签发我们的token,然后带token重定向到我们的应用页完成登录, 用户下一次扫码登陆就走1的步骤了
1.前端
/**
* 第三方登录,微信登录点击事件
*/
wechatHandleClick(){
const redirect_uri = encodeURIComponent('https://申请微信登录时候的域名/auth/login.html');
const url = 'https://open.weixin.qq.com/connect/qrconnect?appid=' + this.appid + '&redirect_uri=' + redirect_uri + '&response_type=code&scope=snsapi_login&state=pc#wechat_redirect';
location.href = url
}
- 后台 redirect_uri 后缀.html是不是很有迷惑性,一看就以为是前端,其实是后台😄
controller:
import com.sinotrans.iot.auth.server.service.biz.service.LoginService;
import com.sinotrans.iot.auth.server.service.biz.wecharOauth2.AccessTokenResult;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import java.awt.*;
@RestController
public class LoginController {
@Value("${wx.pc.login.success.redirect_uri}")
private String successPage;
@Value("${wx.pc.login.bind.redirect_uri}")
private String bindPage;//绑定页面
@Autowired
private LoginService loginService;
/**
* 请求access_token
* @param code
* @return 成功则跳转应用主页,否则跳转绑定用户页面
*/
@GetMapping("/login.html")
public ModelAndView loginHtml(String code,String state) throws Exception {
AccessTokenResult tokenResult = loginService.pc(code,state);
if (StringUtils.isNotBlank(tokenResult.getAccess_token())) {
String spage = successPage + "token=" + tokenResult.getAccess_token();
return new ModelAndView(new RedirectView(spage));
}
String bpage = bindPage + "openid=" + tokenResult.getOpenid();
return new ModelAndView(new RedirectView(bpage));
}
/**
* 绑定用户微信openid
* @param userCode 登录账号
* @param password 登录密码
* @return token
*/
@GetMapping(value = "/bindUser",produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public String bindUser(String openid,String userCode,String password) throws Exception {
return loginService.bindUser(openid,userCode,password);
}
}
service:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sinotrans.framework.mybatis.support.FilterCondition;
import com.sinotrans.iot.auth.server.service.biz.controller.TokenController;
import com.sinotrans.iot.auth.server.service.biz.model.SysUserModel;
import com.sinotrans.iot.auth.server.service.biz.service.LoginService;
import com.sinotrans.iot.auth.server.service.biz.service.SysUserManager;
import com.sinotrans.iot.auth.server.service.biz.vo.TokenPasswordVo;
import com.sinotrans.iot.auth.server.service.biz.wecharOauth2.AccessTokenResult;
import com.sinotrans.iot.auth.server.service.util.HttpUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service
public class LoginServiceImpl implements LoginService {
private Logger log = LoggerFactory.getLogger(getClass());
@Value("${wx.open.appid}")
private String wxOpenAppid;
@Value("${wx.open.appsecret}")
private String wxOpenAppsecret;
@Autowired
private SysUserManager sysUserManager;
@Autowired
private TokenController tokenController;
/**
* pc 端登录
*
* @param code 微信扫码后返回的code
* 拿到openid后。先去用户表查询这个openID有没有关联用户,
* 1、有关联了,就直接签发我们的token,然后带token重定向到我们的应用页完成登录,
* 2、没有关联,带openID重定向到绑定页(新增的页面), 让用户输入平台账号密码点击绑定,
* 然后系统验证账号密码通过将自动关联用户的openid,然后签发我们的token,
* 然后带token重定向到我们的应用页完成登录,用户下一次扫码登陆就走1的步骤了
*/
@Override
public AccessTokenResult pc(String code,String state) throws Exception {
String tokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token";
String param = "appid=" + wxOpenAppid + "&secret=" + wxOpenAppsecret + "&code=" + code
+ "&state=" + state + "&grant_type=authorization_code";
String strResult = HttpUtils.sendGet(tokenUrl, param);
AccessTokenResult resule = JSONObject.parseObject(strResult, AccessTokenResult.class);
if (StringUtils.isBlank(resule.getOpenid())) {
throw new RuntimeException(JSON.toJSONString(resule));
}
List<FilterCondition> conditionList = new ArrayList<>();
conditionList.add(new FilterCondition("openId", resule.getOpenid(), "="));
List<SysUserModel> user = sysUserManager.getUser(conditionList);
if (!user.isEmpty()) {
resule.setAccess_token(getToken(user.get(0)));
}else {
resule.setAccess_token("");
}
return resule;
}
/**
* 绑定用户
* @param userCode
* @param password
* @return
* @throws Exception
*/
@Override
public String bindUser(String openid,String userCode, String password) throws Exception {
String token = "";
List<FilterCondition> conditionList = new ArrayList<>();
conditionList.add(new FilterCondition("code", userCode, "="));
conditionList.add(new FilterCondition("pwd", password, "="));
List<SysUserModel> user = sysUserManager.getUser(conditionList);
if(!user.isEmpty()){
SysUserModel sysUserModel = user.get(0);
sysUserModel.setOpenId(openid);
sysUserManager.save(sysUserModel);//绑定openid
token = getToken(sysUserModel);//获取token
}
return token;
}
/**
* 从现有方法中获取token
* @param user
* @return
* @throws Exception
*/
private String getToken(SysUserModel user) throws Exception {
String token;
TokenPasswordVo tokenPasswordVo = new TokenPasswordVo();
tokenPasswordVo.setUsername(user.getCode());
tokenPasswordVo.setPassword(user.getPwd());
Map<String, Object> o = (Map<String, Object>)tokenController.v2login(null, tokenPasswordVo);
token = o.get("access_token").toString();
return token;
}
}
model:
public class AccessTokenResult {
// 接口调用凭证
private String access_token;
// access_token接口调用凭证超时时间,单位(秒)
private Long expires_in;
// 用户刷新access_token
private String refresh_token;
// 授权用户唯一标识
private String openid;
// 用户授权的作用域,使用逗号(,)分隔
private String scope;
// 用户授权的作用域,使用逗号(,)分隔
private String unionid;
// 错误码
private Long errcode;
// 错误消息
private String errmsg;
public String getAccess_token() {
return access_token;
}
public void setAccess_token(String access_token) {
this.access_token = access_token;
}
public Long getExpires_in() {
return expires_in;
}
public void setExpires_in(Long expires_in) {
this.expires_in = expires_in;
}
public String getRefresh_token() {
return refresh_token;
}
public void setRefresh_token(String refresh_token) {
this.refresh_token = refresh_token;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public String getUnionid() {
return unionid;
}
public void setUnionid(String unionid) {
this.unionid = unionid;
}
public Long getErrcode() {
return errcode;
}
public void setErrcode(Long errcode) {
this.errcode = errcode;
}
public String getErrmsg() {
return errmsg;
}
public void setErrmsg(String errmsg) {
this.errmsg = errmsg;
}
}