尚医通 (十八)微信登录

news2024/9/23 13:16:08

目录

  • 一、生成微信登录二维码
    • 1、准备工作
    • 2、后端开发service_user
    • 3、前端显示登录二维码
    • 4、二维码出现不了进行调试
  • 二、开发微信扫描回调
    • 1、准备工作
    • 2、后台开发
    • 3、前台开发
  • 三、分析代码
  • 四、bug

一、生成微信登录二维码

1、准备工作

1、注册
2、邮箱激活
3、完善开发者资料
4、开发者资质认证
准备营业执照,1-2个工作日审批、300元
5、创建网站应用
提交审核,7个工作日审批
6、熟悉微信登录流程
参考文档:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=e547653f995d8f402704d5cb2945177dc8aa4e7e&lang=zh_CN
获取access_token时序图
在这里插入图片描述
介绍一个详细的
在这里插入图片描述

2、后端开发service_user

说明:微信登录二维码我们是以弹出层的形式打开,不是以页面形式,所以做法是不一样的,参考如下链接,上面有相关弹出层的方式
https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
如图:
在这里插入图片描述

因此我们的操作步骤为:
第一步我们通过接口把对应参数返回页面;
第二步在头部页面启动打开微信登录二维码;
第三步处理登录回调接口;
第四步回调返回页面通知微信登录层回调成功
第五步如果是第一次扫描登录,则绑定手机号码,登录成功
接下来我们根据步骤,一步一步实现

1、添加配置
application.properties添加相关配置信息

# 微信开放平台 appid
wx.open.app_id=你的appid
# 微信开放平台 appsecret
wx.open.app_secret=你的appsecret
# 微信开放平台 重定向url
wx.open.redirect_url=http://你的服务器名称/api/ucenter/wx/callback

2、创建常量类
创建util包,创建ConstantPropertiesUtil.java常量类

@Component
@Data
public class WeixinProperties {

    @Value("${wx.open.app_id}")
    private String appId;

    @Value("${wx.open.app_secret}")
    private String appSecret;

    @Value("${wx.open.redirect_url}")
    private String redirectUrl;
    
}

3、创建controller
service_user微服务中创建api包
api包中创建WeixinController,返回微信参数

@Controller
@RequestMapping("/user/ucenter/wx")
public class WeixinController {

    @Autowired
    private WeixinProperties weixinProperties;

    @RequestMapping("/param")
    @ResponseBody
    public R getWeixinLoginParam() throws UnsupportedEncodingException {
        String redirectUri = URLEncoder.encode(weixinProperties.getRedirectUrl(), "UTF-8");
        HashMap<String, Object> map = new HashMap<>();
        map.put("appid",weixinProperties.getAppId());
        map.put("scope","snsapi_login");
        map.put("redirect_uri",redirectUri);
        map.put("state",System.currentTimeMillis()+"");
        return R.ok().data(map);
    }
}

授权url参数说明
在这里插入图片描述

3、前端显示登录二维码

1、封装api请求
创建api/yygh/wx.js

import request from '@/utils/request'

const api_name = `/user/ucenter/wx`

export default {
  getLoginParam() {
    return request({
      url: `${api_name}/param`,
      method: `get`
    })
  }
}

2、修改组件
修改layouts/myheader.vue文件,添加微信二维码登录逻辑

// 1、引入api
import weixinApi from '@/api/wx'

//2、引入微信js
mounted() {
    ......
    //初始化微信js
    const script = document.createElement('script')
    script.type = 'text/javascript'
    script.src = 'https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js'
    document.body.appendChild(script)
},

......

  weixinLogin() {
    this.dialogAtrr.showLoginType = 'weixin'
    weixinApi.getLoginParam().then(response => {
    var obj = new WxLogin({
      self_redirect:true,
      id: 'weixinLogin', // 需要显示的容器id
      appid: response.data.appid, // 公众号appid wx*******
      scope: response.data.scope, // 网页默认即可
      redirect_uri: response.data.redirect_uri, // 授权成功后回调的url
      state: response.data.state, // 可设置为简单的随机数加session用来校验
      style: 'black', // 提供"black"、"white"可选。二维码的样式
      href: '' // 外部css文件url,需要https
    })
  })

  },
