当前位置: 首页 > news >正文

小程序如何用data的数据控制页面展示_小程序实战之登录的原理和实现(内含福利)...

e9568600-fd18-eb11-8da9-e4434bdf6706.gif

ee568600-fd18-eb11-8da9-e4434bdf6706.jpeg

作者 | 码匠笔记

责编 | 伍杏玲

本文经授权转载自码匠笔记(ID:majiangbiji)

f2568600-fd18-eb11-8da9-e4434bdf6706.png

登录原理

登录便是小程序的开始,小程序可以方便的使用微信登录,获取用户的个人信息,这样我们就能保留用户的信息和记录用户的操作。

如图是小程序官方给出的登录过程:

f5568600-fd18-eb11-8da9-e4434bdf6706.jpeg

1.调用 wx.login() 获取临时登录凭证code ,并回传到开发者服务器。调用 code2Session 接口,换取用户唯一标识 OpenID 和会话密钥(sessionkey),为了安全所以需要使用小程序先获取 code 然后再传递到服务器端获取登录信息进行登录。

2.sessionkey。微信为登录用户设置的登录session,用户校验登录态和下文中我们用于校验用户信息的正确性。
文档地址:
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html

因为我们不仅仅需要有登录状态,还需要获取用户信息保存到服务器端所以我们需要获取用户信息,目前我们获取用户信息需要在小程序端使用 button 组件,并将 open-type 指定为 getUserInfo 类型,点击 button 的时候通过 bindgetuserinfo属性 绑定的 callback 接收用户信息。通过上述方式我们可以获取到 rawData 和 signature,rawData 是用户信息:

{ "nickName": "小匠",  "gender": 1,"language": "zh_CN","city": "北京","province": "北京","country": "CN","avatarUrl": "http://wx.qlogo.cn/mmopen/vi_32/1vZvI39NWFQ9XM4LtQpFrQJ1xlgZxx3w7bQxKARol6503Iuswjjn6nIGBiaycAjAtpujxyzYsrztuuICqIM5ibXQ/0"
}

因为

signature=sha1(rawData+session_key),

所以我们把这两个属性直接传递到服务器端,不仅获取到了用户信息,用于存储到服务器端,还能校验请求数据的真实性。

文档地址
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html

我对流程图进行了优化,这样可以减少一次服务端的请求,提高响应速度。f8568600-fd18-eb11-8da9-e4434bdf6706.png1.首先在小程序端通过调用 wx.login 和 getUserInfo 获取code 和 用户信息,一起通过 wx.request 发送给开发者服务器端,这样便减少一些请求。

2.调用 jscode2session 接口到微信接口服务获取 session_key 和 openId,直接对用户信息进行 SHA1 校验,校验成功以后创建自定义登录态,返回自定义登录态到小程序。

3.自定义登录态是什么呢?做过web的朋友都知道 session 和 cookies 可以组合做登录态,我们这里也是模拟这种设计,只是我们会把session存到数据库中,自定义生成 cookies(token) 传递给小程序端,存储到 storage 里面。

4.storage 类似于浏览器的 localStorage,用户小程序端方便的存储一些基础数据。

fb568600-fd18-eb11-8da9-e4434bdf6706.png

小程序端逻辑实现

现在我们已经理解了怎么做登录验证,那接下来我们开始编写客户端代码。 

首先我们需要在 onLoad 方法里面调用 wx.login() 方法,onLoad 方法在页面加载的是就会调用,当页面加载的时候我们就把 code 存入 data 里面,以便后面调用服务端接口的时候使用。因为每次调用 wx.login 对应的 session_key 就会变,所以必须保证 wx.login 的调用 在获取用户信息之前。这时候我们每次刷新页面都会在控制台看到如下输出(我这里使用的是 Command + S 每次会重新编译小程序)

00578600-fd18-eb11-8da9-e4434bdf6706.png

然后我们需要在 question/index.wxml 文件中的按钮上面添加 open-type 和 bindgetuserinfo,如下:

class="btn" open-type="getUserInfo" 
bindgetuserinfo="getUserInfo" hover-class="btn-click">
登录

接下来在 question/index.js 里面添加 getUserInfo 方法用于接收点击按钮获取用户信息的回调,回调方法里面的 userInfo 就是我们想要的用户信息。我们再模拟器里面点击按钮,在控制台里面查看具体的信息,具体模拟器和控制台怎么用,我们在《客户端代码准备和基础功能讲解》中已经有讲解。

