图片物联网软件开发公司
作家:小傅哥博客:https://bugstack.cn
❝千里淀、共享、成长,让我方和他东谈主齐能有所得益!😜
❞人人好,我是本事UP主小傅哥。
动作一个本事码农,在使用社区、论坛有时各类AI职业的时,时时会看到这样一个领导:“使用微信公众号扫码登录”。那因为这种的登录神气除了登录,还不错让用户千里淀到公众号上,以后还能汲取到公众号彭胀,可谓是一举两得。那它是怎样作念的呢?🤔
图片
小傅哥,先举个这样登录的例子🌰,让人人熟练下这个业务场景。
这是一个 CSDN 微信扫码登录的场景,通过 F12 掀开浏览器的间隔台,不错看到不停的请求一个收集地址,判断用户是否扫码。当你使用微信扫码后,则会登录生效跳转到网站的首页。
图片
通过这样的一个页面成果展示,咱们自便的不错知谈,用户页面不停的 checkScan 检测,是需要用到一个独一ID值。而当用户用微信扫码后,这个独一ID值则不错通过微信公众号获取到并保存,同期创建出独一ID 和 Token 的映射关系。那么当 checkScan 扫描到职业端有这样一个映射,则不错把 Token 取转头存到浏览器中,让用户登录生效。
经由即是这样,那具体的代码终了是如那儿分的呢?接下来小傅哥就给人人共享下,怎样来终了一下这个决议。
文末提供了「星球:码农会锁」🧧优惠加入神气,以及本节课程的代码地址。技俩演示地址:https://gaga.plus - 8个实战技俩
也是徐灿2021年7月31日,在英国布伦特伍德,丢掉WBA世界羽量级金腰带后,中国时隔3年,再次有拳手挑战世界四大组织的世界头衔。
武汉三镇多名球员就欠薪向中国足球和国际足联提出仲裁:
一、经由野心微信扫码登录的经由主要包括;用户、浏览器、后端职业、公众号,这四个部分。咱们不错先通过UML经由图,了解下悉数调用关系。
图片
率先,由用户发起登录操作。让WEB页面从职业端获取登录凭证。之后,前端页面拿到登录凭证后,不错使用 Ticket 从公众号职业平台交流二维码。临了,用户扫码登录。扫码后,职业端会汲取到来自公众号的回调音尘,职业端再把回调音尘中的 openid【用户独一标志】和 ticket 进行绑定。这个期间你也不错创建出 jwt token 反馈给前端,动作登录生效的存储信息,后续校验 jwt token 就不错了。有了这样一个经由的意会,接下来,咱们就不错看下代码是怎样终了的了。
二、对接文档 - 公众号平台微信公众号测试平台:https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index - 不需要央求公众号即可完成测试,雷同沙箱环境获取 Access Token 文档:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html生成带参数的二维码:https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html - 最终即是用户扫描的二维码内网穿透用具,natapp.cn - 因为需要让公众号调用到土产货的职业,所需要把你的职业映射到公网上使用。闪耀;要遴荐付费的12元,不然不成对接。三、功能终了小傅哥这里接受了 DDD 的工程模子结构,建造公众号扫码登录职业端案例。如果你对 DDD 还不是太熟练,不错看下小傅哥写的系列 DDD 教程;《Java 简明教程》-> bugstack.cn -> 路书
小程序开发1. 工程结构图片
xfg-dev-tech-app 是启动期骗门径的进口,其他模块也被径直有时障碍的引入到 app 模块下,这样才能被 Spring 扫描加载。xfg-dev-tech-infrastructure 是基础规范层,用于对接外部接口、缓存、数据库等联系内容的连结使用。本节主若是对接微信建造平台的接口。接受的是 retrofit2 本事框架,这样对接起来愈加便捷。xfg-dev-tech-domain 是功能终了层,像是登录的具体终了,即是在 domain 边界层终了的。你来日使用 DDD 作念的其他功能,物联网软件物联网软件开发多少钱亦然放到 domain 边界下终了,每一个功能即是即是一个模块。xfg-dev-tech-types 用于界说基本的类型、成列、罪行码等内容。2. 二维码获取从微信官网文档阅读不错知谈,为了获取扫码登录的二维码,则需要3步;
先获取 AccessToken,它是公众号的全局独一接口调用左证,公众号调用各接口时齐需使用access_token。通过 AccessToken 获取 ticket 凭证,凭证用于衔尾用户扫码登录和公众号回调后获取凭证,以此关联用户登录信息。通过 ticket 传递给前端,前端页面拜访微信地址径直获取二维码。2.1 接口对接 - retrofit2public interface IWeixinApiService { /** * 获取 Access token * 文档:<a href="https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html">Get_access_token</a> * * @param grantType 获取access_token填写client_credential * @param appId 第三方用户独一凭证 * @param appSecret 第三方用户独一凭证密钥,即appsecret * @return 反应间隔 */ @GET("cgi-bin/token") Call<WeixinTokenResponseDTO> getToken( @Query("grant_type") String grantType, @Query("appid") String appId, @Query("secret") String appSecret ); /** * 获取左证 ticket * 文档:<a href="https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html">Generating_a_Parametric_QR_Code</a> * <a href="https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET">前端根据凭证展示二维码</a> * * @param accessToken getToken 获取的 token 信息 * @param weixinQrCodeRequestDTO 入参对象 * @return 应酬间隔 */ @POST("cgi-bin/qrcode/create") Call<WeixinQrCodeResponseDTO> createQrCode(@Query("access_token") String accessToken, @Body WeixinQrCodeRequestDTO weixinQrCodeRequestDTO);}使用 retrofit2 对接接口,它不错以一种面向对象的想维,使用 HTTP 接口,免去我方处分中间的对接过程。另外 okhttp3 框架对接接口也非常好用,有的期间不错联络通盘使用。
@Slf4j@Configurationpublic class Retrofit2Config { private static final String BASE_URL = "https://api.weixin.qq.com/"; @Bean public Retrofit retrofit() { return new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(JacksonConverterFactory.create()) .build(); } @Bean public IWeixinApiService weixinApiService(Retrofit retrofit) { return retrofit.create(IWeixinApiService.class); }}使用 retrofit2 建造好接口后,在再 xfg-dev-tech-app 模块的 config 文献夹下,创建职业。【这有点像 MyBatis 的 Dao 接口同样,只需要界说好接口即可】2.2 ApiPost 请求
接口:https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET
APIPost 模拟网页取得的扫码登录的二维码。接下来门径到测试的期间,产生的 ticket 会放到这里模拟使用。3. 登录码建造
源码:cn.bugstack.xfg.dev.tech.trigger.http.LoginController
@Slf4j@RestController()@CrossOrigin("*")@RequestMapping("/api/v1/login/")public class LoginController { @Resource private ILoginService loginService; @RequestMapping(value = "weixin_qrcode_ticket", method = RequestMethod.GET) public Response<String> weixinQrCodeTicket() { try { String qrCodeTicket = loginService.createQrCodeTicket(); log.info("生成微信扫码登录 ticket {}", qrCodeTicket); return Response.<String>builder() .code(Constants.ResponseCode.SUCCESS.getCode()) .info(Constants.ResponseCode.SUCCESS.getInfo()) .data(qrCodeTicket) .build(); } catch (Exception e) { log.info("生成微信扫码登录 ticket 失败", e); return Response.<String>builder() .code(Constants.ResponseCode.UN_ERROR.getCode()) .info(Constants.ResponseCode.UN_ERROR.getInfo()) .build(); } } @RequestMapping(value = "check_login", method = RequestMethod.GET) public Response<String> checkLogin(@RequestParam String ticket) { try { String openidToken = loginService.checkLogin(ticket); log.info("扫描检测登录间隔 ticket:{} openidToken:{}", ticket, openidToken); if (StringUtils.isNotBlank(openidToken)) { return Response.<String>builder() .code(Constants.ResponseCode.SUCCESS.getCode()) .info(Constants.ResponseCode.SUCCESS.getInfo()) .data(openidToken) .build(); } else { return Response.<String>builder() .code(Constants.ResponseCode.NO_LOGIN.getCode()) .info(Constants.ResponseCode.NO_LOGIN.getInfo()) .build(); } } catch (Exception e) { log.info("扫描检测登录间隔失败 ticket:{}", ticket); return Response.<String>builder() .code(Constants.ResponseCode.UN_ERROR.getCode()) .info(Constants.ResponseCode.UN_ERROR.getInfo()) .build(); } }}
建造两个接口;
/api/v1/login/weixin_qrcode_ticket - 获取微信 ticket 凭证/api/v1/login/check_login - 轮训考证登录4. 公众号建造率先,唯独作念公众号建造的经由,就必须有公众号的对接。这个对接即是你在我方按照公众号文档建造好对接门径,确立到公众号平台。
4.1 确立阐发图片
如图所示,是你在登录微信公众号测试平台,添加接口确立和JS安全域名以后看到的内容。
最顶上,微信号,需要确立到 xfg-dev-tech-weixin-login 的 application-dev.yml 文献中。测试号信息 appID、appsecret,也需要确立到 application-dev.yml 文献中。接口信息委果立,需要你在启动 xfg-dev-tech-weixin-login,同期在土产货测试时启动 natapp 内网穿透用具后。用你的内网穿透地址,和工程的请求地址的 URL 确立到公众号接口里。确立的期间会进行验签,验签生效则确立生效。你还要扫描眷注测试号二维码,这样才能看到测试信息。4.2 验签职业源码:cn.bugstack.xfg.dev.tech.trigger.http.WeixinPortalController
@Slf4j@RestController()@CrossOrigin("*")@RequestMapping("/api/v1/weixin/portal/")public class WeixinPortalController { @Value("${weixin.config.originalid}") private String originalid; @Resource private Cache<String, String> openidToken; /** * 验签,硬编码 token b8b6 - 按需修改 */ @GetMapping(value = "receive", produces = "text/plain;charset=utf-8") public String validate(@RequestParam(value = "signature", required = false) String signature, @RequestParam(value = "timestamp", required = false) String timestamp, @RequestParam(value = "nonce", required = false) String nonce, @RequestParam(value = "echostr", required = false) String echostr) { try { log.info("微信公众号验签信息开动 [{}, {}, {}, {}]", signature, timestamp, nonce, echostr); if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) { throw new IllegalArgumentException("请求参数行恶,请核实!"); } boolean check = SignatureUtil.check("b8b6", signature, timestamp, nonce); log.info("微信公众号验签信息完成 check:{}", check); if (!check) { return null; } return echostr; } catch (Exception e) { log.error("微信公众号验签信息失败 [{}, {}, {}, {}]", signature, timestamp, nonce, echostr, e); return null; } } /** * 回调,汲取公众号音尘【扫描登录,会汲取到音尘】 */ @PostMapping(value = "receive", produces = "application/xml; charset=UTF-8") public String post(@RequestBody String requestBody, @RequestParam("signature") String signature, @RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce, @RequestParam("openid") String openid, @RequestParam(name = "encrypt_type", required = false) String encType, @RequestParam(name = "msg_signature", required = false) String msgSignature) { try { log.info("汲取微信公众号信息请求{}开动 {}", openid, requestBody); // 音尘调度 MessageTextEntity message = XmlUtil.xmlToBean(requestBody, MessageTextEntity.class); // 扫码登录【音尘类型和事件】 if ("event".equals(message.getMsgType()) && "SCAN".equals(message.getEvent())) { // 骨子的业务场景,不错生成 jwt 的 token 让前端存储 openidToken.put(message.getTicket(), openid); return buildMessageTextEntity(openid, "登录生效"); } log.info("汲取微信公众号信息请求{}完成 {}", openid, requestBody); return buildMessageTextEntity(openid, "测试本案例,需要请扫码登录!"); } catch (Exception e) { log.error("汲取微信公众号信息请求{}失败 {}", openid, requestBody, e); return ""; } } private String buildMessageTextEntity(String openid, String content) { MessageTextEntity res = new MessageTextEntity(); // 公众号分派的ID res.setFromUserName(originalid); res.setToUserName(openid); res.setCreateTime(String.valueOf(System.currentTimeMillis() / 1000L)); res.setMsgType("text"); res.setContent(content); return XmlUtil.beanToXml(res); }}验签和汲取公众号回调,是一个固定的代码,同期验签和汲取公众号回调也齐是吞并个接口名字,仅仅一个是 get 请求,另外一个是 post 请求。验签地址:http://xfg-studio.natapp1.cc/api/v1/weixin/portal/receive 你需要更换为你的内网穿透域名地址。在汲取公众号回调中,有一块固定的代码。汲取公众号音尘类型为事件,事件类型为扫码(SCAN),从中不错取得 ticket 这个独一凭证。考证登录时,简便模拟写入到缓存中。openidToken.put(message.getTicket(), openid); 骨子的业务场景会调度为登录的 jwt token 数据。4.3 内网穿透
图片
内网穿透用具,购买一个12元的付费地谈。https://natapp.cn/购买后,确立你的地谈土产货端口为 8091 也即是你土产货 SpringBoot 门径的端口。如果你不是 8091 端口,不错修改为其他的软件下载,内网穿透需要一个土产货的软件。你不错从它的网站下载。https://natapp.cn/#download 各个版块也齐撑捏,内部也有联系的使用教程。安设软件后,启动 natapp 和期骗,就不错把你的地址确立到上头了。四、功能考证1. 启动 SpringBoot 职业. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ / _` | \ \ \ \ \/ ___)| |_)| | | | |