//说明:微信登录方法已绑定weixinLogin(),查看页面

new WxLogin中的容器id是这个
在这里插入图片描述

解释这几行代码是什么意思

//初始化微信js
const script = document.createElement(‘script’)
script.type = ‘text/javascript’
script.src = ‘https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js’
document.body.appendChild(script)

相当于这行代码

< script type=“test/javascript” src=“https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js”>< /script>

4、二维码出现不了进行调试

在这里插入图片描述
报404错误,路径都对了的,由此可以想到可能是网关的问题
在这里插入图片描述
添加即可
在这里插入图片描述
在这里插入图片描述

二、开发微信扫描回调

1、准备工作

1、全局配置的跳转路径

# 微信开放平台 重定向url
wx.open.redirect_url=http://localhost:8160/user/ucenter/wx/callback

2、修改当前项目启动端口号为8160
一定要和微信重定向url的端口号一致
在这里插入图片描述

3、测试回调是否可用
在WxApiController中添加方法

@GetMapping("callback")
public String callback(String code, String state, HttpSession session) {

    //得到授权临时票据code
    System.out.println("code = " + code);
    System.out.println("state = " + state);
    return "";
}

2、后台开发

1、添加依赖

<!--httpclient-->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>
<!--gson-->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
</dependency>

2、创建httpclient工具类
放入util包

package com.donglin.yygh.user.utils;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 *  依赖的jar包有:commons-lang-2.6.jar、httpclient-4.3.2.jar、httpcore-4.3.1.jar、commons-io-2.4.jar
 * @author zhaoyb
 *
 */
public class HttpClientUtils {

	public static final int connTimeout=10000;
	public static final int readTimeout=10000;
	public static final String charset="UTF-8";
	private static HttpClient client = null;

	static {
		PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
		cm.setMaxTotal(128);
		cm.setDefaultMaxPerRoute(128);
		client = HttpClients.custom().setConnectionManager(cm).build();
	}

	public static String postParameters(String url, String parameterStr) throws ConnectTimeoutException, SocketTimeoutException, Exception{
		return post(url,parameterStr,"application/x-www-form-urlencoded",charset,connTimeout,readTimeout);
	}

	public static String postParameters(String url, String parameterStr,String charset, Integer connTimeout, Integer readTimeout) throws ConnectTimeoutException, SocketTimeoutException, Exception{
		return post(url,parameterStr,"application/x-www-form-urlencoded",charset,connTimeout,readTimeout);
	}

	public static String postParameters(String url, Map<String, String> params) throws ConnectTimeoutException,
			SocketTimeoutException, Exception {
		return postForm(url, params, null, connTimeout, readTimeout);
	}

	public static String postParameters(String url, Map<String, String> params, Integer connTimeout,Integer readTimeout) throws ConnectTimeoutException,
			SocketTimeoutException, Exception {
		return postForm(url, params, null, connTimeout, readTimeout);
	}

	public static String get(String url) throws Exception {
		return get(url, charset, null, null);
	}

	public static String get(String url, String charset) throws Exception {
		return get(url, charset, connTimeout, readTimeout);
	}

