国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

Spring Security 如何實現(xiàn)多種加密方案共存

安全 數(shù)據(jù)安全
這篇文章中,松哥給大家介紹了兩種密碼加密方案,但是兩種都是獨(dú)立使用的!能不能在同一個項目中同時存在多種密碼加密方案呢?答案是肯定的!

[[415547]]

這篇文章中,松哥給大家介紹了兩種密碼加密方案,但是兩種都是獨(dú)立使用的!能不能在同一個項目中同時存在多種密碼加密方案呢?答案是肯定的!

今天松哥就來和大家聊一聊,如何在 Spring Security 中,讓多種不同的密碼加密方案并存。

為什么要加密?常見的加密算法等等這些問題我就不再贅述了,大家可以參考之前的:Spring Boot 中密碼加密的兩種姿勢!,咱們直接來看今天的正文。

1.PasswordEncoder

在 Spring Security 中,跟密碼加密/校驗相關(guān)的事情,都是由 PasswordEncoder 來主導(dǎo)的,PasswordEncoder 擁有眾多的實現(xiàn)類:

這些實現(xiàn)類,有的已經(jīng)過期了,有的用處不大。對于我們而言,最常用的莫過于 BCryptPasswordEncoder。

PasswordEncoder 本身是一個接口,里邊只有三個方法:

  1. public interface PasswordEncoder { 
  2.  String encode(CharSequence rawPassword); 
  3.  boolean matches(CharSequence rawPassword, String encodedPassword); 
  4.  default boolean upgradeEncoding(String encodedPassword) { 
  5.   return false
  6.  } 
  • encode 方法用來對密碼進(jìn)行加密。
  • matches 方法用來對密碼進(jìn)行比對。
  • upgradeEncoding 表示是否需要對密碼進(jìn)行再次加密以使得密碼更加安全,默認(rèn)為 false。

PasswordEncoder 的實現(xiàn)類,則具體實現(xiàn)了這些方法。

2.PasswordEncoder 在哪里起作用

對于我們開發(fā)者而言,我們通常都是在 SecurityConfig 中配置一個 PasswordEncoder 的實例,類似下面這樣:

  1. @Bean 
  2. PasswordEncoder passwordEncoder() { 
  3.     return new BCryptPasswordEncoder(); 

剩下的事情,都是由系統(tǒng)調(diào)用的。今天我們就來揭開系統(tǒng)調(diào)用的神秘面紗!我們一起來看下系統(tǒng)到底是怎么調(diào)用的!

首先,松哥在前面的文章中和大家提到過,Spring Security 中,如果使用用戶名/密碼的方式登錄,密碼是在 DaoAuthenticationProvider 中進(jìn)行校驗的,大家可以參考:SpringSecurity 自定義認(rèn)證邏輯的兩種方式(高級玩法)。

我們來看下 DaoAuthenticationProvider 中密碼是如何校驗的:

  1. protected void additionalAuthenticationChecks(UserDetails userDetails, 
  2.   UsernamePasswordAuthenticationToken authentication) 
  3.   throws AuthenticationException { 
  4.  if (authentication.getCredentials() == null) { 
  5.   throw new BadCredentialsException(messages.getMessage( 
  6.     "AbstractUserDetailsAuthenticationProvider.badCredentials"
  7.     "Bad credentials")); 
  8.  } 
  9.  String presentedPassword = authentication.getCredentials().toString(); 
  10.  if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) { 
  11.   throw new BadCredentialsException(messages.getMessage( 
  12.     "AbstractUserDetailsAuthenticationProvider.badCredentials"
  13.     "Bad credentials")); 
  14.  } 

可以看到,密碼校驗就是通過 passwordEncoder.matches 方法來完成的。

那么 DaoAuthenticationProvider 中的 passwordEncoder 從何而來呢?是不是就是我們一開始在 SecurityConfig 中配置的那個 Bean 呢?

我們來看下 DaoAuthenticationProvider 中關(guān)于 passwordEncoder 的定義,如下:

  1. public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { 
  2.  private PasswordEncoder passwordEncoder; 
  3.  public DaoAuthenticationProvider() { 
  4.   setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()); 
  5.  } 
  6.  public void setPasswordEncoder(PasswordEncoder passwordEncoder) { 
  7.   this.passwordEncoder = passwordEncoder; 
  8.   this.userNotFoundEncodedPassword = null
  9.  } 
  10.  
  11.  protected PasswordEncoder getPasswordEncoder() { 
  12.   return passwordEncoder; 
  13.  } 

從這段代碼中可以看到,在 DaoAuthenticationProvider 創(chuàng)建之時,就指定了 PasswordEncoder,似乎并沒有用到我們一開始配置的 Bean?其實不是的!在 DaoAuthenticationProvider 創(chuàng)建之時,會制定一個默認(rèn)的 PasswordEncoder,如果我們沒有配置任何 PasswordEncoder,將使用這個默認(rèn)的 PasswordEncoder,如果我們自定義了 PasswordEncoder 實例,那么會使用我們自定義的 PasswordEncoder 實例!

從何而知呢?

我們再來看看 DaoAuthenticationProvider 是怎么初始化的。

DaoAuthenticationProvider 的初始化是在 InitializeUserDetailsManagerConfigurer#configure 方法中完成的,我們一起來看下該方法的定義:

  1. public void configure(AuthenticationManagerBuilder auth) throws Exception { 
  2.  if (auth.isConfigured()) { 
  3.   return
  4.  } 
  5.  UserDetailsService userDetailsService = getBeanOrNull( 
  6.    UserDetailsService.class); 
  7.  if (userDetailsService == null) { 
  8.   return
  9.  } 
  10.  PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class); 
  11.  UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class); 
  12.  DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); 
  13.  provider.setUserDetailsService(userDetailsService); 
  14.  if (passwordEncoder != null) { 
  15.   provider.setPasswordEncoder(passwordEncoder); 
  16.  } 
  17.  if (passwordManager != null) { 
  18.   provider.setUserDetailsPasswordService(passwordManager); 
  19.  } 
  20.  provider.afterPropertiesSet(); 
  21.  auth.authenticationProvider(provider); 

