前言:系统中若用到微信扫码登录,则要进行微信公众账号授权,所以在开发功能之前,

需要到微信公众平台申请一个服务号,当然仅仅只是作为开发者,则使用测试公众账号也行。

有了公众号后,则需登录公众后台进行一些基础配置,配置流程如下

1.点击设置–>选择公众号设置功能设置–>配置好业务域名和网页授权域名。

2.点击开发–>选择基本配置–>配置公众号开发信息

详细配置可查看[这篇博文]

一、需求说明

1、用户在登录的时候可通过微信扫码登录

2、若用户在系统中未绑定微信,则可让用户到个人中心扫码绑定

二、业务实现逻辑

扫码登录的实现逻辑

​ 1、用户进入系统登录页时需要生成微信登录二维码

​ 2、用户扫码完成网页授权操作,然后微信要通知咱们系统,

​ 3、微信回调咱们系统后,咱们系统需再向微信索取用户的微信账户信息(主要是拿用户的openid),

​ 然后拿此openid到用户表查询一下,存在则说明此用户属于咱们系统,然后将用户信息丢到

​ 缓存中

​ 4、前端轮训后台是否登录

扫码绑定的实现逻辑

​ 1、用户进入系统的个人中心时需要生成微信绑定二维码(此二维码需携带用户信息,如userid)

​ 2、若当前用户没有关注公众号,则用户扫码跳转到关注公众号页面,完成关注并绑定

​ 3、若当前用户已经关注公众号,则用户扫码跳转到公众号聊天欢迎页,同时进行账号绑定

​ 4、前端轮训后台是否绑定

三、代码实现逻辑

* 微信登录流程:
*  1、前端请求后台接口生成UUID
*  2、前端获取授权二维码(传入UUID)
*  3、前端轮询后台是否登录(根据uuid轮询)
* 微信绑定流程:
*  1、前端请求后台接口生成UUID
*  2、前端获取绑定二维码(传入UUID,userId)
*  3、前端轮询后台是否绑定(根据uuid轮询)
pom.xml<!--wxforjava依赖原文链接:https://github.com/Wechat-Group/WxJava-->
<dependency><groupId>com.github.binarywang</groupId><artifactId>weixin-java-mp</artifactId><version>2.9.0</version>
</dependency>
<!-- 谷歌二维码支持包原文链接:https://blog.csdn.net/xm526489770/article/details/83651555
-->
<dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.3.0</version>
</dependency><dependency><groupId>net.glxn</groupId><artifactId>qrgen</artifactId><version>1.4</version>
</dependency>
WeChatUtil微信工具类
import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
public class WeChatUtil {private static Logger logger = LoggerFactory.getLogger(WeChatUtil.class);//wxjava所需服务类private static WxMpService wxMpService = null;//微信用户事件回调地址private static String WEIXIN_OAUTH2="https://XXX/api/wx/common/callback";/*** 获取WxMpService*/public static WxMpService getWxMpService() {if (wxMpService == null) {WxMpInMemoryConfigStorage config = new WxMpInMemoryConfigStorage();config.setAppId(appId);         // 设置微信公众号的appidconfig.setSecret(appSecret);    // 设置微信公众号的app corpSecretconfig.setToken("XXX");         // 设置微信公众号的token//config.setAesKey("...");      // 设置微信公众号的EncodingAESKeyconfig.setOauth2redirectUri(WEIXIN_OAUTH2);wxMpService = new WxMpServiceImpl();wxMpService.setWxMpConfigStorage(config);}return wxMpService;}
}
QRCodeUtil二维码生成工具类

生成二维码工具类的博客

