个人开发者实现微信扫码登录

使用码上登录中转微信扫码登录

使用之前最好有一个公网服务器,能够公网访问的 redismysql 数据库,并且能够部署公网访问的服务

码上登录是一个小程序,对个体开发者提供了免费的微信扫一扫登录入口:官网 http://login.vicy.cn/

针对微信扫码登录需要进行企业认证,个人开发者无法使用的问题,我们可以使用码上登录进行桥接,将微信扫码登录的信息转发给我们的程序

在开发之前,我们需要有一个能被公网访问的地址,用来接收码上登录回调给我们的用户数据

更多信息参考码上登录 Api 文档:http://login.vicy.cn/apiWord.html

使用步骤

进入码上登录官网创建我们的应用:http://login.vicy.cn/

  • 微信扫码登录,进入首页,点击 马上创建应用

    image-20220629122032966
  • 点击创建应用,输入网站名称和回调 URL ,填写完成后创建回得到 secretKey

    网站名称就是用户扫码后看到的你的网站名称

    回调 URL 就是用户确认登录后,接收用户信息的 controller ,可以先随便写,再后期修改

    image-20220629122305663

    image-20220629122747300

创建后台应用

  • 先创建一个 web 模块 service-ucenter 用于编写代码,需要整合 mybatisredis ,我使用 SpringBootweb 项目

    导入 commons 和对应的数据库依赖,该依赖的作用是让我们发送 HTTP 请求,获取二维码信息

    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
    </dependency>
    <!--http相关依赖-->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpcore</artifactId>
    </dependency>
    <!-- redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!--mybatis-plus-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
    </dependency>
    <!--mysql-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    

    配置文件如下,拿去使用记得修改对应的数据库配置

    server:
      port: 8085
      servlet:
        application-display-name: service-ucenter
    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://IP:3306/guli_college?useUnicode=true&characterEncoding=utf8&useSSL=false
        username: root
        password: root
        # 配置 druid 连接池
        type: com.alibaba.druid.pool.DruidDataSource
      redis:
        host: IP
        port: 6379
        database: 0
        timeout: 1800000
      mvc:
        pathmatch:
          matching-strategy: ant_path_matcher
        # 开启rest风格
        hiddenmethod:
          filter:
            enabled: true
    
  • 编写 HttpClientUtils 工具类,直接粘过去用

    package com.zym.utils;
    
    import org.apache.commons.io.IOUtils;
    import org.apache.commons.lang3.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() {
    				@Override
    				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;
    		}
    	}
    }
    
  • 编写 controllerservice 代码,处理前端操作,获取二维码信息

    Controller 代码

    /**
     * 微信扫码登录
     * @author zym
     */
    @RestController
    @CrossOrigin
    @RequestMapping("/wx")
    public class WxLogin {
    
        @Resource
        private MemberService memberServiceImpl;
        @Resource
        private RedisTemplate redisTemplate;
    
        /**
         * 向码上登录服务器请求二维码信息
         * @return
         */
        @GetMapping("/login")
        public String login(HttpSession session) throws GuLiError {
            return memberServiceImpl.getQrCode();
        }
    }
    

    编写 Service 代码,通过 HttpClentUtils 发送 HTTP 请求,得到用户扫码登录的二维码信息

    请求地址为:

    https://server01.vicy.cn/8lXdSX7FSMykbl9nFDWESdc6zfouSAEz/wxlogin/wxLogin/tempUserId?secretKey=你的secretKey

    secretKey 更改为自己刚刚在码上登录创建应用得到的 secretKey

    返回值 codeUrl 里面存放了二维码信息,具体参数如下:

    {"errcode":0,"data":{"qrCodeReturnUrl":"http://login.vicy.cn?tempUserId=18d55e78f66848e781bc7ce65a1fe5b4", "tempUserId":"18d55e78f66848e781bc7ce65a1fe5b4"},"message":"成功"}

    属性类型说明
    qrCodeReturnUrlstring把qrCodeReturnUrl发送给前端生成二维码
    tempUserIdstring该次登录会话的凭证,一个二维码的 tempUserId 在回调的时候会传递给后端
    /**
     * @author zym
     */
    @Service
    public class MemberServiceImpl extends ServiceImpl<MemberMapper, Member> implements MemberService {
    
        @Resource
        private RedisTemplate redisTemplate;
    
        /**
         * 通过码上登录获取二维码信息
         * @return
         */
        @Override
        public String getQrCode() {
            String codeUrl = null;
            try {
                codeUrl = HttpClientUtils.get("https://server01.vicy.cn/8lXdSX7FSMykbl9nFDWESdc6zfouSAEz/wxlogin/wxLogin/tempUserId?secretKey=06a0b50645544c58ba32cb475ae6330a");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return codeUrl;
        }
    }
    

前端展示二维码

使用 vue-qriously 利用二维码信息 qrCodeReturnUrl 生成二维码,展示给用户,代码如下

<template>
  <div class="main">

      <div v-show="!showform">
        <qriously :value="qrCodeReturnUrl" :size="250"/>
        <font aligen="center">请使用微信扫码登录</font>
        <!-- initQCode: 是你在你的vue实例中定义好的变量   size:是这个Canvas的大小,这里要根据你的视觉稿来决定-->
      </div>

      <!-- 更多登录方式 -->
      <div class="more-sign">
        <h6>社交帐号登录</h6>
        <ul>
          <li><a id="weixin" class="weixin" target="_blank" @click="getQrCodeReturnUrl()"><i
                class="iconfont icon-weixin" /></a></li>
        </ul>
      </div>
    </div>

  </div>
</template>

<script>
  import '~/assets/css/sign.css'
  import '~/assets/css/iconfont.css'
  import cookie from 'js-cookie'
  import register from '@/api/register'
  import Qriously from 'vue-qriously'
  export default {
    layout: 'sign',

    data () {
      return {
        loginInfo:{},
        qrCodeReturnUrl:'',
        tempUserId:'',
        showform:true
      }
    },

    methods: {
      // 获取扫码登录信息
      getQrCodeReturnUrl(){
        // 请求刚刚写的控制器 http://localhost:8085/wx/login,可以直接 ajax 异步调用,这里使用 vue-admin 模板作了封装
        register.getQrCodeReturnUrl().then(response => {
          if (response.data.errcode === 0){
            console.log(response.data.data);
            this.qrCodeReturnUrl = response.data.data.qrCodeReturnUrl;
            this.tempUserId = response.data.data.tempUserId
            this.showform = false;
          }
        })
      }
  }
</script>
<style>
   .el-form-item__error{
    z-index: 9999999;
  }
</style>

编写码上登录传递信息给我们的地址

同样在上面的 controller 中添加回调方法,此方法必须是 post 请求类型,接收如下五个参数

属性类型说明
userIdstring用户唯一ID
tempUserIdstring该次会话的凭证
nicknamestring登录用户的昵称(用户授权后,可获得,可能为空)
avatarstring登录用户的头像url(用户授权后,可获得,可能为空)
ipAddrstring登录地点的ip

该方法还需要返回扫码登录状态,码上登录会根据返回的信息在用户手机上提示是否登录成功,返回信息如下,使用 map 封装这两个属性,需要将其转换成 json 格式进行返回

属性类型说明
errcodenumber错误码(0:请求成功)
messagestring接口调用情况描述,如:”成功”

详细代码如下:注意:此方法需要解决跨域问题,我使用了 Getway 网关解决,也可以加上 @CrossOrigin 注解或者其他方式

/**
* 微信登录回调
* @return
*/
@PostMapping("/callback")
@ResponseBody
public String callback(@RequestParam(value = "userId",required = false)  String userId,
                       @RequestParam(value = "tempUserId",required = false) String tempUserId,
                       @RequestParam(value = "nickname",required = false) String nickname,
                       @RequestParam(value = "avatar",required = false) String avatar,
                       @RequestParam(value = "ipAddr",required = false) String ipAddr,
                       HttpServletResponse httpServletResponse) {
    try {
        // 判断用户是否第一次登录
        Member memberById = memberServiceImpl.getById(userId);
        if (memberById == null) {
            // 第一次登录,创建用户
            Member member = new Member();
            member.setId(userId);
            member.setNickname(nickname);
            member.setAvatar(avatar);
            memberServiceImpl.save(member);
            memberById = memberServiceImpl.getById(userId);
        }

        // 使用 JwtUtils 生成 token
        String token = JwtUtils.getJwtToken(memberById.getId(), memberById.getNickname());
        // 将用户 token 存放进 redis,键是 tempUserId
        redisTemplate.opsForValue().set(tempUserId, token, 1, TimeUnit.MINUTES);

        HashMap<String, Object> map = new HashMap<>(1);
        if (userId != null) {
            map.put("errcode", 0);
            map.put("message", "登录成功");
        }
        Gson gson = new Gson();
        return gson.toJson(map);
    } catch (Exception e) {
        HashMap<String, Object> map = new HashMap<>(1);
        map.put("errcode", 1);
        map.put("message", "登录失败");
        Gson gson = new Gson();
        return gson.toJson(map);
    }
}

JwtUtils 如下:

package com.zym.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Optional;

/**
 * JWT工具类
 * @author zym
 */
public class JwtUtils {
    public static final long EXPIRE = 1000 * 60 * 60 * 24;
    public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";
    public static String getJwtToken(String id, String nickname){
        String JwtToken = Jwts.builder()
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")
                .setSubject("guli-user")
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
                .claim("id", id)
                .claim("nickname", nickname)
                .signWith(SignatureAlgorithm.HS256, APP_SECRET)
                .compact();
        return JwtToken;
    }
    /**
     * 判断token是否存在与有效
     * @param jwtToken
     * @return
     */
    public static boolean checkToken(String jwtToken) {
        if(StringUtils.isEmpty(jwtToken)) {
            return false;
        }
        try {
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    /**
     * 判断token是否存在与有效
     * @param request
     * @return
     */
    public static boolean checkToken(HttpServletRequest request) {
        try {
            String jwtToken = request.getHeader("token");
            if(StringUtils.isEmpty(jwtToken)) {
                return false;
            }
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    /**
     * 根据token获取会员id
     * @param request
     * @return
     */
    public static String getMemberIdByJwtToken(HttpServletRequest request) {
        String jwtToken = request.getHeader("guli_token");
        if(!Optional.ofNullable(jwtToken).isPresent()) {
            return "";
        }
        Jws<Claims> claimsJws =
                Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        Claims claims = claimsJws.getBody();
        return (String)claims.get("id");
    }
}

将本项目打包放到公网服务器上运行

登录码上登录官网,修改之前创建应用的回调 URLcallback 方法的公网请求路径

image-20220629134031603

前端获取登录状态

在之前创建的 controller 中新增 getToken 方法,根据 tempUserId 循环查询 redis 中是否有这个用户的信息,如果有,就将其返回给前端

一段时间后,redis 都没有查询到用户的 token 则视为登录过期,提示用户刷新二维码后重新登录

    /**
     * 微信登录回调,根据用户 tempUserId 从 redis 中获取 token
     * @return
     */
    @GetMapping("/getToken/{tempUserId}")
    public R getToken(@PathVariable String tempUserId) {

        System.out.println("tempUserId = " + tempUserId);
        boolean flag = false;

        for (int i = 0; i < 60; i++) {
            String token = (String) redisTemplate.opsForValue().get(tempUserId);
            if (token != null) {
                return R.ok().setData("token", token);
            }else {
                try {
                    sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        return R.error().setData("message", "登录超时,点击微信图标刷新二维码后重试");
    }

前端编写 getToken 函数,调用这个 controller 查询登录状态,这里只展示 js 代码

<script>
  import '~/assets/css/sign.css'
  import '~/assets/css/iconfont.css'
  import cookie from 'js-cookie'
  import register from '@/api/register'
  import Qriously from 'vue-qriously'
  export default {
    layout: 'sign',

    data () {
      return {
        loginInfo:{},
        qrCodeReturnUrl:'',
        tempUserId:'',
        showform:true
      }
    },

    methods: {
      // 获取扫码登录信息
      getQrCodeReturnUrl(){
        register.getQrCodeReturnUrl().then(response => {
          if (response.data.errcode === 0){
            console.log(response.data.data);
            this.qrCodeReturnUrl = response.data.data.qrCodeReturnUrl;
            this.tempUserId = response.data.data.tempUserId
            this.showform = false;
            // 调用 getToken 函数
            this.getToken();
          }
        })
      },
      // 获取扫码登录用户token
      getToken(){
        // 请求后端刚刚写的控制器 http://localhost:8085/wx/getToken/这里是tempUserId ,查询这个二维码的登录信息
        register.getToken(this.tempUserId).then(response => {
          // 成功获取到登录信息,则将用户 token 保存到 cookie 中,并跳转到网站首页
          if (response.data.status === true) {
            console.log(response.data.data);
            cookie.set("token", response.data.data.token);
            this.$router.push("/");
          } else {
              // 获取用户信息超时的逻辑请自己编写
          }
        })
      }
    },
  }
</script>

注意:这里使用jwt做单点登录,你也可以直接存储用户信息