從這段代碼中我們可以看到:

  1. 首先去調(diào)用 getBeanOrNull 方法獲取一個 PasswordEncoder 實例,getBeanOrNull 方法實際上就是去 Spring 容器中查找對象。
  2. 接下來直接 new 一個 DaoAuthenticationProvider 對象,大家知道,在 new 的過程中,DaoAuthenticationProvider 中默認(rèn)的 PasswordEncoder 已經(jīng)被創(chuàng)建出來了。
  3. 如果一開始從 Spring 容器中獲取到了 PasswordEncoder 實例,則將之賦值給 DaoAuthenticationProvider 實例,否則就是用 DaoAuthenticationProvider 自己默認(rèn)創(chuàng)建的 PasswordEncoder。

至此,就真相大白了,我們配置的 PasswordEncoder 實例確實用上了。

3.默認(rèn)的是什么?

同時大家看到,如果我們不進(jìn)行任何配置,默認(rèn)的 PasswordEncoder 也會被提供,那么默認(rèn)的 PasswordEncoder 是什么呢?我們就從這個方法看起:

  1. public DaoAuthenticationProvider() { 
  2.  setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()); 

繼續(xù):

  1. public class PasswordEncoderFactories { 
  2.  public static PasswordEncoder createDelegatingPasswordEncoder() { 
  3.   String encodingId = "bcrypt"
  4.   Map<String, PasswordEncoder> encoders = new HashMap<>(); 
  5.   encoders.put(encodingId, new BCryptPasswordEncoder()); 
  6.   encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder()); 
  7.   encoders.put("MD4", new org.springframework.security.crypto.password.Md4PasswordEncoder()); 
  8.   encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5")); 
  9.   encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance()); 
  10.   encoders.put("pbkdf2", new Pbkdf2PasswordEncoder()); 
  11.   encoders.put("scrypt", new SCryptPasswordEncoder()); 
  12.   encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1")); 
  13.   encoders.put("SHA-256", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256")); 
  14.   encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder()); 
  15.   encoders.put("argon2", new Argon2PasswordEncoder()); 
  16.  
  17.   return new DelegatingPasswordEncoder(encodingId, encoders); 
  18.  } 
  19.  
  20.  private PasswordEncoderFactories() {} 