	/**
	 * 发送一个 Post 请求, 使用指定的字符集编码.
	 *
	 * @param url
	 * @param body RequestBody
	 * @param mimeType 例如 application/xml "application/x-www-form-urlencoded" a=1&b=2&c=3
	 * @param charset 编码
	 * @param connTimeout 建立链接超时时间,毫秒.
	 * @param readTimeout 响应超时时间,毫秒.
	 * @return ResponseBody, 使用指定的字符集编码.
	 * @throws ConnectTimeoutException 建立链接超时异常
	 * @throws SocketTimeoutException  响应超时
	 * @throws Exception
	 */
	public static String post(String url, String body, String mimeType,String charset, Integer connTimeout, Integer readTimeout)
			throws ConnectTimeoutException, SocketTimeoutException, Exception {
		HttpClient client = null;
		HttpPost post = new HttpPost(url);
		String result = "";
		try {
			if (StringUtils.isNotBlank(body)) {
				HttpEntity entity = new StringEntity(body, ContentType.create(mimeType, charset));
				post.setEntity(entity);
			}
			// 设置参数
			Builder customReqConf = RequestConfig.custom();
			if (connTimeout != null) {
				customReqConf.setConnectTimeout(connTimeout);
			}
			if (readTimeout != null) {
				customReqConf.setSocketTimeout(readTimeout);
			}
			post.setConfig(customReqConf.build());

			HttpResponse res;
			if (url.startsWith("https")) {
				// 执行 Https 请求.
				client = createSSLInsecureClient();
				res = client.execute(post);
			} else {
				// 执行 Http 请求.
				client = HttpClientUtils.client;
				res = client.execute(post);
			}
			result = IOUtils.toString(res.getEntity().getContent(), charset);
		} finally {
			post.releaseConnection();
			if (url.startsWith("https") && client != null&& client instanceof CloseableHttpClient) {
				((CloseableHttpClient) client).close();
			}
		}
		return result;
	}


