概述

        最近工作负责的的项目是一个微信小程序,后端使用的是SpringBoot,给大家分享一下SpringBoot拿到前端传过来的code,如何处理最终返回给前端一个唯一标识token。

逻辑

1. 获取code

code是什么呢?大家可以看一下下面的图。

之前的文章也讲过这个图,前端需要先调用wx.login()方法来获取code,它的作用是让后端给微信的服务端发送请求,获取用户的唯一标识openId,以及session_keyopenId是每个微信用户都拥有的一个唯一标识,我们可以用它来存储用户的登录状态,以及识别用户身份,但是最好不要直接使用openId,建议在使用的时候采用一些加密算法。session_key是微信服务端生成的临时会话密钥,用于加密 / 解密用户敏感数据,以及验证用户数据的完整性。

/**
     * 登录认证接口
     * @param code
     * @return jwt
     */
    @GetMapping("/login/{code}")
    public Response<ClientUserLoginVO> login(@PathVariable String code) {
        log.info("code:{}", code);
        ClientUserLoginVO clientUserLoginVO = null;
        try {
            clientUserLoginVO = clientUserService.getUserToken(code);
        } catch (Exception e) {
            e.printStackTrace();
            return Response.<ClientUserLoginVO>builder()
                    .code(ResponseCode.FAIL.getCode())
                    .info(e.toString())
                    .build();
        }
        log.info("生成ClientUserLoginVO:{}", clientUserLoginVO);
        return Response.<ClientUserLoginVO>builder()
                .code(ResponseCode.SUCCESS.getCode())
                .info(ResponseCode.SUCCESS.getInfo())
                .data(clientUserLoginVO)
                .build();
    }

2. 获取openId和session_key

ok,那么后端获取到code之后,就可以使用code,appid,appSecret去向微信服务器发送请求去获得openId和session_key,其中openId就是我们用来标识用户身份的一个值。

(appId,appSecret需要到微信公众平台获取)

https://mp.weixin.qq.com/https://mp.weixin.qq.com/

这里我编写了一个获取openId的方法,由于项目中没有用到session_key去解密信息,信息是需要用户填写的,所以我就没存。

public String getUserOpenId(String code) {
        Map<String, String> buildParams = buildParams(code);
        String url = "https://api.weixin.qq.com/sns/jscode2session";
        String result = HttpClientUtil.sendPost(url, buildParams);
        if (result == null) {
            throw new RuntimeException("获取openId失败");
        }
        log.info("post请求获取结果: {}", result);

        // 解析出openId
        JSONObject jsonObject = JSONObject.parseObject(result);
        String openid = jsonObject.getString("openid");
        return openid;
    }
private Map<String, String> buildParams(String code) {
        Map<String, String> paramsMap = new HashMap<>();
        paramsMap.put("appid", appid);
        paramsMap.put("secret", appsecret);
        paramsMap.put("js_code", code);
        paramsMap.put("grant_type", "authorization_code");
        return paramsMap;
    }

既然需要向微信服务端发请求,那我们就先把参数装到一个Map当中,在接下来发请求的时候使用,构建好请求参数,接下来我们就要去发送请求获取openId

发送请求获取openId的代码如下:

public static String sendPost(String url, Map<String, String> params) {
        HttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);

// 将Map<String, String>类型的参数转换为 HttpClient 需要的List<NameValuePair>类型, NameValuePair是键值对接口,BasicNameValuePair是其基本实现类
        List<NameValuePair> urlParameters = params.entrySet().stream()
                .map(entry -> new BasicNameValuePair(entry.getKey(), entry.getValue()))
                .collect(Collectors.toList());

        try {
            httpPost.setEntity(new UrlEncodedFormEntity(urlParameters, StandardCharsets.UTF_8));
            HttpResponse response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();

            if (null != entity) {
                return new BufferedReader(new InputStreamReader(entity.getContent())).lines()
                        .collect(Collectors.joining("\n"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

这里我们用的HttpClient来发送请求,获得结果字符串

我们再将结果字符串,变成json对象,从里面拿到openId,这里也可以获取session_key。

3. 对openId 进行处理

拿到openId之后,我们就需要将他存储起来,他就是用户的唯一标识,我们要进行关联存储,这里我为用户添加了一个字段交userId,来存储一个与openId相关的值,openId是每个用户在微信服务端的唯一标识,所以这里我没有直接存储,而是进行了AES对称加密,在存储到userId上

这里大家可以自己采取办法处理一下,AES加密后面文章我也会给大家分享一下。

4. 构建jwt返回给前端

经过加密之后我们就得到了真正存储到数据库的userId唯一标识,这时候我们就可以构建jwt,来返回给前端了

/**
     * 生成JWT
     * @Param dataMap 数据载荷
     */
    public String createJWT(Map<String, Object> payload) {
        // JWT头部信息【header】
        Map<String, Object> header = new HashMap<>();
        header.put("alg", alg);
        header.put("typ", typ);

        // 声明token失效时间(这部分也会放到数据载荷部分), 默认为24小时
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND, 60*60*24);

        // 生成token
        String token = Jwts.builder()
                .setHeader(header)
                .setClaims(payload)
                .setExpiration(instance.getTime())
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();

        return token;
    }

    /**
     * 从jwt中解析出数据载荷
     * @return
     */
    public JWTDTO parseJWT(String jwt) {
        JWTDTO jwtdto = new JWTDTO();
        // 解析payload
        Claims claims = Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(jwt)
                .getBody();
        jwtdto.setUserId((String) claims.get("userId"));
        jwtdto.setExpireTime(claims.getExpiration());
        return jwtdto;
    }

这里也是直接把jwt解密加密代码给大家

这是alg和typ,需要自己定义一个秘钥secret用来加密与解密

经过这一步骤,我们就可以生成jwt返回给前端了,前端拿到jwt之后,可以将jwt存储到请求头中,接下来后端编写拦截器来判断用户是否携带有效jwt,没有则拦截,让用户进行登录。

(当然这里jwt是有时间的,过期就无效了,大家可以想一想jwt滑动刷新如何实现)

到这里后端就处理完code生成了jwt可以返回给前端啦。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