Controller代码
@Api(tags = { "微信常用接口" }, description = "微信常用接口")
@RestController
@RequestMapping("/api/wx/common/")
public class WxAuthController {private static Logger logger = LoggerFactory.getLogger(WxAuthController.class);//用户操作相关的service@Autowiredprivate UsersService usersService;//获取微信发送消息的Serviceprivate static WxMpService wxMpService=WeChatUtil.getWxMpService();//远程调用服务@Autowiredprivate RestTemplate restTemplate;/*** @Desc获取唯一标识 此接口只接收前端的请求* @Author LRH* @Date 2020/6/28 17:02*/@ApiOperation("获取系统全局唯一标识")@RequestMapping(value = "/getGUID", method = {RequestMethod.POST})public JSONObject getGUID() throws IOException {//生成UUIDString uuid = UUID.randomUUID().toString().replaceAll("-", "");//返回封装JSONObject jsonObject = new JSONObject();jsonObject.put("Data", uuid);jsonObject.put(Constant.RETURN_STA, uuid == null ? Constant.ERROR : Constant.SUCCESS);//响应状态码jsonObject.put(Constant.RETURN_MSG, uuid == null ? Constant.QUERY_MSG_F : Constant.QUERY_MSG_S);//响应消息return jsonObject;}/*** @Desc该接口只接收微信回调请求获取code* @Author LRH* @Date 2020/6/28 16:05*/@ApiOperation("接收微信回调请求(网页授权回调)")@RequestMapping(value = "/code", method = {RequestMethod.GET,RequestMethod.POST})public void getCallBackCode(HttpServletRequest request, HttpServletResponse response) throws IOException, WxErrorException {logger.info("=============微信登录回调");String code = request.getParameter("code");                //获取codeString handlerType = request.getParameter("handlerType");  //获取二维码类型String uuid = request.getParameter("uuid");                //获取二维码uuidlogger.info("接收微信数据:code:" + code + ",handlerType:" + handlerType + ",uuid:" + uuid);// 获取用户微信账户信息(包含用户openid)WxMpOAuth2AccessToken accessToken = wxMpService.oauth2getAccessToken(code);logger.info("返回微信用户信息:" + JSONObject.toJSONString(accessToken));if (accessToken != null) {if (handlerType.equals("login")) {          //如果是登录//查询用户是否属于这个系统的账户logger.info("查询用户 "+accessToken.getOpenId()+" 是否属于这个系统的账户");UserVo user= usersService.getUserInfoByOpenId(accessToken.getOpenId());if(user!=null){user.setIsLogin(true);      //不为空则允许登录logger.info("欢迎登录!!!");}else{user=new UserVo();user.setIsLogin(false);        //为空则不允许登录logger.info("拒绝登录!!!");}logger.info("把用户数据存Redis中:{},有效时间1小时",JSONObject.toJSONString(user));RedisUtil.put(uuid,JSONObject.toJSONString(user),3600);response.sendRedirect(user.getIsLogin()?"https://XXX.com/fixed/ScanSuccess.html?uuid="+uuid:"https://XXX.com/fixed/ScanFail.html");         // 微信端跳转到响应页面(成功或失败页面)}}else{logger.info("获取微信用户无响应!!!");}}/*** @Desc获取微信授权二维码 此接口只接收前端的请求(监听IP白名单)* @Author LRH* @Date 2020/6/28 17:02*/@ApiOperation("获取微信授权二维码")@RequestMapping(value = "/getWxAuthorizeQR/{handlerType}/{UUID}", method = {RequestMethod.GET})public void getWxAuthorizeQR(@PathVariable("handlerType") String handlerType, @PathVariable("UUID") String uuid, HttpServletResponse response) throws IOException {logger.info("=============获取微信登录授权二维码,handlerType:{},uuid:{}",handlerType,uuid);if (!(handlerType.equals("login"))) {//与Constant中的throw new BusinessException(0, "请求参数错误");}if (uuid.equals("undefined")) {throw new BusinessException(0, "请求参数uuid错误:"+uuid);}// 授权请求地址(系统自定义的)String url = "https://XXX/api/wx/common/send/wxreq/" + handlerType + "/" + uuid;BufferedImage bufferedImage = QRCodeUtil.getBufferedImage(url, 250, "");response.setContentType("image/png");OutputStream os = response.getOutputStream();ImageIO.write(bufferedImage, "png", os);//将url转二维码}@ApiOperation("发起微信网页授权请求")@RequestMapping(value = "/send/wxreq/{handlerType}/{UUID}", method = {RequestMethod.GET})public void push_wx_auth(@PathVariable("handlerType") String handlerType, @PathVariable("UUID") String uuid, HttpServletResponse response) throws IOException {logger.info("发起用户微信认证:parame:{}", "handlerType:" + handlerType + ",uuid:" + uuid);if (!(handlerType.equals("login") || handlerType.equals("bind")||handlerType.equals("myapprove"))) {throw new BusinessException(0, "发起用户微信认证请求参数错误");}if (uuid == null) {throw new BusinessException(0, "发起用户微信认证请求参数错误");}//微信重定向页面String backUrl = "https://XXX/api/wx/common/code?handlerType=" + handlerType + "&uuid=" + uuid;// 授权页面地址String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + (系统appId)WeChatUtil.appId + "&redirect_uri="+ URLEncoder.encode(backUrl) + "&response_type=code" + "&scope=snsapi_userinfo"+ "&state=STATE#wechat_redirect";logger.info("发起用户微信认证url:" + url);response.sendRedirect(url);//重定向微信授权页}//生成关注公众号的二维码@ApiOperation("生成关注公众号的二维码")@RequestMapping(value = "/myGetWxQR/{UUID}", method = {RequestMethod.GET})public void myGetWxQR(@PathVariable("UUID") String uuid,String userId,HttpServletResponse response) throws IOException {logger.info("========调用微信,生成关注公众号的二维码");//获取接口访问凭证tokenString accessToken = WeChatUtil.getAccessToken();JSONObject paramJSON=new JSONObject();paramJSON.put("uuid",uuid);paramJSON.put("userId",userId==null?"":userId);logger.info("paramstr:{}",paramJSON);//凭证不为空则获取微信二维码WxMpQrCodeTicket wxMpQrCodeTicket=new WxMpQrCodeTicket();if(accessToken!=null) {try {wxMpQrCodeTicket= wxMpService.getQrcodeService().qrCodeCreateTmpTicket(paramJSON.toJSONString(), 100000);} catch (WxErrorException e) {logger.info("调用微信,生成关注公众号的二维码失败");e.printStackTrace();}//下面生成图片用的BufferedImage bufferedImage = QRCodeUtil.getBufferedImage(wxMpQrCodeTicket.getUrl(), 250, "");response.setContentType("image/png");OutputStream os = response.getOutputStream();ImageIO.write(bufferedImage, "png", os);}}private final static String MEDIATYPE_CHARSET_JSON_UTF8 = MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8";@RequestMapping(value = "/callback",  method = {RequestMethod.GET,RequestMethod.POST}, produces = MEDIATYPE_CHARSET_JSON_UTF8)@ResponseBody@ApiOperation("微信用户操作事件回调")public String callback(HttpServletRequest request, HttpServletResponse response) throws Exception {//先验证是否为微信请求回调if("false".equals(checkSign(request))){logger.info("非微信访问,接口请求失败");throw new BusinessException(0,"非微信访问,接口请求失败");}WxMpXmlMessage message=WxMpXmlMessage.fromXml(request.getInputStream());//获取消息流,并解析xmlString messageType=message.getMsgType();                                //消息类型String messageEvent=message.getEvent();                                  //消息事件String fromUser=message.getFromUser();                                   //发送者帐号String touser=message.getToUser();                                      //开发者微信号String text=message.getContent();                                      //文本消息  文本内容String eventKey=message.getEventKey();                                 //二维码参数String uuid="";                                                           //从二维码参数中获取uuid通过该uuid可通过websocket前端传数据String userid="";                                                     //从二维码参数中获取用户IDlogger.info("总的message:"+JSONObject.toJSONString(message));logger.info("消息类型:{},消息事件:{},发送者账号:{},接收者微信:{},文本消息:{},二维码参数:{}",messageType,messageEvent,fromUser,touser,text,eventKey);//获取微信服务器的IP地址/*String[] callbackIP = wxMpService.getCallbackIP();for(int i=0;i<callbackIP.length;i++){System.out.println("IP地址"+i+":"+callbackIP[i]);}*//*** 文本消息*/if(messageType.equals("text")){WxMpXmlOutTextMessage texts=WxMpXmlOutTextMessage.TEXT().toUser(fromUser).fromUser(touser).content("欢迎光临,热烈欢迎").build();String result = texts.toXml();System.out.println("响应给用户的消息:"+result);return result;}/*** 图片消息*/
//      if(messageType.equals("image")){//          String[] imaurl =
//          {};
//          //创建file对象
//          Random rand = new Random();
//          int index = rand.nextInt(3);
//          File file=new File(imaurl[index]);
//          //上传多媒体文件
//          WxMediaUploadResult wxMediaUploadResult = wxMpService.getMaterialService().mediaUpload(WxConsts.MediaFileType.IMAGE, file);
//          WxMpXmlOutImageMessage images = WxMpXmlOutMessage.IMAGE()
//                  .mediaId(wxMediaUploadResult.getMediaId())//获取上传到微信服务器的临时素材mediaid.
//                  .fromUser(touser)
//                  .toUser(fromUser)
//                  .build();
//          String result = images.toXml();
//          System.out.println("响应给用户的消息:"+result);
//          return result;
//      }//如果是订阅信息UsersRequest us=new UsersRequest();String userName="";                //用户名//查询当前用户是否绑定了系统logger.info("查询当前用户:{}是否绑定了系统",fromUser);UsersModel user = usersService.getUserByOpenId(fromUser);Boolean isbind=false;switch (messageEvent){case "subscribe"://解析二维码携带的参数(未关注)eventKey = eventKey.replace("qrscene_", "");if("subscribe".equals(messageEvent))logger.info("关注时触发订阅消息");case "SCAN":if("SCAN".equals(messageEvent))logger.info("扫描时触发订阅消息");//解析二维码携带的参数(已关注)JSONObject jsonObject = JSONObject.parseObject(eventKey);uuid=jsonObject.getString("uuid");userid=jsonObject.getString("userId");//不管3721直接获取用户头像WxMpUser wxMpUser=wxMpService.getUserService().userInfo(fromUser);logger.info("通过用户openid获取用户信息:"+JSONObject.toJSONString(wxMpUser));//则说明该用户微信账号还从来没绑定过系统//绑定过的就无需再绑,除非先解绑String rsmsg="";if(user==null){if(StringUtils.isNotEmpty(userid)){//如果有传用户idlogger.info("根据用户userid查询"+userid+"用户名称");UsersModel model = usersService.getUserById(userid);//如果用户openId不为空if(model!=null){if(StringUtils.isNotEmpty(model.getWxOpenid())){logger.info("扫码无效,此二维码已被绑定!!!");rsmsg="扫码无效,此二维码已被绑定!!!";}else{userName=model.getName()==null?"":model.getName()+","; //获取用户名us.setId(Integer.parseInt(userid));                      //用户IDus.setWxOpenid(fromUser);                             //用户openIDus.setHeadImg(wxMpUser.getHeadImgUrl());              //用户头像us.setUnionId(wxMpUser.getUnionId());       //公众号加入微信开放平台才会有unionId//修改用户的openidlogger.info("当前正在修改"+userid+"用户的openId:"+fromUser+",用户头像:"+wxMpUser.getHeadImgUrl()+",unionId:"+wxMpUser.getUnionId());usersService.updateOneData(us);isbind=true;rsmsg="您的微信已经绑定您的erp账号,以后可以用微信扫码登录erp";}}}rsmsg=userName+rsmsg;}else{//不可重复绑定,您的账号已绑定到某某用户上rsmsg="不可重复绑定,您的账号已绑定到"+user.getName()+"用户上\n如需绑定,请先解绑!!!";}String result="";if(!StringUtils.isEmpty(rsmsg)){WxMpXmlOutTextMessage texts=WxMpXmlOutTextMessage.TEXT().toUser(fromUser).fromUser(touser).content(rsmsg).build();result= texts.toXml();logger.info("响应给用户的消息:"+result);}//pushMsg(fromUser,userHeadImage,uuid,Constant.SUCCESS);//推送给前端(不用websocket推了)//扫码后的用户数据存入redisUserVo userVo=new UserVo();userVo.setWxOpenid(fromUser);userVo.setUnionId(wxMpUser.getUnionId());userVo.setHeadImg(wxMpUser.getHeadImgUrl());userVo.setCode(Constant.SUCCESS);userVo.setIsbind(isbind);//是否绑定logger.info("把用户数据存Redis中:"+JSONObject.toJSONString(userVo));RedisUtil.put(uuid,JSONObject.toJSONString(userVo),3600);return result;case "unsubscribe":logger.info("取关时触发订阅消息");//其实用户的基本信息可以先放缓存if(user!=null){//绑定过则清除其在系统中的openid,用户头像logger.info("取关时用户数据:{}",JSONObject.toJSONString(user));us.setId(user.getId());us.setWxOpenid("");us.setUnionId("");if(us.getSex().equals("M")){us.setHeadImg("https://XXX/fixed/mhead.png");//男性默认头像}else{us.setHeadImg("https://XXX/fixed/fhead.png");//女性默认头像}usersService.updateOneData(us);}return null;}return null;}/*** @Desc配置到微信后台的地址* 微信事件触发请求回调验证* @Author LRH* @Date 2020/7/15 11:18*/public String checkSign ( HttpServletRequest request) throws Exception {//获取微信请求参数logger.info("接收微信公众号事件触发回调请求");String signature = request.getParameter ("signature");String timestamp = request.getParameter ("timestamp");String nonce = request.getParameter ("nonce");String echostr = request.getParameter ("echostr");//参数排序。 token 就要换成自己实际写的 tokenString [] params = new String [] {timestamp,nonce,token} ;Arrays.sort (params) ;//拼接String paramstr = params[0] + params[1] + params[2] ;//加密//获取 shal 算法封装类MessageDigest Sha1Dtgest = MessageDigest.getInstance("SHA-1") ;//进行加密byte [] digestResult = Sha1Dtgest.digest(paramstr.getBytes ("UTF-8"));//拿到加密结果String mysignature = WebUtils.byte2HexStr(digestResult);logger.info("微信加密,signature:{}",signature);logger.info("本地加密,mysignature:{}",mysignature);//是否正确boolean signsuccess = mysignature.equals(signature);if (!signsuccess) {logger.info("验证失败,signature check fail");return "false"  ;//不正确就直接返回失败提示.}else{logger.info("验证成功");return "true"  ;//不正确就直接返回失败提示.}}/*** @Desc获取用户扫码后的数据* @Author LRH* @Date 2020/7/16 13:26*/@ApiOperation("获取用户扫码后的数据")@RequestMapping(value = "getWxScanData/{uuid}",method = RequestMethod.GET)public Object getWxScanData(@PathVariable("uuid") String uuid){logger.info("==================从redis中获取用户扫码后的数据==========");//从redis中拿到用户信息String userInfo = RedisUtil.get(uuid);//获取用户对象logger.info("==================返回数据:{}",userInfo);return StringUtils.isNotEmpty(userInfo)?ResultVOUtil.success(userInfo):ResultVOUtil.error(0,"暂无数据,稍后再试!!!");}
}

