如何开发手机端网站,云设计,dux主题4.0 wordpress,手机网站建站软件当我们开发个人项目的时候#xff0c;为了用户登录的便捷性#xff0c;经常会给我们的项目加上一些除了注册之外的方式#xff0c;其中最常见的就是微信登录#xff0c;但作为个人开发者#xff0c;是无法使用微信的授权登录的#xff0c;但是通过微信公众号可以获得同样… 当我们开发个人项目的时候为了用户登录的便捷性经常会给我们的项目加上一些除了注册之外的方式其中最常见的就是微信登录但作为个人开发者是无法使用微信的授权登录的但是通过微信公众号可以获得同样的结果。 看完这篇文章你能得到什么
引入微信公众号开发微信公众号开发比较容易只要入门后面根据开发文档操作即可为你的项目加上微信登陆功能学会如何阅读文档文章中标识了这个模块具体在哪个章节可以先去尝试自己学习开发掌握一些开发的思想
实现思路
微信公众号为我们提供了带信息的二维码的方式通过这个二维码我们可以让用户去关注我们的公众号微信为我们提供了一个可以获取到用户头像等基本信息的方法用户点击链接授权后我们为他注册账号然后返回登录密钥我们将这个密钥作为键存入我们的 redis设置过期时间将用户的 openId 作为值当用户输入正确的密钥的时候后端就能拿到这个密钥对应的 openId记录用户的登录态完成登录功能
前期准备
注册公众号
我们首先需要去注册一个属于我们的公众号微信公众平台https://mp.weixin.qq.com/ 注意注册类型一定要选择订阅号服务号需要企业身份而且选择后是无法更改的一个邮箱只能绑定一种账号)选择的时候务必注意跟着走完注册流程后我们就获得了自己的公众号。 为了开发的便捷性我们使用开发者工具中提供的公众平台的测试号来尝试实验测试号省去了一些繁琐的部分我们开发完成后将代码上线后再去改回使用我们的公众号即可。
内网穿透工具
公众号开发是需要向我们后端发送请求的我们不可能再服务器上进行代码开发为了本地开发的便捷这里使用内网穿透工具来让微信服务器可以访问我们本地的端口。 这里我们选择一个免费的内网穿透工具提供的免费服务供给我们开发足够了 netapphttps://natapp.cn/根据系统的提示下载即可配置参考官方给的图文即可https://natapp.cn/article/natapp_newbie唯一需要注意的点就是这个 config.ini 文件的编写 我们将这两个文件放在同一个目录下直接启动即可启动后的界面是这样
开发者文档
文章后面括号中会标识这个操作对应的开发者文档中的哪个模块方便大家进行学习和查阅我们可以再开发者工具中很容易的找到开发者文档
开始开发
接入微信公众平台开发
首先我们打开我们的微信测试号填写接口配置信息微信服务器发送请求的时候会向我们填写的 url 发送请求有了上面的内网穿透工具微信服务器已经可以发送请求到我们的内网。 将想要微信服务器访问到的组件粘贴到接口配置信息中这里我们使用 /wx 来接收所有微信发送的请求。
RequestMapping(/wx)
Slf4j
RestController
CrossOrigin(origins http://localhost:8000, allowCredentials true)
public class WxController {/*** 微信 url 验证接口*/GetMapping(/)public String wxCheck(RequestParam(signature) String signature,RequestParam(timestamp) Integer timestamp,RequestParam(nonce) String nonce,RequestParam(echostr) String echostr) {log.info(开始验证 url signature{}timestamp{}nonce{}echostr{}, signature,timestamp, nonce, echostr);//TODO:验证是微信发送的return echostr;
}这时候微信会向我们填写的地址发送一个 GET 请求同时会附带上面写的参数我们接收到这个信息后需要将随机数作为返回值返回给微信服务器才能通过验证**开始开发 / 接入指南**微信发送给我们的参数为 通过开发文档提供的方式可以验证这个消息是否来自微信这里先不提供具体的实现方法。
开发带参数的二维码
想要实现扫码登录需要一个能导航到我们公众号的二维码二维码开发的具体信息在**账号管理 / 生成带参数的二维码**简单来说就是我们拿我们的 accessToken 和我们希望二维码中携带的信息去向微信服务器换取一个 ticket再通过这个 ticket 去获得我们的二维码图片地址。
获取 AccessToken access_toke n是公众号的全局唯一接口调用凭据公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。 关于获取 AccessToken 的具体流程在**开始开发 / 获取 Access token**我们发送 GET 请求到这个地址 https请求方式: GET https://api.weixin.qq.com/cgi-bin/token?grant_typeclient_credentialappidAPPIDsecretAPPSECRET 注意将参数改为自己的就能获取到类似于这样的 JSON 字符串
{access_token:ACCESS_TOKEN,expires_in:7200}示例实现方法 我在项目中是封装了一个 WxUtil 作为工具类来存放这些方法发送请求是通过 HuTool 的 HttpUtil这里就不再赘述使用方法可以当作参考 /*** 获取 accessToken*/
public static String getAccessToken() throws JsonProcessingException {String url https://api.weixin.qq.com/cgi-bin/token?grant_typeclient_credential appid secret;HttpResponse execute HttpUtil.createGet(url).execute();String body execute.body();ObjectMapper objectMapper new ObjectMapper();// 将 JSON 字符串转化为 MapMapString, Object stringObjectMap objectMapper.readValue(body,new TypeReference(){});return (String)stringObjectMap.get(access_token);
}这里的序列化工具采用的是 Jackson
dependencygroupIdcom.fasterxml.jackson.dataformat/groupIdartifactIdjackson-dataformat-xml/artifactIdversion2.9.8/version
/dependency获取带参数的二维码
接下来让我们再次回到主线上来继续开发带参数的二维码参考官方文档的说明 临时二维码请求说明 http请求方式: POST URL: https://api.weixin.qq.com/cgi-bin/qrcode/create?access_tokenTOKEN POST数据格式json POST数据例子{“expire_seconds”: 604800, “action_name”: “QR_SCENE”, “action_info”: {“scene”: {“scene_id”: 123}}} 或者也可以使用以下POST数据创建字符串形式的二维码参数{“expire_seconds”: 604800, “action_name”: “QR_STR_SCENE”, “action_info”: {“scene”: {“scene_str”: “test”}}} 永久二维码请求说明 http请求方式: POST URL: https://api.weixin.qq.com/cgi-bin/qrcode/create?access_tokenTOKEN POST数据格式json POST数据例子{“action_name”: “QR_LIMIT_SCENE”, “action_info”: {“scene”: {“scene_id”: 123}}} 或者也可以使用以下POST数据创建字符串形式的二维码参数 {“action_name”: “QR_LIMIT_STR_SCENE”, “action_info”: {“scene”: {“scene_str”: “test”}}} 这里我们获取的是临时的二维码用户如果长时间不扫码则二维码会失效我们需要构造一个 Post 请求将微信文档中需要参数传递过去下面给出我的示例代码 public static String getTemporaryQRCode(Boolean isTemplate, Boolean isSTR) throws JsonProcessingException {HashMapString, Object map new HashMap();//获取临时二维码//该二维码有效时间以秒为单位。//最大不超过2592000即30天此字段如果不填则默认有效期为60秒。map.put(expire_seconds, 120);if (isTemplate) {//二维码类型QR_SCENE为临时的整型参数值,QR_STR_SCENE为临时的字符串参数值// QR_LIMIT_SCENE为永久的整型参数值,QR_LIMIT_STR_SCENE为永久的字符串参数值if (isSTR) {map.put(action_name, QR_STR_SCENE);}else {map.put(action_name, QR_SCENE);}} else {if (isSTR) {map.put(action_name, QR_LIMIT_STR_SCENE);} else {map.put(action_name, QR_LIMIT_SCENE);}}// 构建 actionInfo内嵌HashMapString, Object actionInfo new HashMap();HashMapString, Object scene new HashMap();scene.put(scene_id, 1);actionInfo.put(scene, scene);map.put(action_info, actionInfo);//发送请求来获取 ticketString ticket getTicket(map);// 通过 ticket 来获取到二维码的地址String encode URLEncoder.encode(ticket);String codeUrl https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket encode;return codeUrl;}这段代码中我需要传入两个参数来判断请求的是否是临时的二维码场景值 ID 是否是字符串通过判断语句来修改 POST 参数的内容这里可以采用直接编写 JSON 字符串的方式实现但如果想通过 Map 来实现的话需要注意请求的参数中有一个内嵌的 JSON 对象。 通过这个方法我们就能获取到我们二维码的 URL这个二维码能干什么呢
接收带参数的二维码
我们将这些信息封装到二维码后用户扫码关注我们的公众号的时候就会将这些信息同时提交给我们的服务器**基础消息能力 / 接收事件推送**当用户扫码后可以有两种事件推送已经关注公众号和未关注但通过我们提供的二维码扫码关注两种事件。
xmlToUserName![CDATA[toUser]]/ToUserNameFromUserName![CDATA[FromUser]]/FromUserNameCreateTime123456789/CreateTimeMsgType![CDATA[event]]/MsgTypeEvent![CDATA[SCAN]]/EventEventKey![CDATA[SCENE_VALUE]]/EventKeyTicket![CDATA[TICKET]]/Ticket
/xml xmlToUserName![CDATA[toUser]]/ToUserNameFromUserName![CDATA[FromUser]]/FromUserNameCreateTime123456789/CreateTimeMsgType![CDATA[event]]/MsgTypeEvent![CDATA[subscribe]]/EventEventKey![CDATA[qrscene_123123]]/EventKeyTicket![CDATA[TICKET]]/Ticket
/xml这两个事件仅有 Event 参数不同我们可以通过获取这个参数来判断用户是否是第一次关注公众号
处理信息构建授权地址
我们在二维码的参数中设置一个标识登录的参数当我们接收到这个参数的时候就执行登录的逻辑同时为这个用户构建一个登录的密钥将密钥和用户的 openId 作为一个键值对存储在 Redis 数据库中同时将密钥返回给用户。 需要获取当用户的具体信息的时候就需要学习公众号开发的具体授权**微信网页开发 / 网页授权**
用户同意授权获取到用户的 code 请求地址 https://open.weixin.qq.com/connect/oauth2/authorize?appidAPPIDredirect_uriREDIRECT_URIresponse_typecodescopeSCOPEstateSTATE#wechat_redirect 参考示例 https://open.weixin.qq.com/connect/oauth2/authorize?appidwx807d86fb6b3d4fd2redirect_urihttp%3A%2F%2Fdevelopers.weixin.qq.comresponse_typecodescopesnsapi_userinfostateSTATE#wechat_redirect 这个地址的逻辑是用户访问这个地址后会请求授权用户同意授权后会跳转到我们指定的回调界面我这里希望在回调界面中为用户提供他们的登录密钥。 设置回调接口 找到测试公众号界面中的这个修改按钮来指定回调的地址的域名注意不要带 http微信会检测我们的回调地址是否在这个域名下如果在就无法实现跳转功能。
返回授权地址
当用户扫码进入我们的公众号且验证事件信息后发现是登录请求的时候就向他提供我们的授权地址 代码示例 下面提供我实现的方式仅供参考
/*** 接收消息并自动回复的功能*/
PostMapping(/)
public String receiveMessage(HttpServletRequest request) throws IOException, DocumentException {ServletInputStream inputStream request.getInputStream();// 将传来的信息封装到 map 中HashMapString, String map new HashMap();SAXReader saxReader new SAXReader();Document read saxReader.read(inputStream);Element rootElement read.getRootElement();ListElement elements rootElement.elements();for (Element element : elements) {map.put(element.getName(), element.getStringValue());}log.info(开始返回信息,请求参数{}, map);// 拿到用户的 OpenIdString openId map.get(FromUserName);// 验证是扫码登录且事件值是1 || (map.get(Event).equals(subscribe))//TODO给事件值封装一个常量类if ((map.get(Event).equals(subscribe) || map.get(Event).equals(SCAN))) {//String token wxService.WxLogin(openId);String url https://open.weixin.qq.com/connect/oauth2/authorize? appid redirect_urihttp://pab2mw.natappfree.cc/wx/wxCallBack response_typecode scopesnsapi_userinfo stateSTATE#wechat_redirect;return WxUtil.getReplyMessage(map, 请跳转到授权网址 url);}return ;先从 request 中接收到提供的参数再去验证这个请求是否为登录请求再将我们前面构造的回调地址传回去这里需要学习被动回复用户信息**基础消息能力 / 被动回复用户信息**这里我只解析这个回复文本消息的能力 被动回复用户信息 我们需要在 return 中返回一个 XML 格式的字符串
xmlToUserName![CDATA[toUser]]/ToUserNameFromUserName![CDATA[fromUser]]/FromUserNameCreateTime12345678/CreateTimeMsgType![CDATA[text]]/MsgTypeContent![CDATA[你好]]/Content
/xml这是给出的字符串的示例其中 ToUserName 和 FromUSerName 都可以通过请求的 XML 拿取我们只需要指定创建时间和回复的具体消息即可。
public static String getReplyMessage(MapString, String map, String message) throws JsonProcessingException {// 设置基础的信息TestMessage testMessage new TestMessage();testMessage.setFromUserName(map.get(ToUserName));testMessage.setToUserName(map.get(FromUserName));testMessage.setMsgType(text);testMessage.setCreateTime(System.currentTimeMillis() / 1000);//设置需要返回给用户的信息testMessage.setContent(message);// 利用 JackSon 实现序列化return JsonUtil.getXmlString(testMessage);
}这个方法我也封装到了 WxUtil 这个类中可以作为参考序列化库使用的 Jackson导入方式上面已经讲过了。
拉取用户信息
当用户授权成功后微信会调用我们提供的回调地址并且将 code 传递过来比如我们上面指定的 /wxcallback我们在这个回调接口中实现注册和密钥的返回。 我们拿到用户的 code 就可以向微信请求用户的具体信息了首先要请求获取一个 accessToken这和我们上面讲到的 accessToken 不是同一种是用于网页授权的 accessToken。 我们请求这个地址 获取code后请求以下链接获取access_token https://api.weixin.qq.com/sns/oauth2/access_token?appidAPPIDsecretSECRETcodeCODEgrant_typeauthorization_code 正确返回的 JSON 对象
{access_token:ACCESS_TOKEN,expires_in:7200,refresh_token:REFRESH_TOKEN,openid:OPENID,scope:SCOPE,is_snapshotuser: 1,unionid: UNIONID
}通过这个 accessToken 我们就可以获取到用户的信息了 如果网页授权作用域为snsapi_userinfo则此时开发者可以通过access_token和openid拉取用户信息了。 httpGET请使用https协议 https://api.weixin.qq.com/sns/userinfo?access_tokenACCESS_TOKENopenidOPENIDlangzh_CN 向这个地址发送请求信息就能得到用户的详细信息
{ openid: OPENID,nickname: NICKNAME,sex: 1,province:PROVINCE,city:CITY,country:COUNTRY,headimgurl:https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46,privilege:[ PRIVILEGE1 PRIVILEGE2 ],unionid: o6_bmasdasdsad6_2sgVt7hMZOPfL
}返回的数据是这样的 下面给出我实现的代码示例仅供参考
/*** 获取用户详细信息*/
public static MapString, Object getUserMes(String pageAccessToken, String openId) throws JsonProcessingException {HttpResponse execute HttpUtil.createGet(https://api.weixin.qq.com/sns/userinfo? access_token pageAccessToken openidOPENID openId langzh_CN).execute();String body execute.body();return JsonUtil.getParamMap(body);
}完成注册逻辑返回密钥
Controller 层 RequestMapping(/wxCallBack)ResponseBodypublic String wxCallBack(String code) throws JsonProcessingException {MapString, Object pageMap WxUtil.getPageMap(code);String pageAccessToken (String) pageMap.get(access_token);String openId (String) pageMap.get(openid);String token wxService.WxLogin(pageAccessToken, openId);return 你的登录密钥为 token;}Service 层
Overridepublic String WxLogin(String pageAccessToken, String openId) throws JsonProcessingException {// 先去数据库中查询是否有 openId 如果有则表明已经注册QueryWrapperUser queryWrapper new QueryWrapper();queryWrapper.eq(mpOpenId, openId);User one userService.getOne(queryWrapper);String token TokenUtil.generateShortToken();String key LoginToken token;if (one null) {// 用户还没有注册调用注册方法MapString, Object userMes WxUtil.getUserMes(pageAccessToken, openId);User user new User();// 保存用户的 unionId 和 openIduser.setMpOpenId(openId);user.setUnionId((String) userMes.get(unionid));user.setUserName((String)userMes.get(nickname));user.setUserAvatar((String)userMes.get(headimgurl));//TODO:完成 API 调用的 accessKey 的获取boolean save userService.save(user);if (!save) {throw new BusinessException(400, 系统内部错误);}}// 在 Redis 中存储密钥信息redisService.setExpireString(key, openId, 120, TimeUnit.SECONDS);return token;}在业务层实现了注册的业务并且设置了向 Redis 中设置密钥。
用户输入密钥执行登录逻辑
这时候用户就能在授权地址中看到我们的 token在前端界面输入 token验证后即可完成登录逻辑。 GetMapping(/verifyToken)public BaseResponseUser verifyToken(String wxToken, HttpServletRequest request) {log.info(接收到的 token 为{}, wxToken);String key LoginToken wxToken;String openId redisService.getString(key);if (openId null) {throw new BusinessException(ErrorCode.OPERATION_ERROR, 密钥错误);}QueryWrapperUser wrapper new QueryWrapper();wrapper.eq(mpOpenId, openId);User one userService.getOne(wrapper);request.getSession().setAttribute(USER_LOGIN_STATE,one);return ResultUtils.success(one);}具体根据自己的业务调整这只是给出一个实例。
小结
这样就完成了微信登录功能的开发当然还有很多其他的实现方式这个方式或许不是最好的也存在一些问题比如用户恰好输成别人的密钥就会导致登录到别人的账号可以后期调整当然用户授权后可以将信息保存到 session 中前端轮询查看这个 session 是否有登录数据查询后完成登录跳转。。。 总之实现的方法有很多希望这篇文章能为大家带来启发。