精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Spring Security 如何實現多種加密方案共存

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

[[415547]]

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

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

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

1.PasswordEncoder

在 Spring Security 中,跟密碼加密/校驗相關的事情,都是由 PasswordEncoder 來主導的,PasswordEncoder 擁有眾多的實現類:

這些實現類,有的已經過期了,有的用處不大。對于我們而言,最常用的莫過于 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 方法用來對密碼進行加密。
  • matches 方法用來對密碼進行比對。
  • upgradeEncoding 表示是否需要對密碼進行再次加密以使得密碼更加安全,默認為 false。

PasswordEncoder 的實現類,則具體實現了這些方法。

2.PasswordEncoder 在哪里起作用

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

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

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

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

我們來看下 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 中關于 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 創建之時,就指定了 PasswordEncoder,似乎并沒有用到我們一開始配置的 Bean?其實不是的!在 DaoAuthenticationProvider 創建之時,會制定一個默認的 PasswordEncoder,如果我們沒有配置任何 PasswordEncoder,將使用這個默認的 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. 首先去調用 getBeanOrNull 方法獲取一個 PasswordEncoder 實例,getBeanOrNull 方法實際上就是去 Spring 容器中查找對象。
  2. 接下來直接 new 一個 DaoAuthenticationProvider 對象,大家知道,在 new 的過程中,DaoAuthenticationProvider 中默認的 PasswordEncoder 已經被創建出來了。
  3. 如果一開始從 Spring 容器中獲取到了 PasswordEncoder 實例,則將之賦值給 DaoAuthenticationProvider 實例,否則就是用 DaoAuthenticationProvider 自己默認創建的 PasswordEncoder。

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

3.默認的是什么?

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

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

繼續:

  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 中,首先構建了一個 encoders,然后給所有的編碼方式都取了一個名字,再把名字做 key,編碼方式做 value,統統存入 encoders 中。
  2. 最后返回了一個 DelegatingPasswordEncoder 實例,同時傳入默認的 encodingId 就是 bcrypt,以及 encoders 實例,DelegatingPasswordEncoder 看名字應該是一個代理對象。

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

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

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

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); 

生成結果如下:

  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 進行登錄,發現都能登錄成功。

5.意義何在?

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

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

本文轉載自微信公眾號「江南一點雨」,可以通過以下二維碼關注。轉載本文請聯系江南一點雨公眾號。

 

責任編輯:武曉燕 來源: 江南一點雨
相關推薦

2022-06-16 10:38:24

URL權限源代碼

2021-12-28 11:13:05

安全認證 Spring Boot

2009-11-03 14:19:53

2021-04-28 06:26:11

Spring Secu功能實現源碼分析

2021-05-31 10:47:17

SpringSecuritySession

2020-09-16 08:07:54

權限粒度Spring Secu

2022-06-04 12:25:10

解密加密過濾器

2021-03-09 13:18:53

加密解密參數

2024-10-18 08:00:00

SpringBoot框架開發

2020-10-25 09:04:46

數據加密數據泄露攻擊

2021-07-12 12:20:08

Spring初始化方案

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技術棧公眾號