public class WeChatUtil {private static Logger logger = LoggerFactory.getLogger(WeChatUtil.class);//wxjava所需服务类private static WxMpService wxMpService = null;//微信用户事件回调地址private static String WEIXIN_OAUTH2="https://XXX/api/wx/common/callback";/*** 获取WxMpService*/public static WxMpService getWxMpService() {if (wxMpService == null) {WxMpInMemoryConfigStorage config = new WxMpInMemoryConfigStorage();config.setAppId(appId);         // 设置微信公众号的appidconfig.setSecret(appSecret);    // 设置微信公众号的app corpSecretconfig.setToken("XXX");         // 设置微信公众号的token//config.setAesKey("...");      // 设置微信公众号的EncodingAESKeyconfig.setOauth2redirectUri(WEIXIN_OAUTH2);wxMpService = new WxMpServiceImpl();wxMpService.setWxMpConfigStorage(config);}return wxMpService;}
}

springboot实现微信扫码登录和绑定相关推荐

  1. springboot实现微信扫码登录并且绑定

    前言:系统中若用到微信扫码登录,则要进行微信公众账号授权,所以在开发功能之前, 需要到微信公众平台申请一个服务号,当然仅仅只是作为开发者,则使用测试公众账号也行. 有了公众号后,则需登录公众后台进行一 ...

  2. SpringBoot整合微信扫码登录

    SpringBoot整合微信扫码登录 准备工作 基本思路流程 搭建SpringBoot 引入依赖 加入配置文件 代码实现 工具类 controller层 结果 准备工作 1.登录官网了解到,学习者想本 ...

  3. 一篇文章,带你了解微信扫码登录

    文章目录 前言 一.功能背景 二.扫码登录原理 1.基本原理 三.实现效果图 1.登录页 2.点击微信账号登录 3.已绑定微信的账号 4.未绑定微信号的账号 四.代码实现 1.准备工作 2.编写代码 ...

  4. Vue+abp微信扫码登录

    最近系统中要使用微信扫码登录,根据微信官方文档和网络搜索相关文献实现了.分享给需要的人,也作为自己的一个笔记.后端系统是基于ABP的,所以部分代码直接使用了abp的接口,直接拷贝代码编译不通过. 注册 ...

  5. 微信扫码登录网页实现原理

    扫码登录操作过程 浏览器输入:https://wx.qq.com/?lang=zh_CN 手机登录微信,利用"扫一扫"功能扫描网页上的二维码 手机扫描成功后,提示"登录网 ...

  6. (转)微信扫码登录网页实现原理

    扫码登录操作过程 浏览器输入:https://wx.qq.com/?lang=zh_CN 手机登录微信,利用"扫一扫"功能扫描网页上的二维码 手机扫描成功后,提示"登录网 ...

  7. 微信扫码登录_JAVA

    一.需求 在PC端的登录页面加个微信扫码的按钮,点击按钮弹出二维码,实现微信扫码登录网站的功能.如图: 二.调研 扫码登录属于微信开放平台提供的API,不是微信公众平台.这里需要注册等配置,暂不赘述. ...

  8. 通过微信扫码登录剖析 oauth2 认证授权技术

    本文目录 前言 趣味解读oauth2 oauth2精髓 oauth2核心概念 结合微信登录深刻理解oauht2 本文小结 前言 相信很多小伙伴在学习 JAVA 的过程中或多或少接触或者开发过类似于 x ...

  9. Web应用多账号系统设计及微信扫码登录实现

    https://www.cnblogs.com/beer/p/5538403.html 1 前言概述 公司对功能测试,性能测试,安全测试等等都做了比较好的自动化后,急需要一个MIS系统来统一管理这些结 ...

