zoukankan      html  css  js  c++  java
  • 微信扫码登录实现

    1.前端准备

    定义一个div容器,用于展示生成的二维码

    <div id="weixinLogin"></div>

    安装二维码插件

    npm install vue-qriously --save-dev

    在main.js中进行引入插件

    import VueQriously from 'vue-qriously'
    
    Vue.use(VueQriously)

    编写一个微信二维码生成插件的方法

        weixinLogin() {
          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.redirectUrl, //redirectUrl// 授权成功后回调的url
                  state: response.data.state, // 可设置为简单的随机数加session用来校验
                  style: 'black', // 提供"black"、"white"可选。二维码的样式
                  href: '' // 外部css文件url,需要https
                })
          })
        },

    注意:此处需要的参数定义在后端,需要调用接口来获取,对应接口如下

    api:

    const BASEURL = "/api/user/wx";
    //获取微信参数
    export function getLoginParam() {
      return request({
        url: `${BASEURL}/getLoginParam/`,
        method: "get",
      });
    }

    2.后端实现

    后端接口实现:

    WX_OPEN_REDIRECT_URL为 http://localhost:8160/api/user/wx/callback

    这里的url即为扫描成功后跳转的页面

        /**
         * 获取二维码相关参数
         */
        @GetMapping("getLoginParam")
        @ResponseBody
        public Result getLoginParam() {
            Map<String, Object> map = new HashMap<>();
            map.put("appid", ConstantWxUtils.WX_OPEN_APP_ID);
            map.put("scope", "snsapi_login");
            //对redirect_url进行URLEncoder编码
            String redirectUrl = ConstantWxUtils.WX_OPEN_REDIRECT_URL;
            try {
                redirectUrl = URLEncoder.encode(redirectUrl, "utf-8");
            } catch (Exception e) {
                e.printStackTrace();
            }
            map.put("redirectUrl", redirectUrl);
            map.put("response_type", "code");
            map.put("state", "Eric");
            return Result.ok(map);
        }

    远程调用微信接口可能用到的工具类

    package com.gh.util;
    
    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;
    
    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);
                }
                // 设置参数
                RequestConfig.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 请求
         */
        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
         */
        @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;
            }
        }
    }
    View Code

    微信扫码登录回调

    注意:扫码登录会自动获取一个随机code,这个code是微信发给用户的临时令牌。我们可以根据code再次请求微信第三方登陆接口得到access_token(正式令牌)

    /**
         * 微信扫码登录回调
         */
        @GetMapping("callback")
        public String callback(String code, String state) {
            //拿着临时票据code和微信id和密钥,请求微信固定地址,得到两个值accessToken和openId
            //使用code和appid以及appscrect换取access_token
            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(),
                    ConstantWxUtils.WX_OPEN_APP_ID,
                    ConstantWxUtils.WX_OPEN_APP_SECRET,
                    code);//替换url中的值
            try {
                String accessTokenInfo = HttpClientUtils.get(accessTokenUrl);
                JSONObject jsonObject = JSONObject.parseObject(accessTokenInfo);
                String access_token = jsonObject.getString("access_token");
                String openid = jsonObject.getString("openid");
                //判断数据库是否存在扫描人信息
                UserInfo userInfo = userInfoService.findByOpenId(openid);
                if (userInfo == null) {
                    //拿着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);
                    String resultUserInfo = HttpClientUtils.get(userInfoUrl);
                    JSONObject object = JSONObject.parseObject(resultUserInfo);
                    //解析用户信息(头像,昵称等等)
                    String headimgurl = object.getString("headimgurl");
                    String nickname = object.getString("nickname");
                    //添加数据库
                    userInfo = new UserInfo();
                    userInfo.setNickName(nickname);
                    userInfo.setOpenid(openid);
                    userInfo.setStatus(1);
                    userInfoService.insert(userInfo);
                }
                //返回name和token字符串
                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);
                //  前端判断openid不为空,需要绑定手机号,如果为空不要绑定手机号
                if (StringUtils.isEmpty(userInfo.getPhone())) {
                    map.put("openid", openid);
                } else {
                    map.put("openid", "");
                }
                String token = JwtHelper.createToken(userInfo.getId(), name);
                map.put("token", token);
                return "redirect:" + ConstantWxUtils.YYGH_BASE_URL + "/weixin/callback?token="+map.get("token")+"&openid="+map.get("openid")+"&name="+URLEncoder.encode((String)map.get("name"));
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

    注意:上述方法返回值是一个前端vue页面,这个页面作为一个中间页面callback.vue,用来获取返回携带的参数。

    <template>
      <!-- header -->
      <div>
      </div>
      <!-- footer -->
    </template>
    <script>
    export default {
    //   layout: "empty",
      data() {
        return {
        }
      },
      mounted() {
        let token = this.$route.query.token
        let name = this.$route.query.name
        let openid = this.$route.query.openid
        // 调用父vue方法
        window.parent['loginCallback'](name, token, openid)
      }
    }
    </script>

    在父组件进行接收:

    mounted(){
        //微信回调的方法
        let self = this;
        window["loginCallback"] = (name,token, openid) => {
          self.loginCallback(name, token, openid);
        }
    }
    判断openid是否为空,如果不为空,需要绑定手机号,如果为空不要绑定手机号
        loginCallback(name, token, openid){
          console.log("openid="+openid)
          if(openid!=null){
            this.userInfo.openid=openid
            this.showLogin()
          }else{
            this.setCookies(name,token)
          }
        },

    把name和token存在cookie

        setCookies(name, token) {
          cookie.set("token", token, { domain: "localhost" });
          cookie.set("name", name, { domain: "localhost" });
          window.location.reload();
        }

    一点点学习,一丝丝进步。不懈怠,才不会被时代所淘汰!

  • 相关阅读:
    弹性网卡支持私网多IP
    微服务浪潮中,程序猿如何让自己 Be Cloud Native
    Nacos v0.7.0:对接CMDB,实现基于标签的服务发现能力
    如何更高效的管理原生微服务应用
    如何在 Intellij IDEA 更高效地将应用部署到容器服务 Kubernetes
    PHP flock文件锁
    MySQL锁(MyISAM和InnoDB)
    汽车操作系统革命:封闭还是开源?
    采集百度top500歌曲,python2.7.2
    关于revision 的cover letter
  • 原文地址:https://www.cnblogs.com/fqh2020/p/15388261.html
Copyright © 2011-2022 走看看