// 绑定wxml的button,用户获取用户信息
getUserInfo: function(userInfo) {console.log(userInfo)
}

06578600-fd18-eb11-8da9-e4434bdf6706.png

大家应该都知道 JSON 这种数据传输格式,我们下面就使用 wx.request 发送用户信息和 code 到服务器端,wx.request 对于初学者你们可以理解为 ajax。按照我的接口定义,我只要发送 signature, rawData 和 code 到服务器端,然后接收数据。所以我的实现如下:

wx.request({
    url: config.serverHost + '/api/login',
    method: "post",data: JSON.stringify({
        code: this.data.code,
        rawData: userInfo.detail.rawData,
        signature: userInfo.detail.signature
    }),
    dataType: "json",
    success: response = >{
        wx.hideLoading();
        console.log(response);if (response.data.status == 200) {// 展示 登录成功 提示框
            wx.showToast({
                title: '登录成功',
                icon: "none",
                duration: 1000
            });// 把自定义登录状态 token 缓存到小程序端
            wx.setStorage({
                key: "token",data: response.data.data.token
            });
        } else {// 展示 错误信息
            wx.showToast({
                title: response.data.message,
                icon: "none",
                duration: 1000
            });
        }
    },
    fail: response = >{
        console.log(response)
        wx.showToast({
            title: '登录失败,请重试'
        });
    }
})

切记这段代码块要放在 wx.login 成功以后。

  • url 就是请求的地址

  • config.serverHost是我封装的常量,以便后面修改地址

  • method 是请求类型

  • data 是请求的 JSON 请求体

  • dataType 是定义的请求类型

  • success 和 fail 分别是成功和失败的回调

我们会根据返回的 response 做相应的处理。wx.request 会把我返回的内容再包裹一个 data,所以如上内容登录成功和失败我实际返回的 JSON 如下:

{"status": 200,"message": "登录成功","data": {"token": "86247a92-38d9-4908-a024-07be468c2c76"
    }
}

{"status": 400,"message": "无效登录"
}

wx.showToast 是提示框,所以当成功以后提示成功,失败以后把message获取到提示给用户。

最后代码中你会看到

wx.showLoading({title: "登录中",
    mask: true
});

wx.hideLoading();

是为了优化一下体验,当点击登录的时候提示登录中,等待服务端返回数据的时候消失,到此本节小程序部分已经结束。

0a578600-fd18-eb11-8da9-e4434bdf6706.png

服务端逻辑实现

上面客户端已经完成,开始开发服务端。首先我们需要定义一个 API 入口 Controller,用于定义 api/login 接口接收小程序发过来的请求。如下:

@RestController@Slf4jpublic class LoginController {// Spring 自动注入 wechatAdapter,因 WechatAdapter 类上面有 @Service 注解@Autowiredprivate WechatAdapter wechatAdapter;// 定义 domain/api/login 访问接口,用于实现登录// 使用 LoginDTO 自动解析传递过来的 JSON 数据@RequestMapping("/api/login")public ResultDTO login(@RequestBody LoginDTO loginDTO) {try {
            log.info("login request : {}", loginDTO);// 使用 code 调用微信 API 获取 session_key 和 openid
            SessionDTO sessionDTO = wechatAdapter.jscode2session(loginDTO.getCode());
            log.info("login get session : {}", sessionDTO);// 检验传递过来的使用户信息是否合法
            DigestUtil.checkDigest(loginDTO.getRawData(), sessionDTO.getSessionKey(), loginDTO.getSignature());//TODO: 储存 token//生成token,用于自定义登录态,这里的存储逻辑比较复杂,放到下一讲
            TokenDTO data = new TokenDTO();data.setToken(UUID.randomUUID().toString());return ResultDTO.ok(data);
        } catch(ErrorCodeException e) {
            log.error("login error, info : {}", loginDTO, e.getMessage());return ResultDTO.fail(e);
        } catch(Exception e) {
            log.error("login error, info : {}", loginDTO, e);return ResultDTO.fail(CommonErrorCode.UNKOWN_ERROR);
        }
    }
}

根据如上代码,我们找一些关键点讲解一下
1.RestController,定义当前 Controller 为 Restful,最简单的理解就是 @RestController = @ResponseBody + @Controller