最新文章

  1. 直博清华的小姐姐!本科就发表了SCI,享受朝九晚五的学习生活,做自己的小太阳!...
  2. Ubuntu 安装任意版本Django
  3. TCP/IP协议之网络链接的背后故事
  4. 定时/计数器(定时和计数的功能)、定时器中断
  5. php右侧弹窗QQ客服,JavaScript_网页右侧悬浮滚动在线qq客服代码示例,网页右侧悬浮滚动QQ在线客服 - phpStudy...
  6. 【机器学习】总结:线性回归求解中梯度下降法与最小二乘法的比较
  7. SWF反编神器Action Script Viewer终身免费升级!
  8. 软件工程大学大三课表_专业选修课 | 面向大二、大三同学的专业选修课全面介绍来啦!...
  9. 牛客:阶乘结果换算进制后得到数字的尾部有几个0
  10. OPENCV与OPENCL
  11. C++/测绘附和导线测量源码
  12. iOS 出现:不受信任的开发者 弹框
  13. 携程2021年校招笔试题[2021年10月21日19点-21点]
  14. C#工作总结(一):Fleck的WebSocket使用
  15. Mixly电子音乐:蜗牛与黄鹂鸟
  16. 好的提问和寻找答案的网站(会时常更新)
  17. Gtest 测试指导 入门基础(A)
  18. 《弃子长安》第二章 长乐驱蛊
  19. 清华吴翼学长——ACM参赛故事
  20. android怎么增量编译,阿里秒级android增量编译工具freeLine的使用入门

热门文章

  1. VB.NET小报表(收据)打印
  2. 自然语言生成技术现状调查:核心任务、应用和评估(1)
  3. 分享商家为什么要做扫码点餐系统_微信小程序点餐系统有什么作用
  4. 访问学者美国访学哪些东西不能带?
  5. 一对一直播源码开发基础方案全面讲解,拯救不开心
  6. 2018 年技术趋势预测
  7. 《C++详解》(二)初入C++最重要的知识点:引用 操作符
  8. 图片标签,内联框架 css简介
  9. HP T530瘦客户机上部署 朵拉云DoraOS连接华为桌面云
  10. 叶绿体基因组分析须要注意的地方(注释篇)