	/**
	 * 提交form表单
	 *
	 * @param url
	 * @param params
	 * @param connTimeout
	 * @param readTimeout
	 * @return
	 * @throws ConnectTimeoutException
	 * @throws SocketTimeoutException
	 * @throws Exception
	 */
	public static String postForm(String url, Map<String, String> params, Map<String, String> headers, Integer connTimeout,Integer readTimeout) throws ConnectTimeoutException,
			SocketTimeoutException, Exception {

		HttpClient client = null;
		HttpPost post = new HttpPost(url);
		try {
			if (params != null && !params.isEmpty()) {
				List<NameValuePair> formParams = new ArrayList<NameValuePair>();
				Set<Entry<String, String>> entrySet = params.entrySet();
				for (Entry<String, String> entry : entrySet) {
					formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
				}
				UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, Consts.UTF_8);
				post.setEntity(entity);
			}

			if (headers != null && !headers.isEmpty()) {
				for (Entry<String, String> entry : headers.entrySet()) {
					post.addHeader(entry.getKey(), entry.getValue());
				}
			}
			// 设置参数
			Builder customReqConf = RequestConfig.custom();
			if (connTimeout != null) {
				customReqConf.setConnectTimeout(connTimeout);
			}
			if (readTimeout != null) {
				customReqConf.setSocketTimeout(readTimeout);
			}
			post.setConfig(customReqConf.build());
			HttpResponse res = null;
			if (url.startsWith("https")) {
				// 执行 Https 请求.
				client = createSSLInsecureClient();
				res = client.execute(post);
			} else {
				// 执行 Http 请求.
				client = HttpClientUtils.client;
				res = client.execute(post);
			}
			return IOUtils.toString(res.getEntity().getContent(), "UTF-8");
		} finally {
			post.releaseConnection();
			if (url.startsWith("https") && client != null
					&& client instanceof CloseableHttpClient) {
				((CloseableHttpClient) client).close();
			}
		}
	}




	/**
	 * 发送一个 GET 请求
	 *
	 * @param url
	 * @param charset
	 * @param connTimeout  建立链接超时时间,毫秒.
	 * @param readTimeout  响应超时时间,毫秒.
	 * @return
	 * @throws ConnectTimeoutException   建立链接超时
	 * @throws SocketTimeoutException   响应超时
	 * @throws Exception
	 */
	public static String get(String url, String charset, Integer connTimeout,Integer readTimeout)
			throws ConnectTimeoutException,SocketTimeoutException, Exception {

		HttpClient client = null;
		HttpGet get = new HttpGet(url);
		String result = "";
		try {
			// 设置参数
			Builder customReqConf = RequestConfig.custom();
			if (connTimeout != null) {
				customReqConf.setConnectTimeout(connTimeout);
			}
			if (readTimeout != null) {
				customReqConf.setSocketTimeout(readTimeout);
			}
			get.setConfig(customReqConf.build());

			HttpResponse res = null;

			if (url.startsWith("https")) {
				// 执行 Https 请求.
				client = createSSLInsecureClient();
				res = client.execute(get);
			} else {
				// 执行 Http 请求.
				client = HttpClientUtils.client;
				res = client.execute(get);
			}

			result = IOUtils.toString(res.getEntity().getContent(), charset);
		} finally {
			get.releaseConnection();
			if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) {
				((CloseableHttpClient) client).close();
			}
		}
		return result;
	}


	/**
	 * 从 response 里获取 charset
	 *
	 * @param ressponse
	 * @return
	 */
	@SuppressWarnings("unused")
	private static String getCharsetFromResponse(HttpResponse ressponse) {
		// Content-Type:text/html; charset=GBK
		if (ressponse.getEntity() != null  && ressponse.getEntity().getContentType() != null && ressponse.getEntity().getContentType().getValue() != null) {
			String contentType = ressponse.getEntity().getContentType().getValue();
			if (contentType.contains("charset=")) {
				return contentType.substring(contentType.indexOf("charset=") + 8);
			}
		}
		return null;
	}



	/**
	 * 创建 SSL连接
	 * @return
	 * @throws GeneralSecurityException
	 */
	private static CloseableHttpClient createSSLInsecureClient() throws GeneralSecurityException {
		try {
			SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
				public boolean isTrusted(X509Certificate[] chain,String authType) throws CertificateException {
					return true;
				}
			}).build();

			SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {

				@Override
				public boolean verify(String arg0, SSLSession arg1) {
					return true;
				}

				@Override
				public void verify(String host, SSLSocket ssl)
						throws IOException {
				}

				@Override
				public void verify(String host, X509Certificate cert)
						throws SSLException {
				}

				@Override
				public void verify(String host, String[] cns,
								   String[] subjectAlts) throws SSLException {
				}

			});

			return HttpClients.custom().setSSLSocketFactory(sslsf).build();

		} catch (GeneralSecurityException e) {
			throw e;
		}
	}

	public static void main(String[] args) {
		try {
			String str= post("https://localhost:443/ssl/test.shtml","name=12&page=34","application/x-www-form-urlencoded", "UTF-8", 10000, 10000);
			//String str= get("https://localhost:443/ssl/test.shtml?name=12&page=34","GBK");
            /*Map<String,String> map = new HashMap<String,String>();
            map.put("name", "111");
            map.put("page", "222");
            String str= postForm("https://localhost:443/ssl/test.shtml",map,null, 10000, 10000);*/
			System.out.println(str);
		} catch (ConnectTimeoutException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SocketTimeoutException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

3、创建回调controller方法
在WxApiController.java中添加如下方法

    @GetMapping("/callback")
    public String callback(String code, String state) throws Exception {
        //第一步 获取临时票据 code
        System.out.println("code:"+code);
        //第二步 拿着code和微信id和秘钥,请求微信固定地址 ,得到两个值
        //使用code和appid以及appscrect换取access_token
        //  %s   占位符
        StringBuffer baseAccessTokenUrl = new StringBuffer()
                .append("https://api.weixin.qq.com/sns/oauth2/access_token")
                .append("?appid=%s")
                .append("&secret=%s")
                .append("&code=%s")
                .append("&grant_type=authorization_code");
        String accessTokenUrl = String.format(baseAccessTokenUrl.toString(),
                weixinProperties.getAppId(),
                weixinProperties.getAppSecret(),
                code);
        //使用httpclient请求这个地址
        String accesstokenInfo = HttpClientUtils.get(accessTokenUrl);
        //System.out.println("accesstokenInfo:"+accesstokenInfo);
        //从返回字符串获取两个值 openid  和  access_token
        JSONObject jsonObject   = JSONObject.parseObject(accesstokenInfo);
        String access_token = jsonObject.getString("access_token");
        String openid = jsonObject.getString("openid");
        //System.out.println(access_token+" "+openid);
        //-----到这里可以调试能不能打印access_token和openid---------

        //判断数据库是否存在微信的扫描人信息
        //根据openid判断
        UserInfo userInfo = userInfoService.selectWxInfoOpenId(openid);
        if (userInfo == null){
            //第三步 拿着openid  和  access_token请求微信地址,得到扫描人信息
            String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
                    "?access_token=%s" +
                    "&openid=%s";
            String userInfoUrl = String.format(baseUserInfoUrl, access_token, openid);
            String resultInfo = HttpClientUtils.get(userInfoUrl);
            System.out.println("resultInfo:"+resultInfo);
            JSONObject resultUserInfoJson = JSONObject.parseObject(resultInfo);
            //解析用户信息
            //用户昵称
            String nickname = resultUserInfoJson.getString("nickname");
            //用户头像
            String headimgurl = resultUserInfoJson.getString("headimgurl");

            //获取扫描人信息添加数据库
            userInfo = new UserInfo();
            userInfo.setNickName(nickname);
            userInfo.setOpenid(openid);
            userInfo.setStatus(1);
            userInfoService.save(userInfo);
        }

        //返回name和token字符串
        Map<String,String> 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);
        //使用jwt生成token字符串
        String token = JwtHelper.createToken(userInfo.getId(), name);
        map.put("token", token);

        //判断userInfo是否有手机号,如果手机号为空,返回openid
        //如果手机号不为空,返回openid值是空字符串
        //前端判断:如果openid不为空,绑定手机号,如果openid为空,不需要绑定手机号
        if(StringUtils.isEmpty(userInfo.getPhone())){
            map.put("openid", userInfo.getOpenid());
        }else {
            map.put("openid", "");
        }
        
        return "redirect:http://localhost:3000/weixin/callback?token="+map.get("token")+ "&openid="+map.get("openid")+"&name="+URLEncoder.encode(map.get("name"),"utf-8");
    }

4、创建UserInfoService方法
根据openid查询用户信息

    //定义方法
    UserInfo selectWxInfoOpenId(String openid);

    //实现方法
    @Override
    public UserInfo selectWxInfoOpenId(String openid) {
        return baseMapper.selectOne(new QueryWrapper<UserInfo>().eq("openid",openid));
    }

到这里可以把前端页面先写上(直接看前台开发)
在这里插入图片描述
同一个手机号登录,(微信登录+强行绑定手机号登录)会出现两条sql,我们需要两条sql合并成一条,这里需要修改之前写的手机登录模块改一下逻辑(判断手机验证时,openid为不为空)

5、创建UserInfoServiceImpl的登录方法
绑定手机号操作

    //登录接口
    @Override
    public Map<String, Object> login(LoginVo loginVo) {
        //1.首先获取用户输入的手机号和验证码信息
        String phone = loginVo.getPhone();
        String code = loginVo.getCode();
        //2.对接收的手机号和验证码做一个非空判断
        if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(code)){
            throw  new YyghException(20001,"数据为空");
        }
        //TODO 对验证码做进一步确认
        //校验校验验证码
        String mobleCode = redisTemplate.opsForValue().get(phone);
        if(!code.equals(mobleCode)) {
            throw new YyghException(20001,"验证码失败");
        }

        String openid = loginVo.getOpenid();
        Map<String, Object> map = new HashMap<>();
        if(StringUtils.isEmpty(openid)) {
            //4.是否手机首次登陆,如果首次登陆,就先网表中注册一下当前用户信息
            QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
            wrapper.eq("phone",phone);
            UserInfo userInfo = baseMapper.selectOne(wrapper);
            if (null == userInfo){
                userInfo = new UserInfo();
                userInfo.setPhone(phone);
                userInfo.setCreateTime(new Date());
                userInfo.setStatus(1);
                baseMapper.insert(userInfo);
            }
            //5.验证用户的status
            if (userInfo.getStatus() == 0){
                throw new YyghException(20001,"用户已经禁用");
            }
            map.get(userInfo);
        }else {
            //1 创建userInfo对象,用于存在最终所有数据
            UserInfo userInfoFinal = new UserInfo();
            //2 根据手机查询数据
            // 如果查询手机号对应数据,封装到userInfoFinal
            UserInfo userInfoPhone =
                    baseMapper.selectOne(new QueryWrapper<UserInfo>().eq("phone", phone));
            if(userInfoPhone != null) {
                // 如果查询手机号对应数据,封装到userInfoFinal
                BeanUtils.copyProperties(userInfoPhone,userInfoFinal);
                //把手机号数据删除
                baseMapper.delete(new QueryWrapper<UserInfo>().eq("phone", phone));
            }
            //3 根据openid查询微信信息
            QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
            wrapper.eq("openid",openid);
            UserInfo userInfoWX = baseMapper.selectOne(wrapper);

            //4 把微信信息封装userInfoFinal
            userInfoFinal.setOpenid(userInfoWX.getOpenid());
            userInfoFinal.setNickName(userInfoWX.getNickName());
            userInfoFinal.setId(userInfoWX.getId());
            //数据库表没有相同绑定手机号,设置值
            if(userInfoPhone == null) {
                userInfoFinal.setPhone(phone);
                userInfoFinal.setStatus(userInfoWX.getStatus());
            }
            //修改手机号
            baseMapper.updateById(userInfoFinal);
            
            //5 判断用户是否锁定
            if(userInfoFinal.getStatus() == 0) {
                throw new YyghException(20001,"用户被锁定");
            }
            //6 登录后,返回登录数据
            map = get(userInfoFinal);
        }
        return map;
    }

    private Map<String,Object> get(UserInfo 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);
        //根据userid和name生成token字符串
        String token = JwtHelper.createToken(userInfo.getId(), name);
        map.put("token", token);
        return map;
    }