午夜福利三级理论电影| 97久久伊人激情网| 日韩不卡的av| 国产高清中文字幕在线| 久久久久亚洲综合| 91精品免费视频| 日韩成人在线免费视频| 日韩av有码| 亚洲成人在线视频播放| 免费看污污网站| h片精品在线观看| 国产欧美一区二区精品性| 亚洲综合大片69999| 午夜影院免费在线观看 | 亚洲国产一区二区三区在线| 精品久久久无码中文字幕| 久久久久综合| 欧美精品videos性欧美| 人妻互换一区二区激情偷拍| 欧美jizz19性欧美| 欧美一区二区福利视频| 国产真人无码作爱视频免费| 操喷在线视频| 亚洲美女精品一区| 亚洲国产欧美日韩| 九色在线观看视频| av亚洲精华国产精华| 亚洲综合在线小说| 一级黄色免费看| 日本美女一区二区三区| 欧美在线亚洲在线| 欧美亚韩一区二区三区| 午夜视频精品| 日韩在线视频观看| 男人的天堂av网| 国产精品欧美三级在线观看| 亚洲第一天堂无码专区| 久久久久国产免费| aaa国产精品视频| 欧美一区二区久久| 搡的我好爽在线观看免费视频| 精品免费av在线| 日本韩国一区二区三区视频| www国产黄色| 国产传媒在线观看| 婷婷综合另类小说色区| 超碰成人免费在线| 国产高清视频色在线www| 亚洲国产精品综合小说图片区| 欧美日韩在线免费观看视频| 色综合久久久久综合一本到桃花网| 欧美激情综合在线| 日本午夜精品一区二区三区| 毛片在线免费| 国产日韩av一区| 日本一区免费在线观看| 九一国产在线| 国产精品乱码一区二三区小蝌蚪| 亚洲欧洲精品一区二区| 久久亚洲天堂| 亚洲三级久久久| 肉大捧一出免费观看网站在线播放| 国产在线高清理伦片a| 亚洲精品中文在线影院| 免费在线黄网站| 77thz桃花论族在线观看| 黑人巨大精品欧美一区免费视频| 国产黄色一级网站| 久久91导航| 欧美日韩国产a| 美女被爆操网站| 理论片一区二区在线| 亚洲美女久久久| 天堂av网手机版| 亚洲色图二区| 777午夜精品福利在线观看| 国产一区二区99| 青青草国产成人av片免费| 国产精品久久不能| 国产精品一区二区av白丝下载 | 欧美尤物美女在线| 一区二区三区小说| 久久国产成人精品国产成人亚洲| 激情都市亚洲| 777xxx欧美| 少妇一级淫免费观看| 欧美少妇xxxx| 欧美国产日韩中文字幕在线| 日韩特级黄色片| 美国一区二区三区在线播放| 国产精品国产精品国产专区蜜臀ah | 99精品在线免费| 亚洲精品人成| a'aaa级片在线观看| 欧美亚洲综合久久| 欧美极品jizzhd欧美仙踪林| 国产一区二区精品久| 九九九热精品免费视频观看网站| 黄网站免费在线| 9久久婷婷国产综合精品性色| 美女又黄又免费的视频| 亚洲精选av| 亚洲免费高清视频| 波多野结衣家庭教师| 国产日韩欧美一区在线| 国产综合色香蕉精品| 手机在线观看免费av| 国产精品久久久久影院色老大| 久久99久久久久久| 成人综合网站| 亚洲精品福利免费在线观看| 无码人妻精品中文字幕 | 久久综合亚洲社区| www.国产com| 国产高清一区日本| 亚洲视频在线二区| 巨茎人妖videos另类| 精品国产乱码久久久久久夜甘婷婷| 中字幕一区二区三区乱码| 亚洲福利免费| 7777精品伊久久久大香线蕉语言| 九色网友自拍视频手机在线| 亚洲在线视频网站| www.日本久久| 国产精品久久久久久影院8一贰佰| 欧美在线视频免费| 天天色综合久久| 亚洲va中文字幕| 粗大的内捧猛烈进出视频| 国产韩日影视精品| 国产日韩欧美视频在线| 欧美成人免费| 色综合久久中文字幕综合网| 蜜臀视频在线观看| 一区二区在线| 91精品视频播放| 日本电影在线观看网站| 欧美在线观看一区| japanese中文字幕| 久久av一区| 欧美日韩一区二区三区在线观看免| 国模雨婷捆绑高清在线| 日韩美女视频在线| 欧美国产精品一二三| 国产一区二区三区精品欧美日韩一区二区三区 | 99精品在线视频观看| 亚洲欧美综合在线精品| 亚洲第一色av| 亚洲最大黄网| 成人精品一二区| 国产三级在线播放| 日韩一区二区三区免费看 | 色鬼7777久久| 色欧美日韩亚洲| 亚洲精品乱码久久久久久久久久久久| 性欧美xxxx大乳国产app| 欧美极品色图| 国产激情久久| 九九九热精品免费视频观看网站| 亚洲成a人片在线| 亚洲成人你懂的| 男女黄床上色视频| 久久人人超碰| 亚洲人成影视在线观看| 国产麻豆一区二区三区| 欧美精品生活片| 91欧美一区二区三区| 天堂8在线天堂资源bt| 在线免费观看av片| 国产精品美女久久久久aⅴ| 午夜免费看视频| 婷婷综合视频| 国产精品久久久久久久免费大片| 欧美gv在线观看| 国产一区二区三区直播精品电影| 天堂在线一区二区三区| 嘿嘿视频在线观看| 国产精品videosex性欧美| 成人免费视频97| 成人性生交大片免费看网站| 亚洲精品国产品国语在线| 国产一区二区视频网站| 国产精品家庭影院| 又色又爽又黄18网站| 欧美综合二区| 可以在线看黄的网站| 老司机精品在线| 成人精品视频99在线观看免费| 日韩激情av| 国产亚洲欧美aaaa| 亚洲成人第一区| 欧美亚一区二区| 久久综合综合久久| 国产精品乱码人人做人人爱| 亚洲色偷偷色噜噜狠狠99网| 蜜臀久久99精品久久久久宅男 | 成人h视频在线| 九色porny丨入口在线| 日韩中文在线不卡| 五月婷婷丁香花| 日韩一区二区三区四区| а中文在线天堂| 亚洲国产裸拍裸体视频在线观看乱了 | www.av毛片| 99久久.com| 欧美日韩电影一区二区| 网站一区二区| 国产色综合天天综合网| 最新欧美色图| 97福利一区二区| av在线免费网站| 中文字幕精品www乱入免费视频| 人妻夜夜爽天天爽| 91精品国产一区二区三区蜜臀| 国产精品第5页| 亚洲成人av福利| 小向美奈子av| 国产欧美精品一区二区色综合 | 五月天久久网站| 欧美日韩一区在线视频| 牛牛影视久久网| 电影午夜精品一区二区三区| 亚洲三级在线| 国产精品免费一区豆花| 男人久久天堂| 午夜剧场成人观在线视频免费观看| 国产1区在线| 久久精品国产精品| 91网页在线观看| 国产亚洲视频中文字幕视频| 天堂在线免费av| 精品久久久网站| www.激情五月.com| 欧美一区二区在线看| 97超碰人人草| 91.com在线观看| 国产又粗又黄又爽| 欧美人伦禁忌dvd放荡欲情| 午夜一级黄色片| 欧美性大战久久久久久久蜜臀 | 亚洲在线色站| 日韩精品一卡| 国产精品h视频| 亚洲精品a级片| 草草草视频在线观看| 综合激情视频| 福利视频一区二区三区四区| 最新国产拍偷乱拍精品| 真人抽搐一进一出视频| 日韩午夜av在线| 国产成人黄色片| 日韩精品成人一区二区三区| 成人性生生活性生交12| 捆绑紧缚一区二区三区视频| xxxx在线免费观看| 国产综合色在线| 国产免费无码一区二区| 成人av在线播放网站| 色天使在线视频| 久久久精品综合| 三级黄色免费观看| 一区二区成人在线观看| 国产午夜福利片| 色悠悠久久综合| 91久久国语露脸精品国产高跟| 制服丝袜成人动漫| 蜜臀久久久久久999| 日韩电影大片中文字幕| melody高清在线观看| 久久精品国产2020观看福利| 动漫一区二区| 国产成人av网址| **精品中文字幕一区二区三区| 91成人免费在线观看| 国产精品流白浆在线观看| 人偷久久久久久久偷女厕| 91免费精品| 日韩小视频在线播放| 免费在线观看日韩欧美| 麻豆传媒在线看| 久久免费国产精品| 婷婷伊人五月天| 福利二区91精品bt7086| 在线观看中文字幕av| 精品国产百合女同互慰| 久久久久国产精品嫩草影院| 久久久国产视频| 神马午夜在线视频| 成人美女av在线直播| 日韩高清影视在线观看| 中文字幕成人一区| 国产欧美在线| 国产在线观看中文字幕| 91色.com| 麻豆疯狂做受xxxx高潮视频| 色综合天天综合网国产成人综合天| 国产又黄又猛又爽| 日韩精品在线视频| 爆操欧美美女| 国产精品福利观看| 国内精品免费| 男人的天堂成人| 日韩1区2区日韩1区2区| 国产日韩视频一区| 综合久久一区二区三区| 手机看片久久久| 亚洲电影在线看| 成人日批视频| 国产精品都在这里| 亚洲人成伊人成综合图片| 免费cad大片在线观看| 美女高潮久久久| 日本乱子伦xxxx| 亚洲国产精品一区二区www | 日韩制服诱惑| 久中文字幕一区| 亚洲午夜黄色| 97超碰免费在线观看| 国产精品国产馆在线真实露脸 | 视频在线日韩| 精品一区二区三区自拍图片区| 亚洲九九视频| 亚洲天堂网2018| 国产午夜三级一区二区三| 91在线看视频| 精品久久久久久亚洲综合网| 成人ww免费完整版在线观看| 国产精品自产拍在线观看| 少妇精品久久久一区二区| 久久久久久久中文| 成人久久视频在线观看| 青春草免费视频| 欧美大片一区二区三区| 在线中文字幕视频观看| 成人国产精品av| 香蕉视频官网在线观看日本一区二区| 污网站免费在线| 中日韩av电影| 中文字幕日产av| 最近2019中文字幕mv免费看| 日韩漫画puputoon| 欧美一区二区在线视频观看| 久久精品人人做人人爽电影蜜月| 日本免费福利视频| 精品久久久久久久久久ntr影视| 日本激情一区二区| 97国产精品视频| 欧美亚洲国产日韩| 欧美亚洲国产成人| 久久久国产精品不卡| 加勒比在线一区| 中文日韩在线视频| 伊人亚洲精品| 真实国产乱子伦对白视频| 国产成人福利片| 制服.丝袜.亚洲.中文.综合懂色| 亚洲精品影视在线观看| 秋霞国产精品| 中文字幕一区二区三区乱码| 国产综合久久久久影院| 免费日韩在线视频| 亚洲精品国产电影| 欧美精品资源| 性做爰过程免费播放| 成人免费毛片app| 日本熟妇一区二区三区| 日韩在线观看你懂的| 日本99精品| 欧洲黄色一级视频| 国产精品毛片无遮挡高清| 99热在线只有精品| 97超级碰在线看视频免费在线看| 伊人久久大香线蕉综合网站 | 精品一区二区三区免费看| 免费网站在线观看视频| 久久久久成人黄色影片| 国产色片在线观看| 91精品国产色综合久久不卡98| 精品免费av| 古装做爰无遮挡三级聊斋艳谭| 午夜精品久久久久久久| 成人欧美亚洲| 国产精品9999久久久久仙踪林| 久久婷婷麻豆| 欧美成欧美va| 一区二区三区在线播放欧美| 少妇精品在线| 亚洲黄色av网址| 亚洲成人自拍网| 午夜国产福利在线| 国产精品推荐精品| 美女视频黄 久久| 国产无遮挡又黄又爽| 中文字幕不卡av| 欧美美女在线直播| 毛片毛片毛片毛| 欧美性生活大片免费观看网址| 国产三区在线观看| 日本一区二区不卡高清更新| 国产v日产∨综合v精品视频|