可以看到:

  1. 在 PasswordEncoderFactories 中,首先構(gòu)建了一個 encoders,然后給所有的編碼方式都取了一個名字,再把名字做 key,編碼方式做 value,統(tǒng)統(tǒng)存入 encoders 中。
  2. 最后返回了一個 DelegatingPasswordEncoder 實例,同時傳入默認(rèn)的 encodingId 就是 bcrypt,以及 encoders 實例,DelegatingPasswordEncoder 看名字應(yīng)該是一個代理對象。

我們來看下 DelegatingPasswordEncoder 的定義:

  1. public class DelegatingPasswordEncoder implements PasswordEncoder { 
  2.  private static final String PREFIX = "{"
  3.  private static final String SUFFIX = "}"
  4.  private final String idForEncode; 
  5.  private final PasswordEncoder passwordEncoderForEncode; 
  6.  private final Map<String, PasswordEncoder> idToPasswordEncoder; 
  7.  private PasswordEncoder defaultPasswordEncoderForMatches = new UnmappedIdPasswordEncoder(); 
  8.  public DelegatingPasswordEncoder(String idForEncode, 
  9.   Map<String, PasswordEncoder> idToPasswordEncoder) { 
  10.   if (idForEncode == null) { 
  11.    throw new IllegalArgumentException("idForEncode cannot be null"); 
  12.   } 
  13.   if (!idToPasswordEncoder.containsKey(idForEncode)) { 
  14.    throw new IllegalArgumentException("idForEncode " + idForEncode + "is not found in idToPasswordEncoder " + idToPasswordEncoder); 
  15.   } 
  16.   for (String id : idToPasswordEncoder.keySet()) { 
  17.    if (id == null) { 
  18.     continue
  19.    } 
  20.    if (id.contains(PREFIX)) { 
  21.     throw new IllegalArgumentException("id " + id + " cannot contain " + PREFIX); 
  22.    } 
  23.    if (id.contains(SUFFIX)) { 
  24.     throw new IllegalArgumentException("id " + id + " cannot contain " + SUFFIX); 
  25.    } 
  26.   } 
  27.   this.idForEncode = idForEncode; 
  28.   this.passwordEncoderForEncode = idToPasswordEncoder.get(idForEncode); 
  29.   this.idToPasswordEncoder = new HashMap<>(idToPasswordEncoder); 
  30.  } 
  31.  public void setDefaultPasswordEncoderForMatches( 
  32.   PasswordEncoder defaultPasswordEncoderForMatches) { 
  33.   if (defaultPasswordEncoderForMatches == null) { 
  34.    throw new IllegalArgumentException("defaultPasswordEncoderForMatches cannot be null"); 
  35.   } 
  36.   this.defaultPasswordEncoderForMatches = defaultPasswordEncoderForMatches; 
  37.  } 
  38.  
  39.  @Override 
  40.  public String encode(CharSequence rawPassword) { 
  41.   return PREFIX + this.idForEncode + SUFFIX + this.passwordEncoderForEncode.encode(rawPassword); 
  42.  } 
  43.  
  44.  @Override 
  45.  public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) { 
  46.   if (rawPassword == null && prefixEncodedPassword == null) { 
  47.    return true
  48.   } 
  49.   String id = extractId(prefixEncodedPassword); 
  50.   PasswordEncoder delegate = this.idToPasswordEncoder.get(id); 
  51.   if (delegate == null) { 
  52.    return this.defaultPasswordEncoderForMatches 
  53.     .matches(rawPassword, prefixEncodedPassword); 
  54.   } 
  55.   String encodedPassword = extractEncodedPassword(prefixEncodedPassword); 
  56.   return delegate.matches(rawPassword, encodedPassword); 
  57.  } 
  58.  
  59.  private String extractId(String prefixEncodedPassword) { 
  60.   if (prefixEncodedPassword == null) { 
  61.    return null
  62.   } 
  63.   int start = prefixEncodedPassword.indexOf(PREFIX); 
  64.   if (start != 0) { 
  65.    return null
  66.   } 
  67.   int end = prefixEncodedPassword.indexOf(SUFFIX, start); 
  68.   if (end < 0) { 
  69.    return null
  70.   } 
  71.   return prefixEncodedPassword.substring(start + 1, end); 
  72.  } 
  73.  
  74.  @Override 
  75.  public boolean upgradeEncoding(String prefixEncodedPassword) { 
  76.   String id = extractId(prefixEncodedPassword); 
  77.   if (!this.idForEncode.equalsIgnoreCase(id)) { 
  78.    return true
  79.   } 
  80.   else { 
  81.    String encodedPassword = extractEncodedPassword(prefixEncodedPassword); 
  82.    return this.idToPasswordEncoder.get(id).upgradeEncoding(encodedPassword); 
  83.   } 
  84.  } 
  85.  
  86.  private String extractEncodedPassword(String prefixEncodedPassword) { 
  87.   int start = prefixEncodedPassword.indexOf(SUFFIX); 
  88.   return prefixEncodedPassword.substring(start + 1); 
  89.  } 
  90.  private class UnmappedIdPasswordEncoder implements PasswordEncoder { 
  91.  
  92.   @Override 
  93.   public String encode(CharSequence rawPassword) { 
  94.    throw new UnsupportedOperationException("encode is not supported"); 
  95.   } 
  96.  
  97.   @Override 
  98.   public boolean matches(CharSequence rawPassword, 
  99.    String prefixEncodedPassword) { 
  100.    String id = extractId(prefixEncodedPassword); 
  101.    throw new IllegalArgumentException("There is no PasswordEncoder mapped for the id \"" + id + "\""); 
  102.   } 
  103.  } 