3、前台开发

1.回调返回页面
说明:我们只期望返回一个空页面,然后跟登录层通信就可以了,其实就是一个过渡页面,所以我们要给这个过渡页面定义一个空模板
(1)添加空模板组件:/layouts/empty.vue

<template>
    <div>
    <nuxt/>
    </div>
</template>

(2)创建回调返回页面 /pages/weixin/callback.vue

<template>
  <!-- header -->
  <div>
  </div>
  <!-- footer -->
</template>
<script>
export default {
  layout: "empty",
  data() {
    return {
    }
  },
  mounted() {
    //params获取/:id的参数
    let token = this.$route.query.token  //query获取?后的参数https://e/md?Id=91975
    let name = this.$route.query.name
    let openid = this.$route.query.openid
    // 调用父vue方法
    window.parent['loginCallback'](name, token, openid)
  }
}
</script>

(3)在myheader.vue添加方法

mounted() {
    // 注册全局登录事件对象
    window.loginEvent = new Vue();
    // 监听登录事件
    loginEvent.$on('loginDialogEvent', function () {
        document.getElementById("loginDialog").click();
    })
    // 触发事件,显示登录层:loginEvent.$emit('loginDialogEvent')
    //初始化微信js
    const script = document.createElement('script')
    script.type = 'text/javascript'
    script.src = 'https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js'
    document.body.appendChild(script)

    // 微信登录回调处理
    let self = this;
    window["loginCallback"] = (name,token, openid) => {
        self.loginCallback(name, token, openid);
    }
},
    