2.@Slf4j,Lombok注解,需要在 Idea 插件中安装 Lombok,这样在需要使用日志的时候,直接使用 log.*()就可以了。

3.@RequestBody LoginDTO loginDTO,会把传递过来的 JSON 对象自动序列化成对象。

ErrorCodeException 自定义 RuntimeException,根据业务的异常友好的提示到小程序端,具体使用interface和enum实现,源码可以参考 com.codedrinker.error 包下面的类。

对于返回对象的统一封装,便于小程序端接收和处理。

@Datapublic class ResultDTO {private Integer status;private Object data;private String message;
}

status 是状态码,如果不是200,都是异常。
data 是数据,可以是多种类型。
message 返回的错误信息。
@Data 是lombok 的注解,可以自动生成 set get 方法,省去了自己编写 set get 的麻烦。

下面是对调用微信的 API 进行封装

@Servicepublic class WechatAdapter {private final Logger logger = LoggerFactory.getLogger(WechatAdapter.class);
    @Value("${wechat.appid}")private String appid;
    @Value("${wechat.secret}")private String secret;public SessionDTO jscode2session(String code) {String url = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";
        OkHttpClient okHttpClient = new OkHttpClient();Request request = new Request.Builder()
        .addHeader("content-type", "application/json")
        .url(String.format(url, appid, secret, code))
        .build();
        try {Response execute = okHttpClient.newCall(request).execute();if (execute.isSuccessful()) {
                SessionDTO sessionDTO = JSON.parseObject(execute.body().string(), SessionDTO.class);
                logger.info("jscode2session get url -> {}, info -> {}", String.format(url, appid, secret, code), JSON.toJSONString(sessionDTO));
                return sessionDTO;
            } else {
                logger.error("jscode2session authorize error -> {}", code);
                throw new ErrorCodeException(CommonErrorCode.OBTAIN_OPENID_ERROR);
            }
        } catch(IOException e) {
            logger.error("jscode2session authorize error -> {}", code, e);
            throw new ErrorCodeException(CommonErrorCode.OBTAIN_OPENID_ERROR);
        }
    }
}

讲解一下 Adapter 里面的关键点:
1.@Value("${wechat.appid}") 是 Spring 的自动注解,直接读取 application-*.yml 里面的配置赋值给appid变量。这时候我们的 application-production.yml 里面的配置如下:

wechat:appid: $ {
    WECHAT_APPID
}secret: $ {
    WECHAT_SECRET
}

appid和secret是在小程序控制台->开发设置->开发者ID里面获取,为了安全,我们不能把这些信息直接提交到代码里面,所以我选择了把它们配置到 Heroku 的环境变量里面,在部署的时候会自动的替换掉 application-production.yml 里面的 ${WECHAT_APPID} 占位符,然后通过 @Value 赋值到 @Service里面的变量 appid。具体配置方式如下,还需要先进入 Heroku 的控制台,然后点击Settings 进行配置,记住左边是 application-production.yml 里面的占位符,右边是你小程序的 id 和 secret。

0e578600-fd18-eb11-8da9-e4434bdf6706.png

11578600-fd18-eb11-8da9-e4434bdf6706.png

2.OkHttpClient,是使用的 OKHttp,小编觉得这个用起来比 Apache 的 HttpClient 要简单,代码就不需要讲解了,继续要因为一个 pom.xml 文件。

--调用HTTP请求--><dependency ><groupId > com.squareup.okhttp3 groupId><artifactId>okhttpartifactId ><version > 3.8.1 version>dependency > 

3.JSON.parseObject 直接解析String到对象。

4.throw new ErrorCodeException(CommonErrorCode.OBTAINOPENIDERROR);

直接返回业务的ErrorCode,

对了,忘记说最重要的一个问题,这次改动需要添加4个pom.xml

--自动生成set get方法--><dependency ><groupId > org.projectlombok groupId><artifactId>lombokartifactId ><version > 1.12.4 version>dependency ><dependency ><groupId > com.squareup.okhttp3 groupId><artifactId>okhttpartifactId ><version > 3.8.1 version>dependency ><dependency ><groupId > com.alibaba groupId><artifactId>fastjsonartifactId ><version > 1.2.33 version>dependency ><dependency ><groupId > commons - codec groupId><artifactId>commons-codecartifactId ><version > 1.11 version>dependency > 

服务端源码地址:

https://github.com/codedrinker/jiuask-server(本讲 Tag V5)

小程序源码地址:

https://github.com/codedrinker/jiuask(本讲 Tag V5)

作者简介:码匠笔记,先后就职于 ThoughtWorks、阿里巴巴等互联网公司。专注于JAVA、并发编程、性能优化、架构设计、小程序、开源软件。

公众号:码匠笔记(ID:majiangbiji)

福利来啦!

如果你是学生,对小程序感兴趣

如果你是开发者,寻找技术交流的伙伴

你可以加入我们——

16578600-fd18-eb11-8da9-e4434bdf6706.jpeg

1c578600-fd18-eb11-8da9-e4434bdf6706.png

20578600-fd18-eb11-8da9-e4434bdf6706.jpeg

26578600-fd18-eb11-8da9-e4434bdf6706.gif

 热 文 推 荐 

☞別跟我提年终奖!

☞“黄鳝门”视频女主播一审宣判!

☞刚刚!华为又被美国盯上了!

☞“拼多多”惊爆重大 Bug!程序员的眼泪,羊毛党的狂欢

☞12306能扛住明星出轨这种流量冲击吗?

☞Kafka学习笔记

☞V神说,解释以太坊2.0最好的文章就是这篇了

print_r('点个好看吧!');
var_dump('点个好看吧!');
NSLog(@"点个好看吧!");
System.out.println("点个好看吧!");
console.log("点个好看吧!");print("点个好看吧!");printf("点个好看吧!");
cout <"点个好看吧!" <Console.WriteLine("点个好看吧!");
fmt.Println("点个好看吧!");
Response.Write("点个好看吧!");alert("点个好看吧!")echo "点个好看吧!"

2a578600-fd18-eb11-8da9-e4434bdf6706.gif点击“阅读原文”,打开 CSDN App 阅读更贴心!

32578600-fd18-eb11-8da9-e4434bdf6706.png 喜欢就点击“好看”吧

http://www.taodudu.cc/news/show-8381704.html

相关文章:

  • 边缘计算与物联网精华问答 | 边缘计算和物联网有什么关系?
  • 营养餐搭配小程序开发价值
  • 「安搭Share你问我答」你老了,如何养老?
  • 神奇瘦身食谱 调整你多余脂肪 - 生活至上,美容至尚!
  • 神奇瘦身食谱 调整你多余脂肪 - 健康程序员,至尚生活!
  • 计算机毕业设计Android家庭食谱推荐系统app(源码+系统+mysql数据库+Lw文档)
  • 域权限维持-黄金/白银票据详解
  • 什么是黄金投资及其特点
  • Kerberos的黄金票据详解
  • RTI Perftest 的大样本测试
  • 精密系统的实用RTI计算
  • 雷诺-日产-三菱汽车联盟(Renault-Nissan-Mitsubishi)旗下的联盟风险投资公司(Alliance Ventures) 领投Enevate的战略融资, 用以进一步推动应用于电动车的
  • 雷诺EZ-Go自动驾驶新型概念车亮相,采用全透明设计
  • JSP与汤姆猫Tomcat环境搭建及基础认识
  • 2021年龙华区产业发展专项资金申报条件及资助,补贴2000万
  • 安全证书上的名称无效或者与站点名称不匹配 的原因 -SSL和CA基础知识
  • 问题九-谷歌浏览器中无法实现查询功能
  • 如何使用 PHP 和 MySQL 创建分页
  • 如何使用 PHP PDO和 MySQL 创建分页
  • DHTMLX To Do List 1.0 亮点功能:高级任务分配、内联编辑、便捷的键盘导航
  • 如何使用 PHP 和 MySQL 对表列进行排序
  • Result of orbslam2 orbslam3 on EuRoC dataset
  • 《论文笔记》ORB-SLAM3:An Accurate Open-Source Library for Visual, Visual-Inertial and Multi-Map SLAM》
  • 卷积核的kernel size为什么要设置为奇数
  • 电子签名手机-vue
  • 企业级敏捷转型系列专题之四 —— 企业转型
  • 《Google Cloud 助力企业转型》线上课程限时免费
  • 聚合数据企业工商服务上线,赋能传统企业转型升级
  • 订单多?盈利难?新零售时代,订单管理系统助力传统企业转型升级
  • 传统企业通过物联网转型升级,主要分为哪几步?