<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          實戰(zhàn)!Shiro安全框架(附詳細(xì)代碼)

          共 19361字,需瀏覽 39分鐘

           ·

          2021-10-01 13:17

          前言

          關(guān)于 Java 安全框架,一個是Spring Security,一個是Shiro。這兩個框架都是很不錯的,沒有絕對誰好誰壞,看業(yè)務(wù)場景選擇框架,最適合的才是最好的。

          今天我們主要來聊聊Shiro這個安全框架,我相信你們也是經(jīng)常用到。至于為什么會有很多人選擇使用Shiro,我認(rèn)為在眾多權(quán)限框架中,Shiro因其簡單而又不失強(qiáng)大的特點引起了不少開發(fā)者的注意。

          Apache Shiro是一個強(qiáng)大且易用的Java安全框架,執(zhí)行身份驗證、授權(quán)、密碼和會話管理。使用Shiro的易于理解的API。您可以快速、輕松地獲得任何應(yīng)用程序,從最小的移動應(yīng)用程序到最大的網(wǎng)絡(luò)和企業(yè)應(yīng)用程序。

          總結(jié)一點,就是因為Shiro簡單、靈活、易上手。至于那些基本概念,我覺得也很簡單,在代碼中一步一步來進(jìn)行說明。

          正文

          shiro 實現(xiàn)登錄、認(rèn)證、授權(quán)的流程大概如下:

          springboot集成Shiro框架實現(xiàn)按鈕級別的權(quán)限。涉及權(quán)限,這里面就涉及到用戶、角色、權(quán)限三張表和用戶角色、角色權(quán)限兩張關(guān)聯(lián)表。數(shù)據(jù)庫我用的是常見的MYSQL,這里我簡單設(shè)計了一下表的結(jié)構(gòu),如下。

          DROP TABLE IF EXISTS `sys_permission`;
          CREATE TABLE `sys_permission` (
          `id` int(11) NOT NULL,
          `available` int(11) DEFAULT NULL,
          `name` varchar(255) DEFAULT NULL,
          `parent_id` int(11) DEFAULT NULL,
          `parent_ids` varchar(255) DEFAULT NULL,
          `permission` varchar(255) DEFAULT NULL,
          `resource_type` varchar(255) DEFAULT NULL,
          `url` varchar(255) DEFAULT NULL,
          PRIMARY KEY (`id`)
          ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

          -- ----------------------------
          -- Records of sys_permission
          -- ----------------------------
          INSERT INTO `sys_permission` VALUES ('1', '0', '用戶管理', '0', '0/', 'userInfo:view', 'menu', 'userInfo/userList');
          INSERT INTO `sys_permission` VALUES ('2', '0', '用戶添加', '1', '0/1', 'userInfo:add', 'button', 'userInfo/userAdd');
          INSERT INTO `sys_permission` VALUES ('3', '0', '用戶刪除', '1', '0/1', 'userInfo:del', 'button', 'userInfo/userDel');

          -- ----------------------------
          -- Table structure for sys_role
          -- ----------------------------
          DROP TABLE IF EXISTS `sys_role`;
          CREATE TABLE `sys_role` (
          `id` int(11) NOT NULL,
          `available` int(11) DEFAULT NULL,
          `description` varchar(255) DEFAULT NULL,
          `role` varchar(255) DEFAULT NULL,
          PRIMARY KEY (`id`)
          ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

          -- ----------------------------
          -- Records of sys_role
          -- ----------------------------
          INSERT INTO `sys_role` VALUES ('1', '0', '管理員', 'admin');
          INSERT INTO `sys_role` VALUES ('2', '0', 'VIP會員', 'vip');
          INSERT INTO `sys_role` VALUES ('3', '1', '測試人員', 'test');

          -- ----------------------------
          -- Table structure for sys_role_permission
          -- ----------------------------
          DROP TABLE IF EXISTS `sys_role_permission`;
          CREATE TABLE `sys_role_permission` (
          `permission_id` int(11) DEFAULT NULL,
          `role_id` int(11) DEFAULT NULL,
          KEY `FKomxrs8a388bknvhjokh440waq` (`permission_id`),
          KEY `FK9q28ewrhntqeipl1t04kh1be7` (`role_id`),
          CONSTRAINT `FK9q28ewrhntqeipl1t04kh1be7` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`),
          CONSTRAINT `FKomxrs8a388bknvhjokh440waq` FOREIGN KEY (`permission_id`) REFERENCES `sys_permission` (`id`)
          ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

          -- ----------------------------
          -- Records of sys_role_permission
          -- ----------------------------
          INSERT INTO `sys_role_permission` VALUES ('1', '1');
          INSERT INTO `sys_role_permission` VALUES ('2', '2');
          INSERT INTO `sys_role_permission` VALUES ('3', '2');
          INSERT INTO `sys_role_permission` VALUES ('2', '3');

          -- ----------------------------
          -- Table structure for sys_user
          -- ----------------------------
          DROP TABLE IF EXISTS `sys_user`;
          CREATE TABLE `sys_user` (
          `uid` int(11) NOT NULL,
          `username` varchar(255) DEFAULT NULL,
          `name` varchar(255) DEFAULT NULL,
          `password` varchar(255) DEFAULT NULL,
          `salt` varchar(255) DEFAULT NULL,
          `state` int(1) DEFAULT NULL,
          PRIMARY KEY (`uid`)
          ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

          -- ----------------------------
          -- Records of sys_user
          -- ----------------------------
          INSERT INTO `sys_user` VALUES ('1', 'admin', '管理員', '123456', '8d78869f470951332959580424d4bf4f', '0');
          INSERT INTO `sys_user` VALUES ('2', 'jiangwang', 'vip', '123456', '8d78869f470951332959580424d4bf4f', '0');
          INSERT INTO `sys_user` VALUES ('3', 'test', '測試', '123456', '8d78869f470951332959580424d4bf4f', '0');

          -- ----------------------------
          -- Table structure for sys_user_role
          -- ----------------------------
          DROP TABLE IF EXISTS `sys_user_role`;
          CREATE TABLE `sys_user_role` (
          `role_id` int(11) DEFAULT NULL,
          `uid` int(11) DEFAULT NULL,
          KEY `FKhh52n8vd4ny9ff4x9fb8v65qx` (`role_id`),
          KEY `FKgkmyslkrfeyn9ukmolvek8b8f` (`uid`),
          CONSTRAINT `FKgkmyslkrfeyn9ukmolvek8b8f` FOREIGN KEY (`uid`) REFERENCES `sys_user` (`uid`),
          CONSTRAINT `FKhh52n8vd4ny9ff4x9fb8v65qx` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`)
          ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

          -- ----------------------------
          -- Records of sys_user_role
          -- ----------------------------
          INSERT INTO `sys_user_role` VALUES ('1', '1');
          INSERT INTO `sys_user_role` VALUES ('1', '2');
          INSERT INTO `sys_user_role` VALUES ('2', '2');
          INSERT INTO `sys_user_role` VALUES ('1', '3');
          INSERT INTO `sys_user_role` VALUES ('3', '1');
          復(fù)制代碼

          數(shù)據(jù)庫設(shè)計好后,下面就是寫代碼,業(yè)務(wù)邏輯很簡單,用戶登錄成功后,會根據(jù)用戶自身角色而顯示擁有的權(quán)限。登錄要經(jīng)過認(rèn)證,認(rèn)證通過后

          創(chuàng)建項目

          目錄結(jié)構(gòu)如下:

          創(chuàng)建好項目,就需要添加依賴,我使用mybatis框架作為持久層,逆向工程來生成代碼。

          添加依賴

          <dependencies>
          <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
          </dependency>

          <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-test</artifactId>
          </dependency>

          <dependency>
          <groupId>org.mybatis.spring.boot</groupId>
          <artifactId>mybatis-spring-boot-starter</artifactId>
          <version>1.3.1</version>
          </dependency>

          <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-thymeleaf</artifactId>
          </dependency>

          <!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
          <dependency>
          <groupId>com.github.theborakompanioni</groupId>
          <artifactId>thymeleaf-extras-shiro</artifactId>
          <version>2.0.0</version>
          </dependency>

          <dependency>
          <groupId>org.apache.shiro</groupId>
          <artifactId>shiro-spring</artifactId>
          <version>1.4.0</version>
          </dependency>

          <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          </dependency>
          </dependencies>

          <build>
          <plugins>
          <plugin>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-maven-plugin</artifactId>
          </plugin>

          <plugin>
          <groupId>org.mybatis.generator</groupId>
          <artifactId>mybatis-generator-maven-plugin</artifactId>
          <version>1.3.2</version>
          <configuration>
          <overwrite>true</overwrite>
          <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
          </configuration>
          </plugin>
          </plugins>
          </build>
          復(fù)制代碼

          基本的代碼生成后,就進(jìn)行業(yè)務(wù)代碼的編寫了。

          application.properties

          server.port=7777
          spring.datasource.url=jdbc:mysql://127.0.0.1:3306/shiro_demo?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false
          spring.datasource.username=root
          spring.datasource.password=123456
          spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
          mybatis.type-aliases-package=com.jw.model
          mybatis.mapper-locations=classpath:mapping/*.xml
          logging.level.tk.mybatis=TRACE
          復(fù)制代碼

          創(chuàng)建UserService.java文件

          @Service
          public class UserService
          {
          @Autowired
          private SysUserMapper sysUserMapper;

          @Autowired
          private SysUserRoleMapper sysUserRoleMapper;

          @Autowired
          private SysRoleMapper sysRoleMapper;

          @Autowired
          private SysRolePermissionMapper sysRolePermissionMapper;

          @Autowired
          private SysPermissionMapper sysPermissionMapper;

          public List<SysUser> getList(int id)
          {
          SysUserExample example = new SysUserExample();
          example.createCriteria().andUidEqualTo(id);
          return sysUserMapper.selectByExample(example);
          }

          /**
          * 根據(jù)用戶名查詢用戶
          *
          * @param username 用戶名
          * @return 用戶
          */

          public SysUser findByUsername(String username)
          {
          SysUser user = new SysUser();
          SysUserExample example = new SysUserExample();
          example.createCriteria().andUsernameEqualTo(username);
          List<SysUser> userList = sysUserMapper.selectByExample(example);
          if (userList.isEmpty())
          {
          return null;
          }
          for (SysUser tbUser : userList)
          {
          user = tbUser;
          }
          return user;
          }


          /**
          * 查詢用戶的角色
          *
          * @param id 用戶id
          * @return 用戶的角色
          */

          public List<SysRole> findRolesById(int id)
          {
          SysUser userInfo = sysUserMapper.selectByPrimaryKey(id);
          if (userInfo == null)
          {
          throw new RuntimeException("該用戶不存在");
          }
          List<SysRole> roles = new ArrayList<>();
          SysUserRoleExample userRoleExample = new SysUserRoleExample();
          userRoleExample.createCriteria().andUidEqualTo(userInfo.getUid());
          List<SysUserRole> sysUserRoleList = sysUserRoleMapper.selectByExample(userRoleExample);
          List<Integer> rids = new ArrayList<>();
          if (!CollectionUtils.isEmpty(sysUserRoleList))
          {
          for (SysUserRole sysUserRole : sysUserRoleList)
          {
          rids.add(sysUserRole.getRoleId());
          }
          if (!CollectionUtils.isEmpty(rids))
          {
          for (Integer rid : rids)
          {
          SysRole sysRole = sysRoleMapper.selectByPrimaryKey(rid);
          if (sysRole != null)
          {
          roles.add(sysRole);
          }
          }
          }
          }
          return roles;
          }


          /**
          * 查詢用戶的權(quán)限
          *
          * @param roles 用戶的角色
          * @return 用戶的權(quán)限
          */

          public List<SysPermission> findPermissionByRoles(List<SysRole> roles)
          {
          List<SysPermission> permissions = new ArrayList<>();
          if (!CollectionUtils.isEmpty(roles))
          {
          Set<Integer> permissionIds = new HashSet<>();//存放菜單id
          List<SysRolePermission> sysRolePermissions;
          for (SysRole role : roles)
          {
          SysRolePermissionExample sysRolePermissionExample = new SysRolePermissionExample();
          sysRolePermissionExample.createCriteria().andRoleIdEqualTo(role.getId());
          sysRolePermissions = sysRolePermissionMapper.selectByExample(sysRolePermissionExample);
          if (!CollectionUtils.isEmpty(sysRolePermissions))
          {
          for (SysRolePermission sysRolePermission : sysRolePermissions)
          {
          permissionIds.add(sysRolePermission.getPermissionId());
          }
          }
          }
          if (!CollectionUtils.isEmpty(permissionIds))
          {
          for (Integer permissionId : permissionIds)
          {
          SysPermission permission = sysPermissionMapper.selectByPrimaryKey(permissionId);
          if (permission != null)
          {
          permissions.add(permission);
          }
          }
          }
          }
          return permissions;
          }
          }
          復(fù)制代碼

          創(chuàng)建UserController.java文件

          @Controller
          @RequestMapping("/userInfo")
          public class UserController
          {

          @GetMapping("/userList")
          public String getUserList()
          {
          return "userList";
          }

          @GetMapping("/userAdd")
          public String addUser()
          {
          return "addUser";
          }

          @GetMapping("/userDel")
          public String deleteUser()
          {
          return "deleteUser";
          }

          }
          復(fù)制代碼

          創(chuàng)建LoginController.java文件

          @Controller
          public class LoginController
          {

          @GetMapping(value = "/toLogin")
          public String toLogin()
          {
          return "login";
          }

          @PostMapping("/login")
          public String login(
          @RequestParam("username") String username,
          @RequestParam("password") String password,
          Model model)

          {
          Subject subject = SecurityUtils.getSubject();
          UsernamePasswordToken token = new UsernamePasswordToken(username, password);
          try
          {
          subject.login(token);
          return "index";
          }
          catch (UnknownAccountException uae)
          {
          model.addAttribute("msg", "用戶不存在");
          return "login";
          }
          catch (IncorrectCredentialsException ice)
          {
          model.addAttribute("msg", "密碼不正確");
          return "login";
          }
          }

          @GetMapping("/logOut")
          public String logOut()
          {
          return "login";
          }

          @GetMapping("/noAuthorization")
          public String noAuthorization()
          {
          return "未經(jīng)授權(quán),無法訪問此頁面";
          }
          }
          復(fù)制代碼

          創(chuàng)建CurrentUser.java文件

          @Data
          public class CurrentUser
          {
          //當(dāng)前登錄用戶
          private SysUser userInfo;

          //當(dāng)前用戶所擁有的角色
          private List<SysRole> roles;

          //當(dāng)前用戶所擁有得權(quán)限
          private List<SysPermission> permissions;

          }
          復(fù)制代碼

          創(chuàng)建MyRealm.java文件

          繼承 Shirot 框架的 AuthorizingRealm 類,并實現(xiàn)默認(rèn)的兩個方法:

          public class MyRealm extends AuthorizingRealm
          {

          @Autowired
          private UserService userService;

          /**
          * 授權(quán)
          *
          * @param principalCollection
          * @return
          */

          @Override
          protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection)
          {
          SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
          //獲取當(dāng)前用戶
          CurrentUser currentUser = (CurrentUser) SecurityUtils.getSubject().getPrincipal();
          List<SysRole> roles = currentUser.getRoles();
          List<SysPermission> permissions = currentUser.getPermissions();
          if (!CollectionUtils.isEmpty(roles))
          {
          for (SysRole role : roles)
          {
          //授權(quán)角色
          authorizationInfo.addRole(role.getRole());
          }
          }
          if (!CollectionUtils.isEmpty(permissions))
          {
          for (SysPermission permission : permissions)
          {
          //授權(quán)權(quán)限
          authorizationInfo.addStringPermission(permission.getPermission());
          }
          }
          return authorizationInfo;
          }

          /**
          * 認(rèn)證
          *
          * @param token
          * @return
          * @throws AuthenticationException
          */

          @Override
          protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
          {
          //當(dāng)前用戶名
          String username = (String) token.getPrincipal();
          SysUser user = userService.findByUsername(username);
          if (user == null)
          {
          return null;
          }
          Subject subject = SecurityUtils.getSubject();
          Session session = subject.getSession();
          //將當(dāng)前用戶的信息放入session中
          session.setAttribute("user", user);
          //獲取當(dāng)前用戶的角色
          List<SysRole> roles = userService.findRolesById(user.getUid());
          //獲取當(dāng)前用戶所擁有的權(quán)限
          List<SysPermission> permissions = userService.findPermissionByRoles(roles);
          CurrentUser currentUser = new CurrentUser();
          currentUser.setUserInfo(user);
          currentUser.setRoles(roles);
          currentUser.setPermissions(permissions);
          return new SimpleAuthenticationInfo(currentUser, user.getPassword(), getName());
          }
          }
          復(fù)制代碼

          創(chuàng)建ShiroConfig.java文件

          @Configuration
          public class ShiroConfig
          {
          @Bean
          public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager)
          {
          ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
          bean.setSecurityManager(securityManager);
          HashMap<String, String> filterMap = new LinkedHashMap<>();

          //授權(quán)
          filterMap.put("/userInfo/userAdd", "perms[userInfo:add]");
          filterMap.put("/userInfo/userDel", "perms[userInfo:del]");
          filterMap.put("/userInfo/userList", "perms[userInfo:view]");

          //需要攔截的url
          filterMap.put("/userInfo/*", "authc");
          //不需要攔截的頁面
          filterMap.put("/static/**", "anon");
          //被攔截的頁面跳轉(zhuǎn)到登錄頁面
          bean.setLoginUrl("/toLogin");
          //登錄成功后跳轉(zhuǎn)的鏈接
          bean.setSuccessUrl("/index");

          bean.setUnauthorizedUrl("/noAuthorization");
          bean.setFilterChainDefinitionMap(filterMap);
          return bean;
          }

          @Bean
          public DefaultWebSecurityManager defaultWebSecurityManager()
          {
          DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
          defaultWebSecurityManager.setRealm(myRealm());
          return defaultWebSecurityManager;
          }

          @Bean
          public MyRealm myRealm()
          {
          return new MyRealm();
          }

          @Bean
          public ShiroDialect getShiroDialect()
          {
          return new ShiroDialect();
          }
          }
          復(fù)制代碼

          在resources目錄下創(chuàng)建templates文件夾,在該文件夾下創(chuàng)建下列html文件。

          創(chuàng)建index.html文件

          <!DOCTYPE html>
          <html lang="en" xmlns:th="http://www.thymeleaf.org"
          xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">

          <head>
          <meta charset="UTF-8">
          <title>首頁</title>
          </head>
          <body>
          <h1>首頁</h1>

          <div shiro:hasPermission="userInfo:view">
          <a th:href="@{/userInfo/userList}">查詢用戶</a>
          </div>

          <div shiro:hasPermission="userInfo:add">
          <a th:href="@{/userInfo/userAdd}">添加用戶</a>
          </div>

          <div shiro:hasPermission="userInfo:del">
          <a th:href="@{/userInfo/userDel}">刪除用戶</a>
          </div>

          </body>
          </html>
          復(fù)制代碼

          創(chuàng)建login.html文件

          <!DOCTYPE html>
          <html lang="en" xmlns:th="http://www.thymeleaf.org">
          <head>
          <meta charset="UTF-8">
          <title>Title</title>
          </head>
          <body>
          <h1>登錄</h1>
          <hr>
          <p th:text="${msg}"></p>
          <form action="/login" method="post">
          <p>用戶名:<input type="text" name="username"/></p>
          <p>密 碼:<input type="text" name="password"/></p>
          <p><input type="submit"/></p>
          </form>

          </body>
          </html>
          復(fù)制代碼

          創(chuàng)建userList.html文件

          <!DOCTYPE html>
          <html lang="en">
          <head>
          <meta charset="UTF-8">
          <title>查詢用戶</title>
          </head>
          <body>

          <p>用戶查詢</p>
          </body>
          </html>
          復(fù)制代碼

          創(chuàng)建addUser.html文件

          <!DOCTYPE html>
          <html lang="en">
          <head>
          <meta charset="UTF-8">
          <title>添加用戶</title>
          </head>
          <body>
          <p>添加用戶</p>
          </body>
          </html>
          復(fù)制代碼

          創(chuàng)建deleteUser.html文件

          <!DOCTYPE html>
          <html lang="en">
          <head>
          <meta charset="UTF-8">
          <title>刪除用戶</title>
          </head>
          <body>
          <p>刪除用戶</p>
          </body>
          </html>
          復(fù)制代碼

          啟動項目

          • 訪問http://localhost:7777/toLogin,登錄頁面,輸入用戶名和密碼,點擊提交。

          • 可以看出,admin用戶有查看和添加權(quán)限,沒有刪除權(quán)限

          • 使用其他用戶登錄,看看有啥權(quán)限。

          • 可以看出,jiangwang用戶擁有查看、添加、刪除權(quán)限。

          Shiro加密

          我們在數(shù)據(jù)庫中保存的密碼都是明文的,一旦數(shù)據(jù)庫數(shù)據(jù)泄露,那就會造成不可估算的損失,所以我們通常都會使用非對稱加密,簡單理解也就是不可逆的加密,而 md5 加密算法就是符合這樣的一種算法。為了更加安全,我們采用加鹽 + 多次加密的方法。

              /**
          * 密碼加密
          * @param source 密碼
          * @param salt 鹽
          * @return
          */

          public static String md5Encryption(String source, String salt)
          {
          String algorithmName = "MD5";//加密算法
          int hashIterations = 1024;//加密次數(shù)
          SimpleHash simpleHash = new SimpleHash(algorithmName, source, salt, hashIterations);
          return simpleHash + "";
          }
          復(fù)制代碼

          小結(jié)

          權(quán)限在我們的項目中應(yīng)用是非常廣泛的,涉及到權(quán)限可以包括:登錄權(quán)限、菜單權(quán)限、數(shù)據(jù)權(quán)限(按鈕權(quán)限),上面的demo可以看出不同的用戶登錄進(jìn)來,有不同的權(quán)限(數(shù)據(jù)權(quán)限),我們的項目中,涉及到權(quán)限都會有這幾個表,用戶表,角色表,權(quán)限表,用戶和角色是多對多的關(guān)系,角色和權(quán)限也是多對多的關(guān)系。

          完整代碼已托管碼云:gitee.com/jiangwang00…


          作者:初念初戀
          鏈接:https://juejin.cn/post/7012506053917016072
          來源:掘金
          著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。



          瀏覽 46
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  WWW一区第一页 | 狠狠狠狠狠插狠狠狠插狠狠狠插 | 欧美一级A黄片 | 奇米狠狠色777久久久欧美老妇 | 最新国产免费黄色 |