我的日常

登录/注册
您现在的位置:论坛 盖世程序员(我猜到了开头 却没有猜到结局) 盖世程序员 > javax.crypto.BadPaddingException: Given final block ...
总共48087条微博

动态微博

查看: 9125|回复: 0

javax.crypto.BadPaddingException: Given final block not properly padded

[复制链接]
admin    

1244

主题

544

听众

1万

金钱

管理员

  • TA的每日心情

    2021-2-2 11:21
  • 签到天数: 36 天

    [LV.5]常住居民I

    管理员

    跳转到指定楼层
    楼主
    发表于 2015-02-07 13:35:06 |只看该作者 |倒序浏览
    现象描述:
    , G" u# W% Z1 n. c2 A; Q; q0 n  M2 C, e1 {1 R4 V3 m
    windows上加解密正常,linux上加密正常,解密时发生如下异常:' f" ~/ h+ ~5 M8 v6 Z

    + U3 I6 L0 m) |javax.crypto.BadPaddingException: Given final block not properly padded+ ^4 `  \4 g* N* }: U
           at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
    4 _6 ~6 O8 H; [: R       at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)/ Y( k1 J. M: m$ V
           at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
    9 }% c. l5 ]- ^4 l+ Y' T% n# o       at javax.crypto.Cipher.doFinal(DashoA13*..)2 m! H3 }! n+ G' f. T
           at chb.test.crypto.AESUtils.crypt(AESUtils.java:386). ]! l) R7 p0 M! F. i0 Y
           at chb.test.crypto.AESUtils.AesDecrypt(AESUtils.java:254)8 s1 {, |- O: V5 B2 u
           at chb.test.crypto.AESUtils.main(AESUtils.java:40)
    解决方法:4 d, K* y  P4 A, A5 V* N$ U
    . ~3 s9 ]6 _2 D" B" y4 e
    经过检查之后,定位在生成KEY的方法上,如下:
    ) |/ \: [- }. D: N
    1. public static SecretKey getKey (String strKey) {! S7 w0 i' J) b$ ~+ q( C
    2.          try {         # B: Y; b0 P( V' a8 U8 v- E5 [
    3.             KeyGenerator _generator = KeyGenerator.getInstance( "AES" );, j* v. s/ C- F2 ?. y0 H5 u3 E
    4.             _generator.init(128, new SecureRandom(strKey.getBytes()));
      ( N: F( j' w, a! D$ U
    5.                 return _generator.generateKey();
      # u! i% }) A) e+ b7 e* ^+ i0 V
    6.         }  catch (Exception e) {
      # A9 w8 C# N! T6 u) o* m
    7.              throw new RuntimeException( " 初始化密钥出现异常 " );
      . O  A& ~0 r: S6 q0 e5 C
    8.         }6 u) _* M: f% `4 t- i
    9.       }
    复制代码
    修改到如下方式,问题解决:
    . E. n5 m; u) `4 p, f1 @
    1.   public static SecretKey getKey(String strKey) {
      # d1 B/ _9 w; U/ q  z# s
    2.          try {         1 p1 S; r$ x2 [9 Y
    3.             KeyGenerator _generator = KeyGenerator.getInstance( "AES" );4 T5 C# |. I* N/ V4 A, ]6 m5 X5 N
    4.              SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
      9 U9 I# H" P$ ], b% d2 ^
    5.             secureRandom.setSeed(strKey.getBytes());
      : t8 g8 N0 q$ W* O' O: K! `5 \
    6.             _generator.init(128,secureRandom);) R; {% ~+ c& ?, o% P
    7.                 return _generator.generateKey();
      1 h( z7 o" m& r# `4 i8 o
    8.         }  catch (Exception e) {
      " u2 D: ~8 t( J2 @& ~
    9.              throw new RuntimeException( " 初始化密钥出现异常 " );' R5 k" @; Y/ u) h* h/ ?% u
    10.         }0 m& u: B% k  N2 N) c, O6 @( s
    11.       }
    复制代码
    原因分析
    5 o- n' _8 G4 B; ESecureRandom 实现完全隨操作系统本身的內部狀態,除非調用方在調用 getInstance 方法之後又調用了 setSeed 方法;该实现在 windows 上每次生成的 key 都相同,但是在 solaris 或部分 linux 系统上则不同。
    5 Q/ _2 v4 C. D. X* R! l+ W6 j8 ]( c  ^; R- o2 U/ r
    原因二:
    ; @  l! B  p( ?2 q* q. u, S, R! `0 r5 t; X7 J- V3 L) K
    1、加密完byte[] 后,需要将加密了的byte[] 转换成base64保存,如:
    0 n8 s( S% w% g7 A& v0 i9 P8 F) R- l( ?BASE64Encoder base64encoder = new BASE64Encoder();
    8 _8 B. w$ ~+ f9 B9 D) U/ FString encode=base64encoder.encode(bytes);
    0 G; m& R% C6 h& J9 s
    ! w, u% [) b  F0 {% q2、解密前,需要将加密后的字符串从base64转回来再解密,如: $ q! @, g2 [  Q1 W8 W% f# _  j
    BASE64Decoder base64decoder = new BASE64Decoder();
      [- R. L0 z: |byte[] encodeByte = base64decoder.decodeBuffer(str);
    7 n- Z6 N4 _! S* i, M" q3 t0 @
    ) D6 g  v! ~+ K. |& \1 l, A
    6 o; w* |6 \0 F- r完整例子:
    1 Z* U% F0 o) I3 a8 h& p5 ?
    1. package com.travelsky.tdp.pkgStock.util;
      $ x# ~" b. q3 U( e; D

    2. , ]) N1 J" L0 I8 |  u$ S3 ]
    3. import java.io.IOException;
      . H  n" b) v4 t2 O7 L9 k8 f
    4. import java.io.UnsupportedEncodingException;
      / O: i) d( R) l& X$ P( e5 `
    5. import java.security.InvalidKeyException;' O8 c0 b  u; i( o
    6. import java.security.NoSuchAlgorithmException;
      5 o. W) N+ a7 N! ~. V3 @' u1 E
    7. import java.security.SecureRandom;% l/ q! r4 i9 x$ B5 ~" V

    8. 7 W5 j: H9 K% ^% P' ?
    9. import javax.crypto.BadPaddingException;
      4 P2 ?, M2 f2 f! p8 ?3 `
    10. import javax.crypto.Cipher;2 E0 T0 I$ R( j! x  C
    11. import javax.crypto.IllegalBlockSizeException;6 r6 d; L5 G" p4 I
    12. import javax.crypto.KeyGenerator;+ ~# b3 S8 Y* Q' E
    13. import javax.crypto.NoSuchPaddingException;
      . e' D; t" k* a- o$ j
    14. import javax.crypto.SecretKey;
      6 e( C& U* C, q, S/ F
    15. import javax.crypto.spec.SecretKeySpec;* N& x0 b# a) ]- c% H( W. e9 P
    16. & o  }! B& s7 W- I1 {& }. ~; S# h
    17. import sun.misc.BASE64Decoder;" }9 E3 J( g7 M9 U- a' ?
    18. import sun.misc.BASE64Encoder;
      ' y5 O9 @% K' N1 S. v- |( d
    19. + ^$ s, o& K) N6 q
    20. public class SecurityAES {
      ' _/ t, e2 y( s9 m; n; U
    21.         private final static String encoding = "UTF-8";
        d. `( |& C6 ?" @" y
    22.         /**
      ) R2 w/ ?( i; e
    23.          * AES加密
      - l* \2 z9 f& ~% B9 V
    24.          *
      + p' v( }! r# o* A: g2 y' i
    25.          * @param content
      4 ~) M" I; ]: M5 O% G* W
    26.          * @param password
      , i( Z  M, j% e* |
    27.          * @return
      % L0 p6 P" p7 p0 Q( u  S& C
    28.          */5 R5 g: L( V2 W: p; H$ R5 A% }
    29.         public static String encryptAES(String content, String password) {; |2 U7 t+ u% \- p! C4 s
    30.                 byte[] encryptResult = encrypt(content, password);9 F$ `0 ^* ]2 U9 P$ ~; Q
    31.                 String encryptResultStr = parseByte2HexStr(encryptResult);
      . @6 x) j) h( l1 E" {" }0 F2 H- ~
    32.                 // BASE64位加密/ {% `& H; ^1 h) b5 n- C
    33.                 encryptResultStr = ebotongEncrypto(encryptResultStr);6 y+ V1 F: a* E  Q+ I- z
    34.                 return encryptResultStr;
      / [. Y8 R2 e- u3 Q
    35.         }
      * O* O% A8 F1 x- U9 R8 v$ ]  c

    36. ! ]/ h; f+ d% C$ v6 l! S' O" l2 W
    37.         /**
      4 L9 d) ^& i& @  s$ r2 S
    38.          * AES解密
      : @  e! P, E8 Y1 {- L" J
    39.          *
      ! Q: I) i0 G/ G1 l
    40.          * @param encryptResultStr
      4 O- h& q* K. J1 _7 c
    41.          * @param password
      ! k. y/ }% W* p
    42.          * @return# r1 C) w' M1 c" u/ o' [
    43.          */9 Q2 s5 p. l2 R% o
    44.         public static String decrypt(String encryptResultStr, String password) {( V/ N& z  C' K2 _' z: B
    45.                 // BASE64位解密9 I% B* c0 x* O7 {, k  X
    46.                 String decrpt = ebotongDecrypto(encryptResultStr);: q  {4 ^! u# A4 U3 b" A
    47.                 byte[] decryptFrom = parseHexStr2Byte(decrpt);
      4 ?8 K8 w0 Z1 `  v
    48.                 byte[] decryptResult = decrypt(decryptFrom, password);- r/ ^8 V* {  N- N
    49.                 return new String(decryptResult);+ i/ r/ R* Q$ Z: f* a
    50.         }
      9 f5 x; v0 C3 U& y( h1 D! K( p& O+ S% l

    51. 0 n% ]5 H& r/ i$ p1 ?( `
    52.                 /**
      3 }9 t; h) G7 q
    53.          * 加密字符串# m$ n/ F2 h' |9 o
    54.          */1 z& j4 T. L$ E! X4 t
    55.         public static String ebotongEncrypto(String str) {) q  ~9 h9 ~3 s% M. V! a
    56.                 BASE64Encoder base64encoder = new BASE64Encoder();7 L# b9 n7 B4 z8 r: ]
    57.                 String result = str;
      ! `7 w8 s# X3 Z% Q+ T
    58.                 if (str != null && str.length() > 0) {
      * a  F2 D3 H" z# x1 t* h/ [" S; H
    59.                         try {+ F. ^& ?' A: H" N& g+ ]
    60.                                 byte[] encodeByte = str.getBytes(encoding);9 o! X7 L# A* |' T( ^
    61.                                 result = base64encoder.encode(encodeByte);
      4 ]# {" o8 |9 C1 \3 t1 c5 \3 _
    62.                         } catch (Exception e) {) ~: f2 B7 J2 x
    63.                                 e.printStackTrace();
      2 Q" m+ }+ ]- ^2 }( |$ E
    64.                         }/ o: x# |+ e. c2 J
    65.                 }
      . m6 l3 k/ x& o+ p0 k# W: I
    66.                 //base64加密超过一定长度会自动换行 需要去除换行符* {* V* c- |' O5 Y
    67.                 return result.replaceAll("\r\n", "").replaceAll("\r", "").replaceAll("\n", "");
      4 J6 Y5 O. M' C( w  P# y4 A- @
    68.         }
      ) G4 Q4 u/ Z3 J8 ~! Y5 p5 T

    69. & ~9 G7 @: @. l
    70.         /**$ v0 y. N  ?" C6 B' Z
    71.          * 解密字符串
      5 Q' b8 Q. \' r, O$ `
    72.          */# W# J) V1 h% s
    73.         public static String ebotongDecrypto(String str) {3 ]- m4 J) T' R3 L2 j! b7 @) [- e
    74.                 BASE64Decoder base64decoder = new BASE64Decoder();- A- F0 k' G$ Y6 T$ Z
    75.                 try {
      2 g# v5 G4 m. \- M1 m! |
    76.                         byte[] encodeByte = base64decoder.decodeBuffer(str);: O5 X/ K! X$ `3 _. N
    77.                         return new String(encodeByte);
      $ {3 t2 E( w0 `4 h4 O3 C
    78.                 } catch (IOException e) {
      2 J% y3 T& ^8 v3 v/ i& N( W
    79.                         e.printStackTrace();
      9 s& G- K, o0 R( J9 l' ], V
    80.                         return str;
      : H7 _- t9 x/ C- _- R% x
    81.                 }! G  R  e& e: ^+ t5 N2 N% I
    82.         }3 {! D  @3 M. }  `. t
    83.         /**  9 F. Q3 X  c; z" t4 H3 k
    84.          * 加密  
      1 e2 W# O" {8 ^$ W; H' S% c7 L9 B
    85.          *   - ]) L  t+ q0 h/ ]
    86.          * @param content 需要加密的内容  
        x% z3 J  C0 r) s- I) \: Z
    87.          * @param password  加密密码  
      % K8 h. s( K% e- ~
    88.          * @return  
      # w; J4 N( X& |" A+ D
    89.          */  7 k- o8 }+ D& a; [- y  Y
    90.         private static byte[] encrypt(String content, String password) {   
      ( f+ V- S4 R, I
    91.                 try {              * X. x$ m+ Y2 E, y$ s: P1 G" i
    92.                         KeyGenerator kgen = KeyGenerator.getInstance("AES");
      1 X+ B( L! _8 y, z
    93.                         //防止linux下 随机生成key
      ; g; m, W0 p+ e) K0 o
    94.                         SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );   
      3 V; @* L# J2 A0 s9 z6 Y" e% l" {
    95.                         secureRandom.setSeed(password.getBytes());   
      0 {* Z" |1 f- \: N. N
    96.                         kgen.init(128, secureRandom);
      , t9 _7 P/ ~/ t3 ~8 {2 q
    97.                         //kgen.init(128, new SecureRandom(password.getBytes()));   6 x  w5 e7 b  U6 ?$ `
    98.                         SecretKey secretKey = kgen.generateKey();   8 c7 k$ }4 ]) @! L% I
    99.                         byte[] enCodeFormat = secretKey.getEncoded();   ! R1 h6 O4 Z, V) D0 i4 }* b
    100.                         SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");   
      / {2 T( g& y! S8 [
    101.                         Cipher cipher = Cipher.getInstance("AES");// 创建密码器   $ @  _+ K3 y" y% ^6 P- o
    102.                         byte[] byteContent = content.getBytes("utf-8");   ) _5 J, [0 w( C; I
    103.                         cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化   . e) O. k" c# G" ]
    104.                         byte[] result = cipher.doFinal(byteContent);   ) H- f- j6 o: X
    105.                         return result; // 加密   * a% m) ?. u7 d% L4 u
    106.                 } catch (NoSuchAlgorithmException e) {   % A, h5 \% d! Q: L
    107.                         e.printStackTrace();   6 w- Y; `/ k8 P; w0 M
    108.                 } catch (NoSuchPaddingException e) {   
      $ j/ Q! T7 W, b; L
    109.                         e.printStackTrace();   * f# A# s# N6 L* z$ q8 q+ R* O
    110.                 } catch (InvalidKeyException e) {   
        |) E% F  I5 X  Q+ h, S
    111.                         e.printStackTrace();   ) ~5 ]) M( L+ Y2 Z7 U
    112.                 } catch (UnsupportedEncodingException e) {   
      1 X; F/ b: E8 f! x; v* v
    113.                         e.printStackTrace();   
      8 N  h: I* k% b: p3 b
    114.                 } catch (IllegalBlockSizeException e) {   
        B# s# _4 O! p- d- Q3 }5 }; H
    115.                         e.printStackTrace();   , T9 Y6 e6 I) t/ g
    116.                 } catch (BadPaddingException e) {   7 A2 @: g! c/ N* O
    117.                         e.printStackTrace();   
      + G; }" e$ P) d( t/ t
    118.                 }   
        j5 j* }1 k' S7 s: j
    119.                 return null;   
      $ A0 y$ w. K5 j/ k8 O. T, Q( k
    120.         }  
      # @% ?" v8 _  j

    121.   s1 y% ?8 n1 N9 e" n- m' y

    122. 0 ]/ S4 F! H2 u! n; T
    123.         /**解密  # f- N% @9 b# U: I- d8 M; u' ^
    124.          * @param content  待解密内容  
      & e% ~* x0 R1 F# t3 X9 i0 R- e
    125.          * @param password 解密密钥  
      ' _1 P" l$ t: E8 T7 u# K: v
    126.          * @return  ! e9 s4 t" |( \
    127.          */  
      ( `2 @1 [7 w& c7 U" l6 N8 R
    128.         private static byte[] decrypt(byte[] content, String password) {   : p& h5 U; `2 {& H4 L8 W
    129.                 try {   ( _  t; X+ ]2 m9 \9 I
    130.                          KeyGenerator kgen = KeyGenerator.getInstance("AES"); + E1 z6 `. b9 F/ `' o0 x: P
    131.                        //防止linux下 随机生成key& l: K' f/ \- ~6 O
    132.                              SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );   - @7 r, z7 n' R: Q1 F
    133.                              secureRandom.setSeed(password.getBytes());   
      8 H- H; H7 j, [! j/ ~
    134.                              kgen.init(128, secureRandom);
      9 m& s: n) {( W0 w
    135.                          //kgen.init(128, new SecureRandom(password.getBytes()));   # m" U2 k6 U: H' M  _
    136.                          SecretKey secretKey = kgen.generateKey();   0 o8 M, |- c: ^2 h
    137.                          byte[] enCodeFormat = secretKey.getEncoded();   $ i5 E. l' L0 O: F3 b1 j0 J- ?
    138.                          SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");               
      5 y* i3 V, ?# ]* {5 Y
    139.                          Cipher cipher = Cipher.getInstance("AES");// 创建密码器   ! \% c9 a; v5 p  p' d& G
    140.                         cipher.init(Cipher.DECRYPT_MODE, key);// 初始化   
      ) t7 `% Q- S: T  X: q4 i# t5 Q' y6 y1 `
    141.                         byte[] result = cipher.doFinal(content);   
      ( @( Y- e( l/ K
    142.                         return result; // 加密   
      . B: i2 _( z1 M+ m* |4 T+ r
    143.                 } catch (NoSuchAlgorithmException e) {   * r' u; V1 f7 z7 W. u) t1 J
    144.                         e.printStackTrace();   
      8 N& P3 g2 h& X) r& u) ]3 P
    145.                 } catch (NoSuchPaddingException e) {   / ]4 O9 n% n0 k# p; G
    146.                         e.printStackTrace();   
        `4 r5 _6 T6 R
    147.                 } catch (InvalidKeyException e) {   
        H# S3 [# Q) T  t3 d' I0 G6 |
    148.                         e.printStackTrace();   
      & y, s. _5 b/ S0 I
    149.                 } catch (IllegalBlockSizeException e) {   
      & Z( d  v0 s: w* B
    150.                         e.printStackTrace();   * M% Y' E. ~7 o. R
    151.                 } catch (BadPaddingException e) {   
      : E$ S' ^( F9 r" c! l% s
    152.                         e.printStackTrace();   
      9 @! W6 r* G' @' k9 m
    153.                 }   4 S$ F5 n0 O% a& D8 H5 H" i3 b6 S
    154.                 return null;   ' F# a( z/ @" Y6 P0 P& r
    155.         }  5 V. U8 g% y) H/ Q" K
    156. ; y* q( i2 G2 U* c. Z3 O
    157.         /**将二进制转换成16进制  5 P8 Y& ^& z" x% E
    158.          * @param buf  ) U* B8 N2 E/ |- Q4 ^
    159.          * @return  + ^2 m. _$ T+ X# B) |) L$ q
    160.          */  
      8 W" n. f* v3 o9 @1 z" W
    161.         public static String parseByte2HexStr(byte buf[]) {   
      ) c! @) Q2 ~+ M* X
    162.                 StringBuffer sb = new StringBuffer();   ' j' n7 K& I) r! g, {
    163.                 for (int i = 0; i < buf.length; i++) {   2 F8 j. m& g2 f9 ?$ v
    164.                         String hex = Integer.toHexString(buf[i] & 0xFF);   
      # n+ ~& B% ~& ?  O% ~) c3 P
    165.                         if (hex.length() == 1) {   
      + I/ T8 M) @( w1 f
    166.                                 hex = '0' + hex;   , b4 l( N; f9 E0 V! `' H
    167.                         }   
      * B/ B, [. N* n5 r4 A* ]
    168.                         sb.append(hex.toUpperCase());   
      9 m6 d) `0 B. _4 [1 Z
    169.                 }   + M, x. Y! `- i% N
    170.                 return sb.toString();   
      ' E6 R% E; n3 V% L; C, j8 s
    171.         }  5 T4 R; ?7 A  x9 W

    172. 4 m# P' D3 w3 {
    173. + V$ K* }6 ?8 d  l* \
    174.         /**将16进制转换为二进制  
      2 f1 k0 Y2 h: h# j/ E. E
    175.          * @param hexStr  * O6 [, Q2 ~0 O. h8 g
    176.          * @return  
      " Q# H& J( k! Y: `2 z- G8 u
    177.          */  
      3 Y# D' p1 A1 @- M, c4 K7 A) m- q1 R
    178.         public static byte[] parseHexStr2Byte(String hexStr) {   % S& B% I( z! T1 e( `
    179.                 if (hexStr.length() < 1)   ) m- L/ g: _1 V( c: k+ D. |9 C8 D
    180.                         return null;   
      5 ^$ }, M7 {. `  h; ?$ Z6 q" l5 @9 M
    181.                 byte[] result = new byte[hexStr.length()/2];   
      8 @2 t5 V8 ?$ @% ]( ?
    182.                 for (int i = 0;i< hexStr.length()/2; i++) {     Q2 j7 `" C* Q" l; U1 ~
    183.                         int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);   
      8 D" O) ^- D% s0 k4 ?% w: a
    184.                         int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);   
      7 i5 }( ^3 d" F+ |- E# c
    185.                         result[i] = (byte) (high * 16 + low);     `, k$ [% B# _( U
    186.                 }   1 ~+ [1 O) P" `% F) v
    187.                 return result;   6 j( Q5 |; v$ D* W7 B+ z
    188.         }  
      6 F  T3 x0 c) }* c! J. M% k# [
    189. * \4 z, _& g; E
    190. + `: C. Q* x' e5 |% L; u- z
    191.        
      ! ?7 M: E4 s( k1 p# T+ }4 t3 O
    192. }( V, r! }+ N* l. O8 W' x: I0 }4 ?
    复制代码
    & @( y; T" W# ?1 Z8 _9 i4 F6 L
    以上在linux测试ok 原文转自:http://konglx.iteye.com/blog/954085$ x( k3 I8 t" n  j" E- Y8 B  u
    ( q3 L7 {! q  q2 `  M

    / v! O; w. L5 q1 L$ U/ O* J: W
    ) w$ R2 J. w6 l9 z8 \3 M& E1 g

    科帮网 1、本主题所有言论和图片纯属会员个人意见,与本社区立场无关
    2、本站所有主题由该帖子作者发表,该帖子作者与科帮网享有帖子相关版权
    3、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和科帮网的同意
    4、帖子作者须承担一切因本文发表而直接或间接导致的民事或刑事法律责任
    5、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
    6、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除并致以最深的歉意
    7、科帮网管理员和版主有权不事先通知发贴者而删除本文


    JAVA爱好者①群:JAVA爱好者① JAVA爱好者②群:JAVA爱好者② JAVA爱好者③ : JAVA爱好者③

    相关帖子
      快速回复
      您需要登录后才可以回帖 登录 | 立即注册

         

      关闭

      站长推荐上一条 /1 下一条

      发布主题 快速回复 返回列表 联系我们 官方QQ群 科帮网手机客户端
      快速回复 返回顶部 返回列表