最新要闻
- 全球快报:小米卢伟冰:小米13 Ultra在意法西德及香港地区正式开售 销售超预期
- 首发预装鸿蒙OS 4.0!华为Mate60 Pro概念图出炉 全球热资讯
- 天天精选!NASA决定造访遍地黄金的“灵神星”:平均每位美国人能分300亿美元
- 美系硬派SUV福特探险者谍照曝光!外观内饰全面升级 预计将在年内首次亮相_今日热讯
- 今日热搜:8GB显卡卖到3199元 显存成本曝光:英伟达实在太赚了
- 视点!男生抠掉脸上痘痘流血近1小时:用了一包400张抽纸
- 每日报道:6月13日华鲁恒升尿素价格暂稳
- 烟台大学附属中学石明校区举行垃圾分类科普讲座|天天时快讯
- 速递!8GB内存笔记本卖到10499元起 苹果被批吃相难看:应该破发
- 货车高速上连续疯狂别车被撞停 官方:未造成伤亡、已找到肇事者|当前播报
- 天天热讯:马斯克相中的男人!14岁成SpaceX最年轻工程师、岗位年薪百万
- 环球即时看!小米平板6 Pro两个月使用心得:找不到短板的安卓板皇
- Arm发布全新智能视觉参考设计 首次整合第三方IP核心
- 环球今日报丨哥伦比亚4名空难获救儿童的母亲生前或遭家暴,孩子外公和父亲欲争夺抚养权
- 当前关注:你收益多少?余额宝上线第十年:每天为国人赚1亿零花钱 网友狂晒单
- 排队5小时!浙江网红面包黄牛加价上百元 网友吐槽:消协回应
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
每日速看!尚医通-day10【微信扫码登录】(内附源码)
第01章-准备工作
1、申请微信登录
https://open.weixin.qq.com
(1)注册开发者账号:准备营业执照
(2)邮箱激活
【资料图】
(3)完善开发者资料
(4)开发者资质认证:1-2个工作日审批、300元
(5)创建网站应用:提交审核,7个工作日审批(免费)
(6)熟悉微信登录流程
参考文档:https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
2、OAuth2.0简介
OAuth 2.0
是目前最流行的授权机制,用来授权第三方应用,获取用户数据。
OAuth 2.0 一词中的 “Auth” 表示 “授权”,字母 “O” 是 Open 的简称,表示 “开放” ,连在一起就表示 “开放授权”。这也是为什么我们使用 OAuth 的场景,通常发生在开放平台的环境下。
越来越多的第三方应用都在向用户提供使用微信登录的解决方案,来减少用户注册的繁琐操作。而这个解决方案的背后原理,也是我们要讲到的 OAuth 2.0 技术。
2.1、一个简单解释
快递员问题
我住在一个大型的居民小区。
小区有门禁系统。
进入的时候需要输入密码。
我经常网购和外卖,每天都有快递员来送货。我必须找到一个办法,让快递员通过门禁系统,进入小区。
如果我把自己的密码,告诉快递员,他就拥有了与我同样的权限,这样好像不太合适。万一我想取消他进入小区的权力,也很麻烦,我自己的密码也得跟着改了,还得通知其他的快递员。
有没有一种办法,让快递员能够自由进入小区,又不必知道小区居民的密码,而且他的唯一权限就是送货,其他需要密码的场合,他都没有权限?
授权机制的设计
于是,我设计了一套授权机制。
第一步,门禁系统的密码输入器下面,增加一个按钮,叫做"获取授权"。快递员需要首先按这个按钮,去申请授权。
第二步,他按下按钮以后,屋主(也就是我)的手机就会跳出对话框:有人正在要求授权。系统还会显示该快递员的姓名、工号和所属的快递公司。
我确认请求属实,就点击按钮,告诉门禁系统,我同意给予他进入小区的授权。
第三步,门禁系统得到我的确认以后,向快递员显示一个进入小区的令牌(access token)。令牌就是类似密码的一串数字,只在短期内(比如七天)有效。
第四步,快递员向门禁系统输入令牌,进入小区。
有人可能会问,为什么不是远程为快递员开门,而要为他单独生成一个令牌?这是因为快递员可能每天都会来送货,第二天他还可以复用这个令牌。另外,有的小区有多重门禁,快递员可以使用同一个令牌通过它们。
互联网场景
我们把上面的例子搬到互联网,就是 OAuth
的设计了。
首先,居民小区就是储存用户数据的网络服务。比如,微信储存了我的好友信息,获取这些信息,就必须经过微信的"门禁系统"。
其次,快递员(或者说快递公司)就是第三方应用
,想要穿过门禁系统,进入小区。
最后,我就是用户本人,同意授权第三方应用进入小区,获取我的数据。
简单说,OAuth 就是一种授权机制。数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据。系统从而产生一个短期的进入令牌(token),用来代替密码,供第三方应用使用。
令牌与密码
令牌(token)与密码(password)的作用是一样的,都可以进入系统,但是有三点差异。
(1)令牌是短期的,到期会自动失效,用户自己无法修改。密码一般长期有效,用户不修改,就不会发生变化。
(2)令牌可以被数据所有者撤销,会立即失效。以上例而言,屋主可以随时取消快递员的令牌。密码一般不允许被他人撤销。
(3)令牌有权限范围(scope),比如只能进小区的二号门。对于网络服务来说,只读令牌就比读写令牌更安全。密码一般是完整权限。
上面这些设计,保证了令牌既可以让第三方应用获得权限,同时又随时可控,不会危及系统安全。这就是 OAuth 2.0 的优点。
注意,只要知道了令牌,就能进入系统。系统一般不会再次确认身份,所以令牌必须保密,泄漏令牌与泄漏密码的后果是一样的。这也是为什么令牌的有效期,一般都设置得很短的原因。
2.4、OAuth2的四种方式
OAuth 的核心就是向第三方应用颁发令牌,OAuth 2.0 的标准是 RFC 6749 文件。由于互联网有多种场景,标准中定义了获得令牌的四种授权方式(authorization grant ):
- 授权码(authorization-code)
- 隐藏式(implicit)
- 密码式(password)
- 客户端凭证(client credentials)
注意,不管哪一种授权方式,第三方应用申请令牌之前,都必须先到系统备案,说明自己的身份,然后会拿到两个身份识别码:客户端 ID(AppID)
和客户端密钥(AppSecret)
。这是为了防止令牌被滥用,没有备案过的第三方应用,是不会拿到令牌的。
参考文档:https://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html
2.5、微信登录获取令牌时序图
微信登录使用了授权码方式
第02章-显示登录二维码
1、创建用户微服务
1.1、创建数据库
资料:资料>微信扫码登录>guigu_syt_user.sql
1.2、创建service-user微服务
1.3、添加依赖
在service-user中添加依赖:
com.atguigu model 1.0 com.atguigu service-util 1.0 com.atguigu spring-security 1.0 mysql mysql-connector-java org.springframework.boot spring-boot-starter-test test
1.4、使用代码生成器
找到service-util模块中的代码生成器,修改moduleName为user
,并执行,然后删除entity包,相关类中引入model模块中的类
1.5、创建配置文件
在server-user模块中resources目录下创建文件
application.yml
:
spring: application: name: service-user profiles: active: dev,redis
application-dev.yml
:
mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl mapper-locations: classpath:com/atguigu/syt/user/mapper/xml/*.xmlserver: port: 8203spring: cloud: nacos: discovery: server-addr: 127.0.0.1:8848 datasource: driver-class-name: com.mysql.cj.jdbc.Driver password: 123456 url: jdbc:mysql://localhost:3306/guigu_syt_user?characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false username: root jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 logging: level: root: info file: path: user feign: client: config: default: connect-timeout: 2000 #连接建立的超时时长,单位是ms,默认1s read-timeout: 2000 #处理请求的超时时间,单位是ms,默认为1s sentinel: enabled: true #开启Feign对Sentinel的支持 wx: open: app-id: wxc606fb748aedee7c # 微信开放平台 appid app-secret: 073e8e1117c1054b14586c8aa922bc9c #微信开放平台 appsecret redirect-uri: http://localhost:8200/api/user/wx/callback #微信开放平台 重定向url syt-base-url: http://localhost:3000 #预约挂号平台baserul
注意:此处重定向url的主机地址必须为 localhost:8200,因为这是在微信开放平台中预先配置的参数。生产环境中这个参数需要根据实际情况进行修改。
1.6、创建启动类
package com.atguigu.syt.user;@SpringBootApplication@ComponentScan(basePackages = {"com.atguigu"})public class ServiceUserApplication { public static void main(String[] args) { SpringApplication.run(ServiceUserApplication.class, args); }}
1.7、创建常量类
创建utils包,创建ConstantProperties.java常量类
package com.atguigu.syt.user.utils;@Configuration@ConfigurationProperties(prefix="wx.open") //读取节点@Data //使用set方法将wx.ope节点中的值填充到当前类的属性中public class ConstantProperties { private String appId; private String appSecret; private String redirectUri; private String sytBaseUrl;}
避免红色提示:
在service的pom.xml中添加如下依赖
org.springframework.boot spring-boot-configuration-processor true
2、Controller
2.1、创建controller
service-user微服务中创建controller.front包,front包中创建FrontWxController
package com.atguigu.syt.user.controller.front;@Api(tags = "微信扫码登录")@Controller//注意这里没有配置 @RestController@RequestMapping("/front/user/wx")@Slf4jpublic class FrontWxController { @Resource private ConstantProperties constantProperties; @GetMapping("login") public String login(HttpSession session){ try { StringBuffer baseUrl = new StringBuffer() .append("https://open.weixin.qq.com/connect/qrconnect") .append("?appid=%s") .append("&redirect_uri=%s") .append("&response_type=code") .append("&scope=snsapi_login") .append("&state=%s") .append("#wechat_redirect"); //处理回调url String redirectUri = URLEncoder.encode(constantProperties.getRedirectUri(), "UTF-8"); //处理state:生成随机数,存入session //ThreadLocalRandom解决了Random在高并发环境下随机数生成性能问题 long nonce = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE); //十六进制表示的随机数 String state = Long.toHexString(nonce); log.info("生成 state = " + state); session.setAttribute("wx_open_state", state); String qrcodeUrl = String.format( baseUrl.toString(), constantProperties.getAppId(), redirectUri, state ); return "redirect:" + qrcodeUrl; } catch (Exception e) { throw new GuiguException(ResultCodeEnum.URL_ENCODE_ERROR, e); } }}
2.2、网关配置
在server-gateway中添加如下配置
- id: service-user predicates: Path=/*/user/** uri: lb://service-user
3、跨站请求伪造
参考资料:https://baike.baidu.com/item/跨站请求伪造/13777878?fr=aladdin
3.1、介绍
跨站请求伪造,Cross-site request forgery,通常缩写为 CSRF或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法
3.2、攻击细节
跨站请求攻击,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。
3.3、例子
假如一家银行用以运行转账操作的URL地址如下:http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName
那么,一个恶意攻击者可以在另一个网站上放置如下代码:
如果有账户名为Tom的用户访问了恶意站点,而他之前刚访问过银行不久,登录信息尚未过期,那么她就会损失1000资金。
3.4、防御措施
添加校验token
由于CSRF的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求在访问敏感数据请求时,要求用户浏览器提供不保存在cookie中,并且攻击者无法伪造的数据作为校验,那么攻击者就无法再运行CSRF攻击。这种数据通常是请求中的一个数据项。服务器将其生成并附加在请求中,其内容是一个伪随机数。当客户端提交请求时,这个伪随机数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪随机数,而通过CSRF传来的欺骗性攻击中,攻击者无从事先得知这个伪随机数的值,服务端就会因为校验token的值为空或者错误,拒绝这个可疑请求。
第03章-登录回调
1、Controller
注意:微信服务器配置授权回调域
要和redirect-uri
一致
service-user微服务中创建controller.api包,api包中创建ApiWxController
package com.atguigu.syt.user.controller.api;@Api(tags = "微信扫码登录回调")@Controller//注意这里没有配置 @RestController@RequestMapping("/api/user/wx")@Slf4jpublic class ApiWxController { /** * 登录回调 * @param code * @param state * @param session * @return */ @GetMapping("callback") public String callback(String code, String state, HttpSession session) { //得到授权临时票据code和state参数 log.info("callback被调用"); log.info("code = " + code); log.info("state = " + state); String sessionState = (String) session.getAttribute("wx_open_state"); log.info("sessionState = " + sessionState); log.info("seesion_id = " + session.getId()); if (StringUtils.isEmpty(code) || StringUtils.isEmpty(state) || !state.equals(sessionState)) { throw new GuiguException(ResultCodeEnum.ILLEGAL_CALLBACK_REQUEST_ERROR); } return null; }}
3、Service
根据微信的openid判断数据库是否存在当前用户信息
接口:UserInfoService
/** * 根据openid查询用户信息 * @param openid * @return */UserInfo getByOpenId(String openid);
实现:UserInfoServiceImpl
@Overridepublic UserInfo getByOpenId(String openid) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(UserInfo::getOpenid, openid); return baseMapper.selectOne(queryWrapper);}
3、完善回调方法
资料:资料>微信扫码登录>CookieUtils.java
放入service-util模块
package com.atguigu.syt.user.controller.api;@Api(tags = "微信扫码登录回调")@Controller//注意这里没有配置 @RestController@RequestMapping("/api/user/wx")@Slf4jpublic class ApiWxController { @Resource private ConstantProperties constantProperties; @Resource private UserInfoService userInfoService; @Resource private RedisTemplate redisTemplate; /** * 登录回调 * @param code * @param state * @param session * @return */ @GetMapping("callback") public String callback(String code, String state, HttpSession session, HttpServletResponse response) { try { //得到授权临时票据code和state参数 log.info("callback被调用"); log.info("code = " + code); log.info("state = " + state); String sessionState = (String) session.getAttribute("wx_open_state"); log.info("sessionState = " + sessionState); log.info("seesion_id = " + session.getId()); if (StringUtils.isEmpty(code) || StringUtils.isEmpty(state) || !state.equals(sessionState)) { throw new GuiguException(ResultCodeEnum.ILLEGAL_CALLBACK_REQUEST_ERROR); } //使用code和appid以及appscrect换取access_token StringBuffer baseAccessTokenUrl = new StringBuffer() .append("https://api.weixin.qq.com/sns/oauth2/access_token") .append("?appid=%s") .append("&secret=%s") .append("&code=%s") .append("&grant_type=authorization_code"); String accessTokenUrl = String.format(baseAccessTokenUrl.toString(), constantProperties.getAppId(), constantProperties.getAppSecret(), code); //使用httpclient发送请求 byte[] respdata = HttpUtil.doGet(accessTokenUrl); String result = new String(respdata); log.info("accesstokenInfo:" + result); JSONObject resultJson = JSONObject.parseObject(result); if (resultJson.getString("errcode") != null) { log.error("获取access_token失败:" + resultJson.getString("errcode") + resultJson.getString("errmsg")); throw new GuiguException(ResultCodeEnum.FETCH_ACCESSTOKEN_FAILD); } String accessToken = resultJson.getString("access_token"); String openId = resultJson.getString("openid"); log.info(accessToken); log.info(openId); //根据access_token获取微信用户的基本信息 //先根据openid进行数据库查询 UserInfo userInfo = userInfoService.getByOpenId(openId); if (userInfo != null) { //存在 log.info("判断用户是否被禁用"); if(userInfo.getStatus() == UserStatusEnum.LOCK.getStatus()){ log.error("用户已被禁用"); throw new GuiguException(ResultCodeEnum.LOGIN_DISABLED_ERROR); } }else{ log.info("注册用户"); //使用access_token换取受保护的资源:微信的个人信息 String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" + "?access_token=%s" + "&openid=%s"; //使用httpclient发送请求 String userInfoUrl = String.format(baseUserInfoUrl, accessToken, openId); byte[] respdataUser = HttpUtil.doGet(userInfoUrl); String resultUserInfo = new String(respdataUser); JSONObject resultUserInfoJson = JSONObject.parseObject(resultUserInfo); if (resultUserInfoJson.getString("errcode") != null) { log.error("获取用户信息失败:" + resultUserInfoJson.getString("errcode") + resultUserInfoJson.getString("errmsg")); throw new GuiguException(ResultCodeEnum.FETCH_USERINFO_ERROR); } //解析用户信息 String nickname = resultUserInfoJson.getString("nickname"); String headimgurl = resultUserInfoJson.getString("headimgurl"); //用户注册 userInfo = new UserInfo(); userInfo.setOpenid(openId); userInfo.setNickName(nickname); userInfo.setHeadImgUrl(headimgurl); userInfo.setStatus(UserStatusEnum.NORMAL.getStatus()); userInfoService.save(userInfo); } //获取用户名,如果没有用户名(未实名认证),则获取昵称 String name = userInfo.getName(); if (StringUtils.isEmpty(name)) { name = userInfo.getNickName(); } //生成token String token = UUID.randomUUID().toString().replaceAll("-", ""); //将token做key,用户id做值存入redis redisTemplate.opsForValue()//30分钟 .set("user:token:" + token, userInfo.getId(), 30, TimeUnit.MINUTES); //将token和name存入cookie //将"资料>微信登录>CookieUtils.java"放入service-utils模块 int cookieMaxTime = 60 * 30;//30分钟 CookieUtils.setCookie(response, "token", token, cookieMaxTime); CookieUtils.setCookie(response, "name", URLEncoder.encode(name), cookieMaxTime); CookieUtils.setCookie(response, "headimgurl", URLEncoder.encode(userInfo.getHeadImgUrl()), cookieMaxTime); return "redirect:" + constantProperties.getSytBaseUrl(); } catch (GuiguException e) { log.error(ExceptionUtils.getStackTrace(e)); return "redirect:" + constantProperties.getSytBaseUrl() + "?code=201&message=" + URLEncoder.encode(e.getMsg()); } catch (Exception e) { log.error(ExceptionUtils.getStackTrace(e)); return "redirect:" + constantProperties.getSytBaseUrl() + "?code=201&message="+URLEncoder.encode("登录失败"); } }}
4、前端整合
资料:资料>微信扫码登录>myheader.vue
将myheader.vue复制到前端项目的layouts目录中,覆盖原来的文件
第04章-spring session共享(了解)
前面我们使用了访问令牌和redis的形式代替session,也可以使用Spring提供的SpringSession实现session共享
开启SpringSession的步骤如下:
1、添加依赖
在service-user中添加spring-session依赖
org.springframework.session spring-session-data-redis
2、添加json序列化配置
在service-util的RedisConfig中添加如下配置:将默认的jdk序列化方案修改为json序列化方案
@Bean //(name = "springSessionDefaultRedisSerializer")public RedisSerializer
上面的配置注入如下的位置
此时,之前我们存入session中的防止CSRF攻击的参数state,就可以自动存储在redis中了
3、测试session共享
在FrontWxController中添加测试代码
@GetMapping("testSaveSession") //在8203执行session存储public void testSaveSession(HttpSession session){ session.setAttribute("user", "helen");}@GetMapping("testGetSession") //在8213执行session获取public void testGetSession(HttpSession session){ String user = (String)session.getAttribute("user"); log.info(user);}
源码:https://gitee.com/dengyaojava/guigu-syt-parent
关键词:
每日速看!尚医通-day10【微信扫码登录】(内附源码)
linux iptables安全技术与防火墙_快播报
收评:创业板指涨0.68%收获三连阳 半导体行业涨幅靠前
全球快报:小米卢伟冰:小米13 Ultra在意法西德及香港地区正式开售 销售超预期
首发预装鸿蒙OS 4.0!华为Mate60 Pro概念图出炉 全球热资讯
天天精选!NASA决定造访遍地黄金的“灵神星”:平均每位美国人能分300亿美元
美系硬派SUV福特探险者谍照曝光!外观内饰全面升级 预计将在年内首次亮相_今日热讯
今日热搜:8GB显卡卖到3199元 显存成本曝光:英伟达实在太赚了
视点!男生抠掉脸上痘痘流血近1小时:用了一包400张抽纸
每日报道:6月13日华鲁恒升尿素价格暂稳
焦点信息:市场监管总局:瞄准先进材料、人工智能等领域推动建立国家标准参考数据中心
烟台大学附属中学石明校区举行垃圾分类科普讲座|天天时快讯
速递!8GB内存笔记本卖到10499元起 苹果被批吃相难看:应该破发
货车高速上连续疯狂别车被撞停 官方:未造成伤亡、已找到肇事者|当前播报
天天热讯:马斯克相中的男人!14岁成SpaceX最年轻工程师、岗位年薪百万
环球即时看!小米平板6 Pro两个月使用心得:找不到短板的安卓板皇
Arm发布全新智能视觉参考设计 首次整合第三方IP核心
2023安洵杯 re复现
每天一道面试题:Spring的Bean生命周期
Axure RP教程_编程入门自学教程_菜鸟教程-免费教程分享_环球今日讯
环球今日报丨哥伦比亚4名空难获救儿童的母亲生前或遭家暴,孩子外公和父亲欲争夺抚养权
当前关注:你收益多少?余额宝上线第十年:每天为国人赚1亿零花钱 网友狂晒单
排队5小时!浙江网红面包黄牛加价上百元 网友吐槽:消协回应
【当前独家】中轴线文化遗产有了常设讲堂
三种方法让.NET轻松实现Excel转PDF
天天快看点丨docker-compose搭建wordpress
【播资讯】比亚迪执行副总裁:美国市场不在我们考虑范围内
石家庄迈入“刷脸”乘车时代:买一根火腿肠就能免费坐地铁活动结束了_环球动态
制作成本16.5亿!《封神三部曲》第一部7月20上映:角色海报公布 太强大 今日报
吹牛还是玩真的?丰田下一代电动汽车续航达1500公里
【天天报资讯】小米发布米家旅行箱:顶部嵌平设计 行走的小桌板
市场监管总局:到2035年 计量数据归集共享规模显著提升-快播
【技术积累】软件设计模式中的工厂模式【一】-独家
STM32F429 Discovery开发板应用:使用FreeRTOS队列+DMA双缓存实现串口数据接收
【寻味中华丨饮食】蔡甸藕带:白若玲珑玉 丝缕皆故乡
【天天时快讯】699元!XREAL Beam投屏盒子发布:随身携带的“可悬停AR空间屏”
AMD今晚发布新CPU Intel急了:至强性能比EPYC快7倍
【世界新视野】4-1战胜热火!掘金队夺队史首个NBA总冠军:网友发帖祝贺 约老师太强
小区门口连装8条减速带 物业回复让业主无语:为防业主逃费
贵1000元值不值?i7-13700H和i5-13500H对比实测 世界观点
我在塞尔维亚寻找约基奇-每日速递
全球微速讯:“铁榔头”郎平重返中学校园,为学弟学妹成长“支招”
世界最新:深度学习应用篇-推荐系统[11]:推荐系统的组成、场景转化指标(pv点击率,uv点击率,曝光点击率)、用户数据指标等评价指标详解
flutter 日志打印三種方法
最新:Linux根文件制作
热推荐:一对一直播源码平台搭建的关键条件,成败在此。
真刑!几行代码端了整个教务系统。。
启明星辰(002439)6月12日主力资金净卖出1310.11万元
长安欧尚Z6新能源半年降价3万多被集体投诉 车主:坑惨我们了
每日热讯!2折!115网盘618大促:10年VIP只要1000元 赠100TB空间
全脂/低脂可选:特仑苏纯牛奶2.7元/盒抄底(商超6元)
腾势N7赛道远超宝马X3 赵长江:意向客户看到展车后几乎全下单了
Fold5、Flip5换壳!三星W24系列折叠屏手机通过认证:25W快充
吴尚垠 吴尚_每日消息
JAVA非递归生成无限级菜单树的较简代码实现。(非泛用型工具包,仅总结逻辑)
每日关注!低代码开发平台为数智赋能,让开发变得更简单
奶我一口是什么意思网络用语_奶你一口是什么意思简介介绍
腾讯祭出的大招《无畏契约》 能不能成为下一个《英雄联盟》?-环球微速讯
环球观点:最大内存+最美拍照手机!小米Civi 3 1TB上市:2999元
当前消息!满级玩家有盼头了 暗黑世界V等你来
28.98万起 智己LS7都市版上市 CEO刘涛:现在买增程过几年就会焦虑 每日热点
男子洗澡被闯入的两匹“狼”吓坏 经辨认是阿拉斯加 焦点精选
C天键(301383)6月12日主力资金净买入2791.69万元 焦点快看
【当前独家】hvv面试常见框架漏洞
天天热头条丨ldquo 以至 rdquo ldquo 以致 rdquo ldquo 以至于 rdquo 与 ldquo 以致于 rdquo 的区别
蔚来降价3万!李斌:买的起2、30万车的人时间成本很高 时薪200元肯定是有的-世界播报
世界即时:仇恨拉满!日本核污水排放在即 韩国人正疯狂买盐:不敢吃海鲜了
三冠王巡游!曼城全队展示三座奖杯 哈兰德赤膊上阵 城迷疯狂庆祝
每日快报!暂停加息预期支撑多头 美债市场周初表现偏强
微资讯!英伟达市占率超83% 显卡降不降价我说了算!4060系列买到偷着乐?
希捷被重罚3亿美元后!消息称华为不缺硬盘、SSD了:西数持续供货中_每日资讯
十多年了 苹果新款Mac Pro依然不是中国制造:美泰联手组装
芬兰加入北约的军事协调工作结束 双方签署声明 速读
每日速读!读发布!设计与部署稳定的分布式系统(第2版)笔记01_生产环境的生存法则
大家超爱看黑美鱼?《小美人鱼》卖座成2023票房TOP10:国内外口碑两极分化
焦点简讯:漫威等大片国人不爱看了 不符合审美!郭帆:中国电影将弯道超车好莱坞
环球微速讯:香干怎么做比较好吃?
靠给肯德基、麦当劳供货的纸袋大王上市:创始人身价近12亿
一个月3家公司退出:日本手机产业无奈 打不过苹果也打不过小米-世界新消息
天天新动态:618降价英雄:大屏电视跌破2000元该如何买?
绝不降价的蔚来全系降3万!李斌被逼得没辙了
夸克App半夜突然上热搜 网友懵了:没广告很好用
全球报道:鲁葱杂5号_关于鲁葱杂5号简述
开远市气象台发布暴雨蓝色预警信号【Ⅳ级/一般】【2023-06-12】|每日速读
微控制器实时操作系统实践4选择正确的MCU 每日报道
全球短讯!.NET 5 以后的 HttpClient 超时问题
Web安全-渗透测试-信息收集02|环球通讯
还买什么杂牌 小米27寸2K 165Hz显示器到手1159元
贵州“美猴王”打坐冥想神情悠闲 网友:猴子竟也懂养生
全球第三大手机系统鸿蒙4.0来了!华为HDC 2023定档:8月4日见 关注
国际原子能机构总干事已出发前往乌克兰 计划与泽连斯基会面|全球快播
如何解决系统报错:nf_conntrack: table full, dropping packets|天天关注
内存马简单了解(新手向)
头条焦点:小米9号员工李明创业:全球首款Android桌面机器人来了
全年无休的数字人 卷走主播
徕卡超大杯首次进军海外!小米13 Ultra登陆欧洲:售价1499欧超iPhone
讯息:中国最强AI研究院的大模型 为何迟到了
海南女网友感冒 福建小伙骑电摩上高速送药:地图显示要骑77小时|今日热闻
广东惠州可提供联创电压力锅维修服务地址在哪
vulhub靶场搭建,以及使用方法