loginCallback(name, token, openid) {
      // 打开手机登录层,绑定手机号,改逻辑与手机登录一致
      //openid不为空,强制绑定手机号,后端写的逻辑
      if(openid != null) {
        this.userInfo.openid = openid
        this.showLogin()
      } else {
        this.setCookies(name, token)
      }
},

在这里插入图片描述

2.预约挂号页面调整
修改/pages/hospital/_hoscode.vue组件
(1)引入cookie

import cookie from 'js-cookie'

(2)修改方法

schedule(depcode) {
  // 登录判断
  let token = cookie.get('token')
  if (!token) {
    loginEvent.$emit('loginDialogEvent')
    return
  }
  window.location.href = '/hospital/schedule?hoscode=' + this.hospital.hoscode + "&depcode="+ depcode
}

意思就是当你选择门诊的时候必须是登录账号,如果没有就要弹框
在这里插入图片描述

思考loginEvent为啥调用就能弹出登录框,因为在myheader.vue里面设定了loginEvent

  // 注册全局登录事件对象
  window.loginEvent = new Vue();
  // 监听登录事件
  loginEvent.$on('loginDialogEvent', function () {
    document.getElementById("loginDialog").click();
  })
  // 触发事件,显示登录层:loginEvent.$emit('loginDialogEvent')

