同步两个域之间的cookie值的问题

同步两个域之间的cookie值的问题

这是完善上个问题代码不全的问题,首先我按照老师的建议修改DetailServlet中的HttpPost httpPost = new HttpPost("http://localhost:8080"+server + "/" + method);
http://img1.sycdn.imooc.com//climg/5a942ad700014a0c08480180.jpg

但是BrowseServlet还是拦截不了/browse/setCookie.do,下面是我这几个页面用到的代码

tomcat设置页面

http://img1.sycdn.imooc.com//climg/5a942bba0001b23508110732.jpg

http://img1.sycdn.imooc.com//climg/5a942bc800018feb07910132.jpg

LocalCache

package cache;

import entity.Product;

import java.util.*;

/**
 * 本地数据加载库
 */
public class LocalCache {
    //产品的数据集合
    private static Map<Long, Product> productMap = new HashMap<>();


    //模拟本地数据库
    static {
        productMap.put(1L, new Product(1l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(2l, new Product(2l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(3l, new Product(3l, "JAVA", "JAVA基础课程-基本语法", "介绍java基本语法特性及编写规范", "初级", 219));
        productMap.put(4l, new Product(4l, "JAVA", "JAVA基础课程-JDBC", "介绍JDBC方式连接数据库", "初级", 219));
        productMap.put(5l, new Product(5l, "JAVA", "JAVA基础课程—Socket", "介绍Java网络编程Socket", "初级", 219));
        productMap.put(6l, new Product(6l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(7l, new Product(7l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(8l, new Product(8l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(9l, new Product(9l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(10l, new Product(10l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(11l, new Product(11l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(12l, new Product(12l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(13l, new Product(13l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(14l, new Product(14l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(15l, new Product(15l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(16l, new Product(16l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(17l, new Product(17l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(18l, new Product(18l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(19l, new Product(19l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(20l, new Product(20l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(21l, new Product(21l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(22l, new Product(22l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(23l, new Product(23l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(24l, new Product(24l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
        productMap.put(25l, new Product(25l, "HTML/CSS", "HTML+CSS基础课程", "HTML+CSS基础教程8小时带领大家步步深入学习标签用法和意义", "初级", 219));
    }
    //数据库的读写方式
    private LocalCache(){

    }

    //得到数据库的商品集合
    public static List<Product> getProductMap(){
        return new ArrayList<Product>(productMap.values());
    }

    //通过序号Id得到商品的方法
    public static Product getProduct(Long id){
        return productMap.get(id);
    }

    //判断序号id在集合中是否存在
    public static boolean isEmptyId(Long id){
       return productMap.containsKey(id);
    }

}

ProduceService

package service;

import cache.LocalCache;
import entity.Product;

import java.util.ArrayList;
import java.util.List;

/**
 * 商品管理的处理类
 */
public class ProduceService {

    //得到商品数据总集合的方法
    public static List<Product> getProducts(){
        return LocalCache.getProductMap();
    }

    //通过id得到商品的方法
    public static Product getProduct(Long productId){
        //查询id是否存在
        if(LocalCache.isEmptyId(productId)){
            return LocalCache.getProduct(productId);
        }
        return null;
    }

    //通过页数取得对应商品的方法
    public static List<Product> getPageProduct(int page,int size,String title){
        //调用得到商品数据总集合的方法
        int totalProducts =getProductCount(title).size();
        int start = (page - 1) * size ;
        int end = totalProducts > page*size ? page*size : totalProducts ;
        return getProductCount(title).subList(start,end);
    }

    //通过查询的字符获得商品个数的方法
    public static List<Product> getProductCount(String title){
        List<Product> productList = new ArrayList<>();
        if(null != title && !"".equals(title)){
            //当传进来的name不为空,遍历当前集合
            getProducts().forEach(product -> {
                //当商品的名称包含有name添加到集合中
                if(product.getName().contains(title)){
                    productList.add(product);
                }
            });
        }else {
            //name为空时,添加全部商品集合
            productList.addAll(getProducts());
        }
        return productList;
    }
}

DetailServlet

package servlet;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import service.ProduceService;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class DetailServlet extends HttpServlet {
    //创建java线程池对象,创建10个线程池
    private ExecutorService service = Executors.newFixedThreadPool(10);

    //实现同步cookie的方法
    private void syncCookie(String server, String method, String cookies) {
        //调用提交的方法
        service.submit(new Runnable() {
            @Override
            public void run() {
                //post请求调用
                HttpPost httpPost = new HttpPost("http://localhost:8080"+server + "/" + method);
                CloseableHttpClient httpClient = null;
                CloseableHttpResponse response = null;
                try {
                    httpClient = HttpClients.createDefault();
                    response = httpClient.execute(httpPost);
                    HttpEntity entity = response.getEntity();
                    String responseContent = EntityUtils.toString(entity, "utf-8");
                    System.out.println("==============" + responseContent);
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (null != response) {
                        try {
                            response.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (null != httpClient) {
                        try {
                            httpClient.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        if (Objects.equals("/detail/detail.do", req.getServletPath())) {
            //为list界面传来请求
            String productId = req.getParameter("productId");
            if (null != productId && !"".equals(productId)) {
                //声明一个existCookie
                Cookie existCookie = null;
                //遍历系统存在的cookie
                //从cookie中取出所有数据
                for (Cookie cookie : req.getCookies()) {
                        if (Objects.equals("productIds", cookie.getName())) {
                            //当cookie与产品id匹配时
                            existCookie = cookie;
                            break;
                        }
                }
                //当existCookie为空时
                if (null == existCookie) {
                    //新建一个cookie
                    existCookie = new Cookie("productIds", productId);
                    //调用同步coookie的方法
                    syncCookie("/browse", "setCookie.do", productId);
                } else {
                    //把productIds中的productId提取出来
                    String[] productIds = existCookie.getValue().split("%2C");
                    if (!"".equals(productIds)) {
                        /**
                         * 当productIds不为""字符串时
                         * 定义一个boolean控制是否添加新的cookie
                         */
                        StringBuilder stringBuilder = new StringBuilder();
                        Boolean addCookieFlag = false;
                        //遍历数组
                        for (String cookieId : productIds) {
                            //当cookie存在于原有cookies时,为真
                            Boolean cookieFlag = false;
                            if (cookieId.equals(productId)) {
                                //当productId与原有id匹配
                                cookieFlag = true;
                            }
                            //反馈addCookieFlag是否添加新的cookie
                            addCookieFlag = cookieFlag || addCookieFlag;
                        }
                        //根据addCookieFlag来决定是否添加新cookie
                        if (addCookieFlag) {
                            //存在即设置为原有cookie
                            existCookie.setValue(existCookie.getValue());
                            //把cookie值设置进session作用域
                            req.getSession().setAttribute("cookies",existCookie.getValue());
                            //调用同步cookie的方法
                            syncCookie("/browse", "setCookie.do", existCookie.getValue());
                        } else {
                            //不存在添加新cookie
                            for (String cookieId : productIds) {
                                //把原有id添加到stringBuilder
                                stringBuilder.append(cookieId + "%2C");
                            }
                            //添加新的cookie
                            stringBuilder.append(productId + "%2C");
                            //为cookie设置新的值
                            existCookie.setValue(stringBuilder.toString());
                            //把cookie值设置进session作用域
                            req.getSession().setAttribute("cookies",existCookie.getValue());
                            //调用同步cookie的方法
                            syncCookie("/browse", "setCookie.do", stringBuilder.toString());
                        }
                    }
                }
                //把cookie添加到当前服务器
                resp.addCookie(existCookie);
                //把id匹配到的产品放到request中
                req.setAttribute("product", ProduceService.getProduct(Long.valueOf(productId)));
                //转发回详情页面
                req.getRequestDispatcher("/WEB-INF/views/biz/detail.jsp").forward(req, resp);
            }
        } else if (Objects.equals("/detail/removeCookie.do", req.getServletPath())) {
            //接收cookie的值
            String cookies = req.getParameter("cookies");
            //把cookie写入response作用域中
            resp.addCookie(new Cookie("productIds", cookies));
            resp.setCharacterEncoding("UTF-8");
            resp.setContentType("application/text;charset=utf-8");
            //写入输出流
            PrintWriter out = null;
            try {
                out = resp.getWriter();
                out.write("ok");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (null != out) {
                    out.close();
                }
            }
        }
    }
}

BrowseServlet

package servlet;

import entity.Product;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import service.ProduceService;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 浏览记录servlet
 */

public class BrowseServlet extends HttpServlet {
    //创建java线程池对象,创建10个线程池
    private ExecutorService service = Executors.newFixedThreadPool(10);

    //实现同步cookie的方法
    private void syncCookie(String server, String method, String cookies) {
        //调用提交的方法
        service.submit(new Runnable() {
            @Override
            public void run() {
                //post请求调用
                HttpPost httpPost = new HttpPost(server + "/" + method+"?cookie="+cookies);
                CloseableHttpClient httpClient = null;
                CloseableHttpResponse response = null;
                try {
                    httpClient = HttpClients.createDefault();
                    response = httpClient.execute(httpPost);
                    HttpEntity entity = response.getEntity();
                    String responseContent = EntityUtils.toString(entity, "utf-8");
                    System.out.println("==============" + responseContent);
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (null != response) {
                        try {
                            response.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (null != httpClient) {
                        try {
                            httpClient.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(req.getServletPath());
        if (Objects.equals("/browse/list.do", req.getServletPath())) {
            String cookieProducts = null;
            List<Product> productList = new ArrayList<>();
            //从cookie中取出所有数据
            for (Cookie cookie : req.getCookies()) {
                if (Objects.equals("productIds", cookie.getName())) {
                    //把产品id提取出来
                    cookieProducts = cookie.getValue();
                    break;
                }
            }
            if (null != cookieProducts && !"".equals(cookieProducts)) {
                //把cookieProducts中的productId提取出来
                String[] productIds = cookieProducts.split("%2C");
                for (String productId : productIds) {
                    //调用得到商品的方法
                    productList.add(ProduceService.getProduct(Long.valueOf(productId)));
                }
            }
            //把productList放进request中
            req.setAttribute("products", productList);
            //转发回浏览记录页面
            req.getRequestDispatcher("/WEB-INF/views/biz/browse_list.jsp").forward(req, resp);
        } else if (Objects.equals("/browse/delete.do", req.getServletPath())) {
            //为删除浏览记录商品请求
            String productId = req.getParameter("productId");
            if (null != productId && !"".equals(productId)) {
                //从cookie中取出所有数据
                for (Cookie cookie : req.getCookies()) {
                    if (Objects.equals("productIds", cookie.getName())) {
                        //把productIds中的productId提取出来
                        String[] productIds = cookie.getValue().split("%2C");
                        StringBuilder stringBuilder = new StringBuilder();
                        for (String cookieId : productIds) {
                            if (cookieId.equals(productId)) {
                                continue;
                            }
                            //把剩下的id要回来
                            stringBuilder.append(cookieId + "%2C");
                        }
                        //为cookie设置新的值
                        cookie.setValue(stringBuilder.toString());
                        //调用同步cookie的方法
                        syncCookie("/detail","removeCookie.do",stringBuilder.toString());
                        resp.addCookie(cookie);
                    }
                }
                //重定向
                resp.sendRedirect("/browse/list.do");
            }
        } else if (Objects.equals("/browse/setCookie.do", req.getServletPath())) {
            //为其它应用传来的setCookie请求
            String cookies = req.getParameter("cookies");
            //把cookie写入response作用域中
            resp.addCookie(new Cookie("productIds", cookies));
            resp.setCharacterEncoding("UTF-8");
            resp.setContentType("application/text;charset=utf-8");
            //写入输出流
            PrintWriter out = null;
            try {
                out = resp.getWriter();
                out.write("ok");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (null != out) {
                    out.close();
                }
            }
        }
    }
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <!--欢迎页面-->
    <welcome-file-list>
        <welcome-file>/index.jsp</welcome-file>
    </welcome-file-list>
    <!--过滤器-->
    <filter>
        <filter-name>CharsetFilter</filter-name>
        <filter-class>filter.CharsetFilter</filter-class>
        <init-param>
            <param-name>charset</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharsetFilter</filter-name>
        <url-pattern>*.do</url-pattern>
    </filter-mapping>
    
    <servlet>
        <servlet-name>ProductServlet</servlet-name>
        <servlet-class>servlet.ProductServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ProductServlet</servlet-name>
        <url-pattern>/product/list.do</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>CartServlet</servlet-name>
        <servlet-class>servlet.CartServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>CartServlet</servlet-name>
        <url-pattern>/cart/cart.do</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>CartServlet</servlet-name>
        <url-pattern>/cart/decr.do</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>CartServlet</servlet-name>
        <url-pattern>/cart/incr.do</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>CartServlet</servlet-name>
        <url-pattern>/cart/delete.do</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>CartServlet</servlet-name>
        <url-pattern>/cart/list.do</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>FavoriteServlet</servlet-name>
        <servlet-class>servlet.FavoriteServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>FavoriteServlet</servlet-name>
        <url-pattern>/favorite/favorite.do</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>FavoriteServlet</servlet-name>
        <url-pattern>/favorite/delete.do</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>FavoriteServlet</servlet-name>
        <url-pattern>/favorite/list.do</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>DetailServlet</servlet-name>
        <servlet-class>servlet.DetailServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>DetailServlet</servlet-name>
        <url-pattern>/detail/detail.do</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>DetailServlet</servlet-name>
        <url-pattern>/detail/removeCookie.do</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>BrowseServlet</servlet-name>
        <servlet-class>servlet.BrowseServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>BrowseServlet</servlet-name>
        <url-pattern>/browse/list.do</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>BrowseServlet</servlet-name>
        <url-pattern>/browse/delete.do</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>BrowseServlet</servlet-name>
        <url-pattern>/browse/setCookie.do</url-pattern>
    </servlet-mapping>


    <error-page>
        <error-code>404</error-code>
        <location>/WEB-INF/views/error/404.jsp</location>
    </error-page>
    <error-page>
        <error-code>500</error-code>
        <location>/WEB-INF/views/error/500.jsp</location>
    </error-page>
</web-app>

用到的jar包

http://img1.sycdn.imooc.com//climg/5a942d100001094405130509.jpg

还有list.jsp和detail.jsp还有browse_list.jsp用的是这节课老师的代码没有修改

正在回答 回答被采纳积分+1

登陆购买课程后可参与讨论,去登陆

1回答
好帮手慕珊 2018-02-27 15:05:55

我运行了一下这个代码,首先如果直接访问路径localhost:8080/browse/setCookie.do是可以进入到BrowseServlet中执行的

关于syncCookie方法的问题,还是传参的时候出错了,方法形式如下:

http://img1.sycdn.imooc.com//climg/5a95020f00010ba807480039.jpg

第一个参数是server,第二个是web.xml中配置的路径,第三个是cookie的值,调用syncCookie方法的形式如下:

http://img1.sycdn.imooc.com//climg/5a95026f0001069610120028.jpg

cookie值是我测试时随便写的,你可以根据实际情况获取。

然后HttpPost这样写:

http://img1.sycdn.imooc.com//climg/5a9502ad0001315506320041.jpg,如果还有cookie值,就要接着拼接字符串,后面有?cookie=xxx类似的内容

也就是HttpPost中的参数拼接好后就是 http://localhost:8080/browse/setCookie.do  这样的链接。

另外,去单点登录的源码中找到这两个jar包导入

http://img1.sycdn.imooc.com//climg/5a950343000154f402550030.jpg

http://img1.sycdn.imooc.com//climg/5a95034c000196bd02560033.jpg

祝学习愉快!

  • 提问者 湛蓝牛仔 #1
    1.加上这两个jar包就能实现HttpPost httpPost = new HttpPost("http://localhost:8080"+server + "/" + method);的拦截了,这两个jar包的作用是什么? 2.但是拦截了的BrowseServlet中调用了resp.addCookie(new Cookie("productIds", cookies));在访问browse_list.jsp还是没有发现cookie的存在,添加的cookie去哪了呢? 3.还有syncCookie()的方法只能发送一次,第二次访问/detail/detail.do发现HttpPost提交不了post请求了,断点直接跳过了,为什么呢?
    2018-02-27 19:27:58
  • 提问者 湛蓝牛仔 #2
    还有直接在浏览器地址输入http://localhost:8080/browse/list.do?cookies=1%2%发现在访问browse_list.jsp就能发现cookie存在,而多线程的post请求就不行,为什么呢?
    2018-02-27 19:31:15
问题已解决,确定采纳
还有疑问,暂不采纳

恭喜解决一个难题,获得1积分~

来为老师/同学的回答评分吧

0 星
从网页搭建入门Java Web2018版
  • 参与学习           人
  • 提交作业       1088    份
  • 解答问题       10205    个

如果你有Java语言基础,又想以后从事Java Web开发,那么本路径是你的不二选择!本路径从网页搭建开始入手,通过大量案例来学习Java Web基础。定能助你完成Java Web小白的蜕变!

了解课程
请稍等 ...
意见反馈 帮助中心 APP下载
官方微信

在线咨询

领取优惠

免费试听

领取大纲

扫描二维码,添加
你的专属老师