這段代碼比較長,我來和大家挨個解釋下:

  1. DelegatingPasswordEncoder 也是實現(xiàn)了 PasswordEncoder 接口,所以它里邊的核心方法也是兩個:encode 方法用來對密碼進(jìn)行編碼,matches 方法用來校驗密碼。
  2. 在 DelegatingPasswordEncoder 的構(gòu)造方法中,通過 通過傳入的兩個參數(shù) encodingId 和 encoders ,獲取到默認(rèn)的編碼器賦值給 passwordEncoderForEncode,默認(rèn)的編碼器實際上就是 BCryptPasswordEncoder。
  3. 在 encode 方法中對密碼進(jìn)行編碼,但是編碼的方式加了前綴,前綴是 {編碼器名稱} ,例如如果你使用 BCryptPasswordEncoder 進(jìn)行編碼,那么生成的密碼就類似 {bcrypt}$2a$10$oE39aG10kB/rFu2vQeCJTu/V/v4n6DRR0f8WyXRiAYvBpmadoOBE.。這樣有什么用呢?每種密碼加密之后,都會加上一個前綴,這樣看到前綴,就知道該密文是使用哪個編碼器生成的了。
  4. 最后 matches 方法的邏輯就很清晰了,先從密文中提取出來前綴,再根據(jù)前綴找到對應(yīng)的 PasswordEncoder,然后再調(diào)用 PasswordEncoder 的 matches 方法進(jìn)行密碼比對。
  5. 如果根據(jù)提取出來的前綴,找不到對應(yīng)的 PasswordEncoder,那么就會調(diào)用 UnmappedIdPasswordEncoder#matches 方法,進(jìn)行密碼比對,該方法實際上并不會進(jìn)行密碼比對,而是直接拋出異常。