三、分析代码

WeixinController层中callback方法
使用code和appid以及appscrect换取access_token
在这里插入图片描述
拿着openid和access_token请求微信地址,得到扫描人信息
在这里插入图片描述

四、bug

后端传的空字符串,在前端变成null
在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/357042.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JSP中http与内置对象学习笔记

本博文讲述jsp客户端与服务器端的http、jsp内置对象与控制流和数据流实现 1.HTTP请求响应机制 HTTP协议是TCP/IP协议中的一个应用层协议&#xff0c;用于定义客户端与服务器之间交换数据的过程 1.1 HTTP请求 HTTP请求由请求行、消息报头、空行和请求数据4部分组成。 请求行…

ThreeJS 之界面控制

文章目录参考描述界面自适应问题resize 事件修改画布大小修改视锥体的宽高比全屏显示dblclick 事件检测全屏显示状态进入全屏显示状态退出全屏显示状态尾声参考 项目描述ThreeJS官方文档哔哩哔哩老陈打码搜索引擎BingMDN 文档document.mozFullScreenElementMDN 文档Element.re…

LeetCode题目笔记——6359. 替换一个数字后的最大差值

文章目录题目描述题目链接题目难度——简单方法一&#xff1a;替换代码/Python代码优化总结题目描述 给你一个整数 num 。你知道 Danny Mittal 会偷偷将 0 到 9 中的一个数字 替换 成另一个数字。 请你返回将 num 中 恰好一个 数字进行替换后&#xff0c;得到的最大值和最小值…

CTK学习:(一)编译CTK

CTK插件框架简介 CTK Plugin Framework是用于C++的动态组件系统,以OSGi规范为模型。在此框架下,应用程序由不同的组件组成,遵循面向服务的方法。 ctk是一个开源项目,Github 地址:https://github.com/commontk。 源码地址commontk/CTK: A set of common support code for…

信小程序点击按钮绘制定制转发分享图

1. 说明 先上代码片断分享链接&#xff1a; https://developers.weixin.qq.com/s/vl3ws9mA72GG 使用 painter 画图 按钮传递定制化信息 效果如下&#xff1a; 2. 关键代码说明 文件列表如下&#xff1a; {"usingComponents": {"painter": "/com…

基于springboot的停车场管理系统(程序+文档)

大家好✌&#xff01;我是CZ淡陌。将再这里为大家分享优质的实战项目&#xff0c;本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路。 &#x1f345;更多优质项目&#x1f447;&#x1f…

Android实例仿真之二

目录 三 从无入手 第一阶段 第二阶段 第三阶段 第四阶段 第五阶段 第六阶段 第七阶段 八 举两个典型例子&#xff1a; 九 逆向工程 三 从无入手 这节标题叫从无入手&#xff0c;什么意思呢&#xff1f;如果没有Android这个实例存在&#xff0c;你要做一个类似Android…

Mysql数据库事务

数据库事务 数据库事务由一组sql语句组成。 所有sql语句执行成功则事务整体成功&#xff1b;任一条sql语句失败则事务整体失败&#xff0c;数据恢复到事务之前的状态。 Mysql 事务操作 开始事务 start transaction;- 或 begin;事务开始后&#xff0c;对数据的增删改操作不…

MySQL最佳实践

一、MySQL查询执行过程 1.MySQL分层结构 MySQL8.0没有查询缓存的功能了,如果频繁修改缓存,将会损耗性能查询流程就按照分层结构就可以清楚,只要了解各个组件的各自功能就行分析器主要分析语法和词法是否正确优化器主要优化SQL语句 二、MySQL更新执行过程 更新主要涉及两个重…

SpringCloud - Ribbon负载均衡

