微信扫描登录

[复制链接]
发表于 2014-2-2 21:55:19 | 显示全部楼层 |阅读模式
用户通过扫描网页提供的二维码实现登陆信息获取 - m, U6 v3 V  N( X8 [
  1. <?php
    , c6 D* r* E/ x+ o& e4 ?. N
  2. /**/ [' E+ n5 b/ U7 {
  3. *  微信公众平台PHP-SDK! q# F5 O! I6 B$ Y" G- E% {
  4. *  Wechatauth为非官方微信登陆API# b4 }& w  W3 V6 t/ X4 J" V; S8 v
  5. *  用户通过扫描网页提供的二维码实现登陆信息获取
    ; S- t  H. e/ r- T/ }; F. f
  6. *  主要实现如下功能:& M8 h; |0 t# F
  7. *  get_login_code() 获取登陆授权码, 通过授权码才能获取二维码
    # n" r( d# S" d
  8. *  get_code_image($code='') 将上面获取的授权码转换为图片二维码
    8 }1 ~9 F+ q* S
  9. *  verify_code() 鉴定是否登陆成功,返回200为最终授权成功., M9 p, `5 A5 j. D) A/ m5 U
  10. *  get_login_cookie() 鉴定成功后调用此方法即可获取用户基本信息! b( d  z- g/ A
  11. *  sendNews($account,$title,$summary,$content,$pic,$srcurl='') 向一个微信账户发送图文信息, |3 F& b1 K6 [. K+ P! ~
  12. *  get_avatar($url) 获取用户头像图片数据8 l/ `' q- H9 l" g$ \: v4 S$ N
  13. *  @author dodge <dodgepudding@gmail.com>
      _8 P7 j, w1 C* }: T3 ?
  14. *  @link https://github.com/dodgepudding/wechat-php-sdk. L: O' J8 ^' W+ O3 R, ?
  15. *  @version 1.1; ?8 {2 t+ y1 W  X$ i
  16. *  
    ' P8 ?4 F/ c$ Q1 N0 e4 ^  q# ^4 r
  17. */' @" A+ p" l  _9 W+ k% Z5 K! q
  18. include "snoopy.class.php";
    - `! v) P2 @$ `1 h
  19. class Wechatauth
    * Z) q2 L- M# g2 ?. d  M2 h
  20. {
    ( ~1 Z. s9 O; K9 T- G
  21.         private $cookie;
    " N: u' \* A, n+ Y& S
  22.         private $_cookiename;
    - O) X2 R- F9 y
  23.         private $_cookieexpired = 3600;, [& ~' K& a" V# F1 I
  24.         private $_account = 'test';! M$ F" a; }5 H# e2 B6 P. ?5 \& }: O
  25.         private $_datapath = './data/cookie_';
    2 b. N0 P! e1 c) |
  26.         private $debug;
    ! j0 h3 K9 i7 W
  27.         private $_logcallback;, G) j& }- d2 w" N
  28.         public $login_user; //当前登陆用户, 调用get_login_info后获取
    . C5 |! A9 t: l1 E. t5 Z/ R
  29.         
    - C0 i. y& E7 G- r6 q/ d
  30.         public function __construct($options)
    / ?4 ]6 R* k, N! Z
  31.         {* ]* _2 y9 {! Q+ s
  32.                 $this->_account = isset($options['account'])?$options['account']:'';
    9 w1 j1 _2 I2 |' [# q7 o# }: z
  33.                 $this->_datapath = isset($options['datapath'])?$options['datapath']:$this->_datapath;
    ( J  |* [+ \  h+ i& U4 T
  34.                 $this->debug = isset($options['debug'])?$options['debug']:false;
    ; k1 N/ L+ M+ Q( |- }% i: m0 w
  35.                 $this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false;" F7 s7 V2 [6 o* c3 Y& a
  36.                 $this->_cookiename = $this->_datapath.$this->_account;8 }( i: k1 @5 }4 ?
  37.                 $this->getCookie($this->_cookiename);7 l' v4 ?  Q3 }- f7 F/ g% k
  38.         }$ m4 O9 i+ `$ @/ P2 U" V
  39.         /**
    - z7 {9 R7 m( N: L
  40.          * 把cookie写入缓存6 Q8 y8 a0 F' ~' q# C
  41.          * @param  string $filename 缓存文件名; Y1 W0 B$ [: Z; j: D1 j/ G0 [
  42.          * @param  string $content  文件内容- I5 n* w" d$ C# k
  43.          * @return bool
    2 p, v% U  v- `
  44.          */
    ; l# q& ^) Y2 I6 P7 t0 `7 W0 P
  45.         public function saveCookie($filename,$content){% q( C) e9 ^, Q% \& g* H" F2 q
  46.                 return file_put_contents($filename,$content);
    - M, W/ {% ]& W; ]& l% h6 k% g
  47.         }
    ) l' j" K' N0 _- g" g* [4 T9 K
  48. 4 N8 G2 B8 A5 _% ^% c
  49.         /**
    . I2 f; |2 p5 b8 W' y
  50.          * 读取cookie缓存内容
    1 A* ~9 s2 ?( M( B1 ~& ^: J
  51.          * @param  string $filename 缓存文件名
    3 M. Y+ E/ u1 }" v+ S4 B. b
  52.          * @return string cookie
    4 b$ d! j6 E6 \. p* y
  53.          */
    / i8 g0 ^# p" k
  54.         public function getCookie($filename){- l# M$ r; I4 Q2 O
  55.                 if (file_exists($filename)) {7 X. T/ J3 c1 @$ R$ s& `
  56.                         $mtime = filemtime($filename);
    , S, ~" o+ \' ^" p+ Q8 H- _
  57.                         if ($mtime<time()-$this->_cookieexpired) return false;+ v% f) c- o2 ?$ T! n
  58.                         $data = file_get_contents($filename);
    2 G4 c5 `& e( u% a1 Q' X9 K
  59.                         if ($data) $this->cookie = $data;+ y) x9 X6 e( _; J
  60.                 }
    & j# k9 C0 A; {2 n1 M
  61.                 return $this->cookie;& l- |6 R+ M! A8 Z! o8 @! F- |: S
  62.         }9 z: r4 u" A& n( O
  63.         
    7 v2 a& m6 x" N5 I( n1 K; h6 g
  64.         /*
    $ ]& N( q: R( o5 t1 }8 P( e% L; z
  65.          * 删除cookie
    ( |+ W# |; b# g  {. A  N5 F
  66.          */$ _5 O( E% \) I
  67.         public function deleteCookie($filename) {
    7 N4 `7 Q" Y/ [) g
  68.                 $this->cookie = '';3 s. d9 S$ B7 u' R% G( N9 ?5 R
  69.                 @unlink($filename);
    . o0 }& a% d& r( ~1 \* `
  70.                 return true;
    # V- W6 d4 A4 l/ b9 C, x* m
  71.         }
    5 X: H0 k  @! l: O
  72.         2 m' t" D. A6 C& e0 I# s
  73.         private function log($log){" ?* [' N- Q% l$ Q
  74.                 if ($this->debug && function_exists($this->_logcallback)) {
    $ p! @# ^* x; n" a
  75.                         if (is_array($log)) $log = print_r($log,true);) \6 J; C+ q# L6 l5 Y( a/ m: _
  76.                         return call_user_func($this->_logcallback,$log);
    6 t5 v9 Y5 b# Q. b4 `! m
  77.                 }
      t( {+ r( }' p" ]$ ?- H* _
  78.         }
    * N! ^1 y/ v- o* a  }8 ^
  79.         / \# s% U( K. N6 _8 h
  80.         /**" T3 g- n2 C! y/ b
  81.          * 获取登陆二维码对应的授权码
    7 U( [7 W( a6 ^' H
  82.          */. s+ }( g0 a8 q0 O1 C3 u: M/ R
  83.         public function get_login_code(){0 [# I1 E% a" g2 n* u
  84.                 if ($this->_logincode) return $this->_logincode;
    / I/ R9 V# {: a* C% w7 ?
  85.                 $t = time().strval(mt_rand(100,999));
    ; \2 B& s" m- H6 r  ?3 J! M% M
  86.                 $codeurl = 'https://login.weixin.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_='.$t;1 d2 d* L+ @3 ^' i% R) s
  87.                 $send_snoopy = new Snoopy; ! ~: V5 w' s: m% t9 h0 Z
  88.                 $send_snoopy->fetch($codeurl);* T1 X: c0 j/ ~! q" d
  89.                 $result = $send_snoopy->results;
    - j; [# |$ S2 k& F  D: a, S8 @
  90.                 if ($result) {8 X7 {3 P, C8 m5 ?  E, L
  91.                         preg_match("/window.QRLogin.uuid\s+=\s+"([^"]+)"/",$result,$matches);5 y3 t' o( X, g/ x2 q9 m
  92.                         if(count($matches)>1) {: H/ @0 r. y: ]
  93.                                 $this->_logincode = $matches[1];2 M8 ?. }. X! e
  94.                                 $_SESSION['login_step'] = 0;/ ~2 @3 T2 P: c# Z5 U
  95.                                 return $this->_logincode;7 l* h/ r# ]6 i7 Y& W2 M
  96.                         }
    3 a- F/ f' T4 m5 b! @+ l
  97.                 }
    $ P0 F, B3 j3 ^' V$ f- H
  98.                 return $result;1 }6 G3 ^5 U8 b6 p) O
  99.         }0 @9 {8 @5 p% K: |$ T

  100. 4 V/ @' R8 f0 W$ U: l
  101.         /**9 d; ?! \" n1 }& P8 s' W, p
  102.          * 通过授权码获取对应的二维码图片地址" [+ A) a5 a& i9 V
  103.          * @param string $code. z+ O4 a* x2 Z) K
  104.          * @return string image url2 w" k  X0 n9 i% u
  105.          */: @( [) {. f6 N: i; q" a* N
  106.         public function get_code_image($code=''){
    1 D' j! D  v/ F5 P  w  _$ L
  107.                 if ($code=='') $code = $this->_logincode;( C. G& H2 f7 e; J8 }
  108.                 if (!$code) return false;0 z8 u/ `% ]" Y* h3 }+ b' h3 m3 X
  109.                 return 'http://login.weixin.qq.com/qrcode/'.$this->_logincode.'?t=webwx';
      r4 z$ [( F& ]) n' \
  110.         }
    - _7 p! a3 o- z
  111.         & ~9 N/ W" l4 [. i& U
  112.         /**1 q  Q1 N& _( I
  113.          * 设置二维码对应的授权码& m$ M+ g/ p2 n: U  [) t
  114.          * @param string $code
    ' N- u# `. _. f& C6 ~4 |
  115.          * @return class $this" c, O, _1 {  [; f& f! }
  116.          */4 U1 a" Q& A  \3 P/ \  R
  117.         public  function set_login_code($code) {2 `1 D% a' `8 j' X5 Z, |8 v
  118.                 $this->_logincode = $code;
    7 }9 x  P& t; L; @3 `+ w* v6 b& M
  119.                 return $this;
    ; z  J% V* f4 V; h4 K, L2 ~* K
  120.         }( y1 P$ F: u: A% y* R
  121.         
    . x! |/ f; \+ g+ n" N: o
  122.         /**
    4 g  N! |, m, O  G  M" T
  123.          * 二维码登陆验证
    2 Z  k+ H9 I7 u9 q* l, }2 b
  124.          *: S, m. }; }! R% z+ T! |& a! y4 X
  125.          * @return status:  [( v( s  E& R3 k
  126.          * >=400: invaild code; 408: not auth and wait, 400,401: not valid or expired$ V, \+ E4 i" v% _. y
  127.          * 201: just scaned but not confirm2 ~, q$ I6 \. l
  128.          * 200: confirm then you can get user info2 e) c/ [# }0 M7 P
  129.          */7 W5 v# |# i4 M# r
  130.         public function verify_code() {" h& [2 X& m1 Q8 R: k
  131.                 if (!$this->_logincode) return false;3 s4 h$ D) U1 T  k' l
  132.                 $t = time().strval(mt_rand(100,999));8 o  U) Z7 Y9 t- q% U( Z

  133. . `& V* I  J) @& P0 V
  134.                         $url = 'https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?uuid='.$this->_logincode.'&tip=1&_='.$t;
    2 @( Z4 y, G8 y; l2 b( m
  135.                         $send_snoopy = new Snoopy; $ G  [% n! y, u& H; `
  136.                         $send_snoopy->referer = "https://wx.qq.com/";: y7 f9 M5 z, {; Y, ^# ?1 g0 F( y
  137.                         $send_snoopy->fetch($url);8 q7 y5 I5 \6 y) x* c; {
  138.                         $result = $send_snoopy->results;; D! a  _' @; U/ j; T$ b
  139.                         $this->log('step1:'.$result);
    / l0 V* X- u6 y; z
  140.                         if ($result) {# ~' r1 ]- k; u0 b
  141.                                 preg_match("/window\.code=(\d+)/",$result,$matches);
    . z# v  f! k% ^
  142.                                 if(count($matches)>1) {" Y# T: S. l. z/ T& L5 W' N$ G! D
  143.                                         $status = intval($matches[1]);2 G6 s$ G7 q! X8 g) a
  144.                                         if ($status==201) $_SESSION['login_step'] = 1;3 C( V$ F1 W& [& n6 [3 x; R$ @) I0 M
  145.                                         if ($status==200) {7 j( n$ |2 T, w6 b, H7 h
  146.                                                 preg_match("/ticket=([0-9a-z-_]+)&lang=zh_CN&scan=(\d+)/",$result,$matches);
    : k4 a5 S3 P# B- {: B# F
  147.                                                 $this->log('step2:'.print_r($matches,true));# e. c+ g" i; O9 |9 j  F* u! _
  148.                                                 if (count($matches)>1) {
    " r. f* {& u- ]( y8 U
  149.                                                         $ticket = $matches[1];1 U( Y* R- M( X- w! U; L5 k
  150.                                                         $scan = $matches[2];% R. u3 i! v3 \* t: k( A
  151.                                                         $loginurl = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket='.$ticket.'&lang=zh_CN&scan='.$scan.'&fun=new';
      b; x. q. R0 n% @4 d, k
  152.                                                         $send_snoopy = new Snoopy;
    - C0 m3 Y" W  j
  153.                                                         $send_snoopy->referer = "https://wx.qq.com/";: R5 m  U, ^, y. {5 Z- \; F$ c& l
  154.                                                         $send_snoopy->fetch($loginurl);
      k  m8 G+ r0 R2 {9 O( P2 Y
  155.                                                         $this->log('step3:'.print_r($send_snoopy->headers,true));4 V8 U/ z+ T3 Q  U- [" C! d
  156.                                                         foreach ($send_snoopy->headers as $key => $value) {/ g* H7 `# A8 m
  157.                                                                 $value = trim($value);. C. @" W" g0 g# x& l
  158.                                                                 if(strpos($value,'Set-Cookie: ') !== false){, K; F: c7 S4 T) r. {
  159.                                                                         $tmp = str_replace("Set-Cookie: ","",$value);
    5 D/ }$ q! Y$ r/ r% ?1 F) A7 I
  160.                                                                         $tmp = str_replace("Path=/","",$tmp);" y+ b# F+ l1 b  N
  161.                                                                         $tmp = str_replace("Domain=.qq.com; ","",$tmp);
      ^7 M7 |5 U9 \7 Q, R7 a# S
  162.                                                                         $cookie.=$tmp;( E# b+ t/ w; W4 F
  163.                                                                 }- O( }' M2 V, S' l+ i4 ]' b  A) Q
  164.                                                         }
    & E$ X! b* p9 P; C# `4 z
  165.                                                         $cookie .="Domain=.qq.com;";1 S: D2 w2 X( c) |3 n& o
  166.                                                         $this->cookie = $cookie;+ y! G2 R. R5 v* v$ W
  167.                                                         $this->saveCookie($this->_cookiename,$this->cookie);
    $ L0 j  u4 V0 T) h
  168.                                                 }
    + {# H: `5 S1 L% c& T
  169.                                         }
    6 }! q' Q6 @2 Q$ h* h3 W
  170.                                         return $status;
    ) M3 f9 n: ]( F$ U0 R$ n% h
  171.                                 }: J5 j- c, w$ N( W+ E$ v# C) Z6 ~
  172.                         }
    6 `8 V* y% W9 V7 y, j' f
  173.                 return false;
    7 c  C- [. P# s, `9 |; J! C
  174.         }* M  E; ?, g, A  ~7 X
  175.         6 u9 ~2 q# Z5 C; N2 _* n
  176.         /**+ G- W9 o9 X: S0 \8 @) j- A* Y
  177.          * 获取登陆的cookie
    8 r( }5 ^3 w/ H. x: t
  178.          *
    7 s# Y5 Q, Z0 a$ i& m1 j
  179.          * @param bool $is_array 是否以数值方式返回,默认否,返回字符串$ y2 J) c$ {3 F
  180.          * @return string|array
    & q, |9 u  x' O& G: y% J3 i4 Y5 c
  181.          */
    $ E1 s$ a8 h: w5 x" x: s4 l
  182.         public function get_login_cookie($is_array = false){
      n# @1 d- }' x" X  Y/ A1 i* U
  183.                 if (!$is_array)        return $this->cookie;
    5 F8 n; q% b1 C* y0 X
  184.                 $c_arr = explode(';',$this->cookie);  h+ _( J8 o& e8 y( C" k; N% x2 \
  185.                 $cookie = array();
    . F4 l* P, P- c- T
  186.                 foreach($c_arr as $item) {. D! H$ |* p/ T) N) `: S# x9 f
  187.                         $kitem = explode('=',trim($item));+ m# A9 p$ B: ^  X
  188.                         if (count($kitem)>1) {) p( J) T6 K/ d+ E! _
  189.                                 $key = trim($kitem[0]);" a9 ^4 H4 L7 E" L/ q2 n6 i
  190.                                 $val = trim($kitem[1]);
    # _7 p# R, h/ [) s& c6 R/ h
  191.                                 if (!empty($val)) $cookie[$key] = $val;
    ! |& r9 J9 P0 u6 A6 q1 A+ L2 J: Z2 O
  192.                         }
      T: e$ u- d4 C+ Z. e9 g& y0 s
  193.                 }
    $ D+ `: \: u/ L2 L% ~
  194.                 return $cookie;
    0 M8 w) i5 f! s9 ?: T
  195.         }) h7 N7 p' ?2 S) g
  196.         
    6 Y# D; b- k4 T' X
  197.         /**
    $ W& v8 S  k# [& t, O
  198.          *          授权登陆后获取用户登陆信息
    - e7 ]$ i! y) D; _4 M
  199.          */
      `3 W' Q  {1 g
  200.         public function get_login_info(){
    3 s! G% D% u( Y$ w" J6 l, Q! t' e
  201.                 if (!$this->cookie) return false;7 I; C0 c% P+ Q1 ]# X6 }9 ?9 s! v, W
  202.                 $t = time().strval(mt_rand(100,999));
    2 u+ ~4 G9 r, r
  203.                 $send_snoopy = new Snoopy; & ]" S8 P" n% G3 x1 m0 o
  204.                 $submit = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r='.$t;
    6 }# U' Y# \1 X
  205.                 $send_snoopy->rawheaders['Cookie']= $this->cookie;9 U5 u( o1 ?# d+ \0 |( `  A
  206.                 $send_snoopy->referer = "https://wx.qq.com/";$ l$ ^2 j8 D  K0 j% d/ x( Q
  207.                 $send_snoopy->submit($submit,array());
    4 Z( ?* E1 W* g# q5 p2 e
  208.                 $this->log('login_info:'.$send_snoopy->results);
    2 W! r! e) R  W
  209.                 $result = json_decode($send_snoopy->results,true);+ S8 {  K$ D7 T2 `, ?
  210.                 if ($result['BaseResponse']['Ret']<0) return false;
    # r8 v. b. d4 x* Q8 g
  211.                 $this->_login_user = $result['User'];; O8 o# H* }, {2 Q% b$ X! @
  212.                 return $result;
    # {0 M* h. N" f  X% R$ m- K* t, U9 W
  213.         }8 N; {, U, [& R- B' l  I
  214.         
      b. B) J# p" ?: u
  215.         /**/ X3 c, j6 E( s9 o; n& U- d5 \3 l( \
  216.          *  获取头像7 W( i2 n& U" C7 `* {
  217.          *  @param string $url 传入从用户信息接口获取到的头像地址7 L7 E, v' F! s9 ?- o: L
  218.          */
    ! }& p  w! X) g" d) |& j- M6 ^
  219.         public function get_avatar($url) {" L2 Q. a6 v1 T+ Q& k: i
  220.                 if (!$this->cookie) return false;' L3 o; L& ?% \' N7 C* p
  221.                 if (strpos($url, 'http')===false) {' ^& {; F  p& {$ X
  222.                         $url = 'http://wx.qq.com'.$url;
    * P1 {5 S. C9 O9 Z' V9 Z5 s
  223.                 }3 z+ d9 V- m9 o  X3 z
  224.                 $send_snoopy = new Snoopy;
    + P( ?/ U+ V1 e" X' C* D8 v
  225.                 $send_snoopy->rawheaders['Cookie']= $this->cookie;
    # k  |) Z3 u9 I* b
  226.                 $send_snoopy->referer = "https://wx.qq.com/";5 U8 w6 J( J4 K
  227.                 $send_snoopy->fetch($url);
    9 |# F! y% n: t$ m& S& \7 ?* W
  228.                 $result = $send_snoopy->results;
    ) ^4 _' @7 c. A! M5 w; l2 r0 @
  229.                 if ($result) 7 [& j! \6 |4 L+ J& n3 m! n
  230.                         return $result;
    ' b3 i/ W2 w0 ?( b- P
  231.                 else4 r/ A  `1 D, ?) B$ a
  232.                         return false;
    % C8 {* x- G8 M  s& \
  233.         }# ~' P7 Y. \0 N9 `0 M, o
  234.         7 [5 @; p1 c9 B3 c7 R' m/ T
  235.         /**
    + P% [/ N5 K$ I8 Y* A9 f8 Q- j
  236.          * 登出当前登陆用户
    ! {4 L! l/ _( B6 q6 L( f
  237.          */
    & I8 T4 Z- k+ v2 @8 h7 I
  238.         public function logout(){
    4 c& I) |/ q7 X6 }, P
  239.                 if (!$this->cookie) return false;! W' ~) Y6 `/ b
  240.                 preg_match("/wxuin=(\w+);/",$this->cookie,$matches);' [* a, l$ [( i+ v" X; ~
  241.                 if (count($matches)>1) $uid = $matches[1];
    3 x6 u+ G8 K9 Y" B8 t- B
  242.                 preg_match("/wxsid=(\w+);/",$this->cookie,$matches);
    & [1 P) K& {. R* j0 }
  243.                 if (count($matches)>1) $sid = $matches[1];
    , [. I+ s5 _8 o+ _7 L
  244.                 $this->log('logout: uid='.$uid.';sid='.$sid);
    ( L& |) k2 B+ h( R3 j( x" m# T2 i
  245.                 $send_snoopy = new Snoopy;
    2 A# G% N% z$ g% l
  246.                 $submit = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxlogout?redirect=1&type=1';
    , s. R% R3 k6 L3 w7 X5 @) U
  247.                 $send_snoopy->rawheaders['Cookie']= $this->cookie;
    0 Y$ H2 k) M# c5 \5 t  d) A& z
  248.                 $send_snoopy->referer = "https://wx.qq.com/";+ G; W+ V4 q, K  u$ m1 X
  249.                 $send_snoopy->submit($submit,array('uin'=>$uid,'sid'=>$sid));
    6 ]; V. Y- S' `. u
  250.                 $this->deleteCookie($this->_cookiename);
    - g, J8 ?/ W! q+ K- L4 S- g
  251.                 return true;
    9 g" l, t% N% N+ E' F2 r1 x
  252.         }
    * |2 f; @" ~9 ?9 h6 y9 k& b
  253. }
复制代码

' b1 S1 Y9 F5 O* a7 K# u# ?. O




上一篇:[Discuz插件] Discuz!微信登录插件配置前提
下一篇:Emacs逆袭:开发微信公众平台小游戏
回复

使用道具 举报

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏

快速回复 返回顶部 返回列表