OK,至此,相信大家都明白了 DelegatingPasswordEncoder 的工作原理了。

如果我們想同時使用多個密碼加密方案,看來使用 DelegatingPasswordEncoder 就可以了,而 DelegatingPasswordEncoder 默認(rèn)還不用配置。

4.體驗

接下來我們稍微體驗一下 DelegatingPasswordEncoder 的用法。

首先我們來生成三個密碼作為測試密碼:

  1. @Test 
  2. void contextLoads() { 
  3.     Map<String, PasswordEncoder> encoders = new HashMap<>(); 
  4.     encoders.put("bcrypt", new BCryptPasswordEncoder()); 
  5.     encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5")); 
  6.     encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance()); 
  7.     DelegatingPasswordEncoder encoder1 = new DelegatingPasswordEncoder("bcrypt", encoders); 
  8.     DelegatingPasswordEncoder encoder2 = new DelegatingPasswordEncoder("MD5", encoders); 
  9.     DelegatingPasswordEncoder encoder3 = new DelegatingPasswordEncoder("noop", encoders); 
  10.     String e1 = encoder1.encode("123"); 
  11.     String e2 = encoder2.encode("123"); 
  12.     String e3 = encoder3.encode("123"); 
  13.     System.out.println("e1 = " + e1); 
  14.     System.out.println("e2 = " + e2); 
  15.     System.out.println("e3 = " + e3); 

生成結(jié)果如下:

  1. e1 = {bcrypt}$2a$10$Sb1gAUH4wwazfNiqflKZve4Ubh.spJcxgHG8Cp29DeGya5zsHENqi 
  2. e2 = {MD5}{Wucj/L8wMTMzFi3oBKWsETNeXbMFaHZW9vCK9mahMHc=}4d43db282b36d7f0421498fdc693f2a2 
  3. e3 = {noop}123 