目录 负载均衡流程 负载均衡策略 Ribbon加载策略 负载均衡流程 Ribbon将http://userservice/user/1请求拦截下来&#xff0c;帮忙找到真实地址http://localhost:8081LoadBalancerInterceptor类对RestTemplate的请求进行拦截&#xff0c;然后从Eureka根据服务id获取服务列表&…

正点原子ARM裸机开发篇

裸机就是手动的操作硬件来实现驱动设备&#xff0c;后面会有驱动框架不需要这么麻烦 第八章 汇编 LED 灯实验 核心过程 通过汇编语言来控制硬件&#xff08;驱动程序&#xff09; 代码流程 1、使能 GPIO1 时钟 GPIO1 的时钟由 CCM_CCGR1 的 bit27 和 bit26 这两个位控制&…

SSL/STL是什么?怎么工作的?Keystore 和 Truststore是什么?

安全套接字层&#xff08;Secure Sockets Layer&#xff09;&#xff0c;也称为 SSL&#xff0c;是一种加密协议(encryption protocol)&#xff0c;可在 Internet 上的设备之间创建身份验证的通道(authenticated channel)&#xff0c;以便可以安全地共享信息。本质上&#xff0…

动态规划专题精讲1

致前行的人&#xff1a; 要努力&#xff0c;但不要着急&#xff0c;繁花锦簇&#xff0c;硕果累累都需要过程&#xff01; 前言&#xff1a; 本篇文章为大家带来一种重要的算法题&#xff0c;就是动态规划类型相关的题目&#xff0c;动态规划类的题目在笔试和面试中是考察非常高…

【日常点滴019】Python制作流浪气球游戏(导弹射击类)

Python制作流浪气球游戏&#xff08;导弹射击类&#xff09;教学课程代码&#xff08;分步教学版&#xff09;1、构建全局通用代码结构2、构建气球精灵类3、构建导弹精灵类4、碰撞检测5、构建游戏信息类 &#xff08;最终完整代码&#xff09;教学课程代码&#xff08;分步教学…

基于springboot+vue的食疗系统

基于springbootvue的食疗系统 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&…

java面试题-并发基础

1.多线程的出现是要解决什么问题的? 本质什么?提高程序性能&#xff1a;单线程程序只能按照固定的顺序依次执行每个任务&#xff0c;无法同时处理多个任务。多线程技术可以在同一时间内执行多个任务&#xff0c;从而提高程序的运行效率和响应速度。提高程序的并发性&#xff…

前端借助Canvas实现压缩图片两种方法

一、具体代码 1、利用canvas压缩图片方法一 // 第一种压缩图片方法&#xff08;图片base64,图片类型,压缩比例,回调函数&#xff09;// 图片类型是指 image/png、image/jpeg、image/webp(仅Chrome支持)// 该方法对以上三种图片类型都适用 压缩结果的图片base64与原类型相同// …

Python自动化测试-使用Pandas来高效处理测试数据

Python自动化测试-使用Pandas来高效处理测试数据 目录&#xff1a;导读 一、思考 二、使用pandas来操作Excel文件 三、使用pandas来操作csv文件 四、总结 一、思考 1.Pandas是什么&#xff1f; 功能极其强大的数据分析库可以高效地操作各种数据集 csv格式的文件Excel文件H…

Python3-基本数据类型

Python3 基本数据类型 Python 中的变量不需要声明。每个变量在使用前都必须赋值&#xff0c;变量赋值以后该变量才会被创建。 在 Python 中&#xff0c;变量就是变量&#xff0c;它没有类型&#xff0c;我们所说的"类型"是变量所指的内存中对象的类型。 等号&…

注意力机制笔记——结合沐神和B站老弓up主

B站【大白话浅谈【注意力机制】】 聚类 是针对 样本, 注意力机制是针对样本相关性,来进行计算的 自注意力机制 指的是 query ,key,value都是同一个部分。 可以学到 类似的 短语 ,和 语义特征。如its 指代的对象。 评论区大佬 根据这篇论文《Effective Approaches to…