接下來,我們把這三個密碼拷貝到 SecurityConfig 中去:

  1. @Configuration("aaa"
  2. public class SecurityConfig extends WebSecurityConfigurerAdapter { 
  3.  
  4.     @Override 
  5.     @Bean 
  6.     protected UserDetailsService userDetailsService() { 
  7.  
  8.         InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); 
  9.         manager.createUser(User.withUsername("javaboy").password("{bcrypt}$2a$10$Sb1gAUH4wwazfNiqflKZve4Ubh.spJcxgHG8Cp29DeGya5zsHENqi").roles("admin").build()); 
  10.         manager.createUser(User.withUsername("sang").password("{noop}123").roles("admin").build()); 
  11.         manager.createUser(User.withUsername("江南一點雨").password("{MD5}{Wucj/L8wMTMzFi3oBKWsETNeXbMFaHZW9vCK9mahMHc=}4d43db282b36d7f0421498fdc693f2a2").roles("user").build()); 
  12.         return manager; 
  13.     } 
  14.  
  15.     @Override 
  16.     protected void configure(HttpSecurity http) throws Exception { 
  17.         http.authorizeRequests() 
  18.                 .antMatchers("/admin/**").hasRole("admin"
  19.                 .antMatchers("/user/**").hasRole("user"
  20.                 ... 
  21.     } 

這里三個用戶使用三種不同的密碼加密方式。

配置完成后,重啟項目,分別使用 javaboy/123、sang/123 以及 江南一點雨/123 進(jìn)行登錄,發(fā)現(xiàn)都能登錄成功。

5.意義何在?

為什么我們會有這種需求?想在項目種同時存在多種密碼加密方案?其實這個主要是針對老舊項目改造用的,密碼加密方式一旦確定,基本上沒法再改了(你總不能讓用戶重新注冊一次吧),但是我們又想使用最新的框架來做密碼加密,那么無疑,DelegatingPasswordEncoder 是最佳選擇。

好啦,這就是今天和小伙伴們分享的多種密碼加密方案問題,感興趣的小伙伴記得點個在看鼓勵下松哥哦~

本文轉(zhuǎn)載自微信公眾號「江南一點雨」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系江南一點雨公眾號。

 

責(zé)任編輯:武曉燕 來源: 江南一點雨
相關(guān)推薦

2022-06-16 10:38:24

URL權(quán)限源代碼

2021-12-28 11:13:05

安全認(rèn)證 Spring Boot

2009-11-03 14:19:53

2021-04-28 06:26:11

Spring Secu功能實現(xiàn)源碼分析

2021-05-31 10:47:17

SpringSecuritySession

2020-09-16 08:07:54

權(quán)限粒度Spring Secu

2022-06-04 12:25:10

解密加密過濾器

2021-03-09 13:18:53

加密解密參數(shù)

2024-10-18 08:00:00

SpringBoot框架開發(fā)

2021-07-12 12:20:08

Spring初始化方案

2020-10-25 09:04:46

數(shù)據(jù)加密數(shù)據(jù)泄露攻擊

2010-04-09 14:47:13

Windows7Ubuntu

2022-05-19 11:29:14

計時攻擊SpringSecurity

2021-04-23 07:33:10

SpringSecurity單元

2021-08-29 18:36:57

項目

2024-10-15 16:41:35

2009-06-17 13:53:57

Spring.jar

2025-03-05 07:58:30

2021-05-31 07:18:46

SpringSecurity信息

2009-05-18 17:16:50

點贊
收藏

51CTO技術(shù)棧公眾號

亚洲第一网中文字幕| 亚洲婷婷国产精品电影人久久| 亚洲天堂一区二区| 欧美日韩激情视频在线观看| 91免费国产视频| 色一区av在线| 欧美少妇一区二区| av一区二区三区在线| 91一区二区三区四区| gogo亚洲高清大胆美女人体| 中文字幕国产在线| 丁香花在线影院观看在线播放| 91精品视频免费| 色偷偷偷亚洲综合网另类| 色乱码一区二区三区88| 久久先锋影音av鲁色资源网| 一区二区激情| 色综合www| 夜鲁夜鲁夜鲁视频在线播放| 日韩欧美亚洲| 自慰无码一区二区三区| 国内精品久久国产| 欧美一区二三区| 亚洲欧美制服中文字幕| 色偷偷88欧美精品久久久| 久久亚洲一级片| 三级欧美韩日大片在线看| 不卡av一区二区| 久久久精品区| av中文资源在线资源免费观看| 最新亚洲人成网站在线观看| 波多野结衣家庭教师在线| 日本高清不卡一区二区三| 国产精品流白浆视频| 精品国产一区二区在线 | 国产精品二区不卡| 中文久久电影小说| 亚洲欧洲自拍| 免费黄色在线观看| 性网站在线播放| 国产特级毛片| 三级a在线观看| 日本a在线免费观看| 无码免费一区二区三区免费播放 | 高清亚洲成在人网站天堂| 亚洲精品av在线播放| 欧美三级视频在线观看| 一区二区三区在线视频观看58| 成+人+亚洲+综合天堂| 久久精品国产99国产精品| 日韩午夜av在线| 你懂的亚洲视频| 欧美亚洲国产精品久久| 美女午夜精品| 精品亚洲二区| 9i看片成人免费高清| 在线中文字幕电影| 激情影院在线观看| 高清在线观看av| 在线看视频你懂得| 日本五十路在线| 99爱视频在线观看| 亚洲欧美视频二区| 国产成人久久777777| 国产v片免费观看| 免费看毛片的网址| 97久久国产亚洲精品超碰热| 亚洲综合第一| 一区二区高清视频| 午夜精品美女久久久久av福利 | 色偷偷av一区二区三区乱| 日韩av网址在线| 亚洲国产精品专区久久| 亚洲国产成人爱av在线播放| 精品久久久网站| 亚洲国产成人av在线| 亚洲国产精品成人av| 3d成人h动漫网站入口| 精品日韩美女的视频高清| 婷婷开心久久网| 色呦呦日韩精品| 在线中文字幕一区| 欧美疯狂性受xxxxx喷水图片| 欧美日韩国产片| 欧美一级日韩不卡播放免费| 日韩欧美黄色影院| 亚洲成人手机在线| 欧美精品成人一区二区在线观看| 91在线观看免费观看| 水莓100在线视频| 国产精品日韩久久久| 亚洲国产精品网站| 成人91视频| 国内高清免费在线视频| 98视频在线噜噜噜国产| 午夜欧美大尺度福利影院在线看 | 国产日韩欧美激情| 久久久久久久久99精品| 97se亚洲国产综合自在线 | 青青草国产免费一区二区下载| 亚洲电影一区| 奇米777日韩| 欧美艳星kaydenkross| 日本综合视频| 日韩免费精品| 精品国产精品国产偷麻豆| 五月开心六月丁香综合色啪| 欧美日韩福利| 欧美黄色小说| 国产精品久久7| 日韩国产大片| 国产日韩v精品一区二区| 国产区欧美区日韩区| 中文字幕人成一区| 精品国内产的精品视频在线观看| 亚洲精品影视在线观看| 亚洲欧洲日产国码无码久久99| 国产精品高潮在线| 999日本视频| 亚洲欧洲精品一区二区| 国产亚洲精品网站| 校园春色影音先锋| 69xxxx欧美| 国产欧美一区二区三区精品酒店| 亚洲视频国产精品| 久久久久亚洲| 日韩高清电影一区| 99久久婷婷国产综合精品| 亚洲色图19p| 欧美精品丝袜久久久中文字幕| 亚洲欧洲高清在线| 97超碰国产精品女人人人爽| 亚洲尤物视频网| 最新中文字幕久久| 在线观看亚洲色图| av在线播放网| 国产69精品久久| 欧美一区三区| 亚洲综合精品四区| 26uuu欧美| 色综合久久综合网| 亚洲色图25p| 国产精品激情av电影在线观看| 欧美一区观看| 久久久久久久久久久久91| 国产视频网站在线| 成人精品一区二区三区电影| 欧美系列电影免费观看 | 欧美××××黑人××性爽| 日本一道高清一区二区三区| 亚洲经典视频在线观看| 91麻豆精东视频| 欧美日韩日日摸| 美女999久久久精品视频 | 久久这里有精品视频| 99九九视频| 黄色免费福利视频| 国产乱视频在线观看| 久久国内精品| 欧美激情视频一区二区三区免费| jvid福利写真一区二区三区| 色婷婷激情综合| 日韩中文字幕不卡视频| 99视频在线播放| 日本女优爱爱视频| 久操视频在线播放| 久9re热视频这里只有精品| 老牛国产精品一区的观看方式| 国产精品传媒视频| 亚洲成人中文字幕| 国产欧美 在线欧美| 久久99久久久久久| 成年在线观看免费人视频| 日韩在线观看中文字幕| 亚洲欧美网站| 亚洲欧美激情插 | 精品亚洲一区二区三区四区| 色一区在线观看| 成人精品毛片| sqte在线播放| 国产亚洲欧美日韩精品| 自拍av一区二区三区| 国产伦精品一区二区三区视频金莲| 精品国产一区二区三区在线观看| 精品国产欧美| 九九精品在线视频| 久久嫩草精品久久久精品一| 色婷婷精品大视频在线蜜桃视频 | 欧美有码在线观看视频| 免费成人高清在线视频| 国产精品日韩电影| 成人av在线观| 黑森林国产精品av| 国产精品日韩一区二区三区| 亚洲乱码中文字幕综合| 欧美第一视频| 国产免费xxx| 欧美午夜精品一区二区三区| 国产91一区| 午夜网站在线观看|