【Spring Security】入门——实现用户注册登录

【Spring Security】入门——实现用户注册登录一 项目框架 1 项目结构 2 选择安装依赖二 数据库三 代码实现 1 配置文件 2 SpringSecuri 是一个功能强大且高度可自定义的身份验证和访问控制框架 它侧重于为 Java 应用程序提供身份验证

大家好,欢迎来到IT知识分享网。

本篇文章目的在于快速使用Spring Boot与Spring Security搭建一个项目,实现简单的用户注册登录。

项目已完整上传到GitHub传送门

此项目所用知识点:

  1. Spring Boot
  2. Spring Security
  3. MySQL
  4. Mybatis
  5. JSP

一、项目框架

1. 项目结构

在这里插入图片描述

2. 选择安装依赖

  1. Spring Boot DevTools
  2. Spring Web
  3. Spring Security
  4. Mybatis Framework
  5. MySQL Driver

以此pom.xml文件为准

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>springboot-security</artifactId> <version>1.0-SNAPSHOT</version> <!-- 引入springboot --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.1.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <!-- spring boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- spring security--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- jsp --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <scope>provided</scope> </dependency> <!-- jstl --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> <!-- tomcat --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <!--用于编译jsp --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <scope>provided</scope> </dependency> <!-- test --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.2</version> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> </dependencies> <!-- 配置tomcat --> <build> <finalName>security-springboot</finalName> <pluginManagement> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <artifactId>maven-resources-plugin</artifactId> <configuration> <encoding>utf-8</encoding> <useDefaultDelimiters>true</useDefaultDelimiters> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>/*</include> </includes> </resource> <resource> <directory>src/main/java</directory> <includes> <include>/*.xml</include> </includes> </resource> </resources> </configuration> </plugin> </plugins> </pluginManagement> </build> </project> 

二、数据库

共5张表

  1. t_user 用户表
  2. t_role 角色表
  3. r_user_role 用户-角色
  4. r_permission 权限表
  5. r_role_permission 角色-权限
/* Navicat Premium Data Transfer Source Server : localhost Source Server Type : MySQL Source Server Version : 50610 Source Host : localhost:3306 Source Schema : user_db Target Server Type : MySQL Target Server Version : 50610 File Encoding : 65001 Date: 21/07/2020 21:29:29 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for t_permission -- ---------------------------- DROP TABLE IF EXISTS `t_permission`; CREATE TABLE `t_permission` ( `id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `code` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '权限标识符', `description` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述', `url` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '请求地址', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of t_permission -- ---------------------------- INSERT INTO `t_permission` VALUES ('1', 'p1', '测试资源1', '/resource/r1'); INSERT INTO `t_permission` VALUES ('2', 'p2', '测试资源2', '/resource/r2'); INSERT INTO `t_permission` VALUES ('3', 'p3', '测试资源3', '/resource/r3'); -- ---------------------------- -- Table structure for t_role -- ---------------------------- DROP TABLE IF EXISTS `t_role`; CREATE TABLE `t_role` ( `id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `role_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `create_time` datetime(0) NULL DEFAULT NULL, `update_time` datetime(0) NULL DEFAULT NULL, `status` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `unique_role_name`(`role_name`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of t_role -- ---------------------------- INSERT INTO `t_role` VALUES ('1', '管理员', NULL, NULL, NULL, ''); -- ---------------------------- -- Table structure for t_role_permission -- ---------------------------- DROP TABLE IF EXISTS `t_role_permission`; CREATE TABLE `t_role_permission` ( `role_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `permission_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, PRIMARY KEY (`role_id`, `permission_id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of t_role_permission -- ---------------------------- INSERT INTO `t_role_permission` VALUES ('1', '1'); INSERT INTO `t_role_permission` VALUES ('1', '2'); -- ---------------------------- -- Table structure for t_user -- ---------------------------- DROP TABLE IF EXISTS `t_user`; CREATE TABLE `t_user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户id', `username` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `password` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `fullname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户姓名', `mobile` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '手机号', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of t_user -- ---------------------------- INSERT INTO `t_user` VALUES (1, 'zhangsan', '$2a$10$37vdSYJUVguwXpLDnZfEt.UDC0y6Yk2RCzFuJKfOrWCiTnUFlmj3K', NULL, NULL); -- ---------------------------- -- Table structure for t_user_role -- ---------------------------- DROP TABLE IF EXISTS `t_user_role`; CREATE TABLE `t_user_role` ( `user_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `role_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `create_time` datetime(0) NULL DEFAULT NULL, `creator` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`user_id`, `role_id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of t_user_role -- ---------------------------- INSERT INTO `t_user_role` VALUES ('1', '1', NULL, NULL); SET FOREIGN_KEY_CHECKS = 1; 

三、代码实现

实现思路

  1. 配置Spring Boot环境。将服务器配置、数据源、MyBatis映射文件、启动文件完成配置。
  2. 完成基本的jsp页面。用于测试用户注册、登录功能。
  3. 配置视图控制器。将项目的根路径重定向到login-view,再将login-view转发到真实的login.jsp页面。
  4. 进行Spring Security配置。主要配置密码编码器和安全拦截器
  5. 编写dao层和mapper文件。用于连接数据库。
  6. 编写service层。一方面是实现用户的注册和登录,另一方面是实现spring-security核心接口,其负载着用户特定数据,它被用来在整个框架作为一个用户DAO。
  7. 编写controller层。用于页面访问,在controller接口中可以设置接口的访问权限,让Spring Security进行安全验证。

1. 配置文件

application.properties

# web server.port=8080 server.servlet.context-path=/ spring.application.name=security-springboot spring.mvc.view.prefix=/WEB-INF/view/ spring.mvc.view.suffix=.jsp # DataSource spring.datasource.url=jdbc:mysql://localhost:3306/user_db spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.jdbc.Driver # mybatis mybatis.mapper-locations=classpath:mapper/*.xml 

SpringBootApp.java

package com.aaa; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; / * springboot启动程序 * @author 淮南King */ @SpringBootApplication @MapperScan("com.aaa.dao") public class SpringBootApp { 
    public static void main(String[] args) { 
    SpringApplication.run(SpringBootApp.class,args); } } 

2. 编写测试页面

在这里插入图片描述

login.jsp

<%@ page contentType="text/html;charset=UTF-8" pageEncoding="utf-8" %>
<html> <head> <title>用户登录</title> </head> <body> <h1>登录</h1> <form action="/login" method="post"> 用户名:<input type="text" name="username"><br> 密码:<input type="password" name="password"><br> <input type="submit" value="登录"> </form> <hr> <p><a href="/register">注册</a></p> </body> </html> 

register.jsp

<%@ page contentType="text/html;charset=UTF-8" pageEncoding="utf-8" %>
<html> <head> <title>用户注册</title> </head> <body> <h1>注册</h1> <form action="/user/register" method="post"> 用户名:<input type="text" name="username"><br> 密码:<input type="password" name="password"><br> <input type="submit" value="注册"> </form> <hr> <p><a href="/">登录</a></p> </body> </html> 

menu.jsp

<%@ page contentType="text/html;charset=UTF-8" pageEncoding="utf-8" %>
<html> <head> <title>菜单页</title> </head> <body> <h1>菜单页</h1> 欢迎${username}登录 <hr> <h3><a href="/resource/r1" target="_blank">资源一</a></h3> <h3><a href="/resource/r2" target="_blank">资源二</a></h3> <h3><a href="/resource/r3" target="_blank">资源三</a></h3> </body> </html> 

3. 配置视图控制器

WebConfig.java

package com.aaa.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; / * springmvc配置 * @author 淮南King */ @Configuration public class WebConfig implements WebMvcConfigurer { 
    / * 用此方法来添加视图控制器。 * @param registry */ @Override public void addViewControllers(ViewControllerRegistry registry) { 
    //将根路径重定向到/login-view registry.addViewController("/").setViewName("redirect:/login-view"); //将login-view转到login页面 registry.addViewController("/login-view").setViewName("login"); } } 

4. 进行Spring Security配置。

WebSecurityConfig.java

package com.aaa.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; / * spring security配置 * * @author 淮南King */ @Configuration @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 
    //密码编码器 @Bean public PasswordEncoder passwordEncoder() { 
    return new BCryptPasswordEncoder(); } //安全拦截机制 @Override protected void configure(HttpSecurity http) throws Exception { 
    http.csrf().disable() .authorizeRequests() //所有/r/的请求必须认证通过 .antMatchers("/r/").authenticated() //除了/r/,其它的请求可以访问 .anyRequest().permitAll() .and() //允许表单登录 .formLogin() //登录页面路径 .loginPage("/login-view") .loginProcessingUrl("/login") //自定义登录成功的页面地址 .successForwardUrl("/login-success") .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .and() .logout() //登录退出 .logoutUrl("/logout") .logoutSuccessUrl("/login-view?logout"); } } 

5. 编写dao层和mapper文件

UserDao.java

package com.aaa.dao; import com.aaa.entity.UserDto; import java.util.List; / * 用户信息持久层 * @author 淮南King */ public interface UserDao { 
    / * 根据账号查询用户信息 * * @param username 用户姓名 * @return 用户信息 */ UserDto getUserByUsername(String username); / * 根据用户id查询用户权限 * * @param userId 用户id * @return 权限列表 */ List<String> findPermissionsByUserId(String userId); / * 添加用户 * * @param userDTO 用户信息 * @return 修改条数 */ int addUser(UserDto userDTO); } 

UserMapper.xml

注意:mapper文件放置的位置在application.properties中的mybatis.mapper-locations进行路径设置

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.aaa.dao.UserDao"> <select id="getUserByUsername" parameterType="String" resultType="com.aaa.entity.UserDto"> select id,username,password,fullname,mobile from t_user where username = #{username} </select> <select id="findPermissionsByUserId" parameterType="String" resultType="String"> SELECT code FROM t_permission WHERE id IN( SELECT permission_id FROM t_role_permission WHERE role_id IN( SELECT role_id FROM t_user_role WHERE user_id = #{id} )) </select> <insert id="addUser" parameterType="com.aaa.entity.UserDto"> INSERT INTO `user_db`.`t_user`(`username`, `password`) VALUES (#{username},#{password}) </insert> </mapper> 

6. 编写service层

UserService.java

package com.aaa.service; import com.aaa.dao.UserDao; import com.aaa.entity.UserDto; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import java.util.List; / * 用户信息持久业务层 * * @author 淮南King * @date 2020-07-21 */ @Service public class UserService { 
    @Autowired UserDao dao; public UserDto getUserByUsername(String username) { 
    return dao.getUserByUsername(username); } public List<String> findPermissionsByUserId(String userId) { 
    return dao.findPermissionsByUserId(userId); } / * 添加用户 * @param userDTO * @return */ public int addUser(UserDto userDTO) { 
    //获取密码编码器 PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); //将用户的密码进行编码 String password = passwordEncoder.encode(userDTO.getPassword()); //将编码后的密码覆盖到用户信息中 userDTO.setPassword(password.substring(8)); //将用户信息持久化到数据库中 return dao.addUser(userDTO); } } 
package com.aaa.service; import com.aaa.dao.UserDao; import com.aaa.entity.UserDto; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import java.util.List; / * 实现spring-security核心接口,其负载的用户特定数据。 * * @author 淮南King */ @Service public class SpringDataUserDetailsService implements UserDetailsService { 
    @Autowired UserDao userDao; // 根据账号查询用户信息 @Override public UserDetails loadUserByUsername(String username) { 
    //将来连接数据库根据账号查询用户信息 UserDto userDto = userDao.getUserByUsername(username); //当查询此用户不存在时,将抛出用户名未找到异常 if (userDto == null) { 
    throw new UsernameNotFoundException("No such user found, the user name is: "+username); } //根据用户id查询权限 List<String> permissions = userDao.findPermissionsByUserId(userDto.getId()); //将permissions转为数组 String[] permissionArray = new String[permissions.size()]; permissions.toArray(permissionArray); UserDetails userDetails = User.withUsername(userDto.getUsername()).password(userDto.getPassword()).authorities(permissionArray) .build(); return userDetails; } } 

7. 编写controller层

package com.aaa.controller; import com.aaa.entity.UserDto; import com.aaa.service.UserService; import com.aaa.util.SecurityUtil; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; import javax.annotation.Resource; import java.util.HashMap; import java.util.Map; / * @author 淮南King */ @RestController public class LoginController { 
    @Resource private UserService service; @RequestMapping(value = "/login-success",produces = { 
   "text/plain;charset=UTF-8"}) public ModelAndView loginSuccess(){ 
    //获取当前线程的SecurityContext Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); //获取当前线程中用户的名称,将名称传递至页面 Map<String,Object> attributes = new HashMap<>(); attributes.put("username",SecurityUtil.getUserNameByAuthentication(authentication)); return new ModelAndView("menu",attributes); } @PostMapping("/user/register") public String register(UserDto userDto) { 
    service.addUser(userDto); return userDto.getUsername()+"注册成功"; } @GetMapping("/register") public ModelAndView registerView() { 
    return new ModelAndView("register"); } } 
package com.aaa.controller; import com.aaa.util.SecurityUtil; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; / * @author 淮南King * @date 2020-07-21 */ @RestController @RequestMapping("/resource") public class AuthController { 
    / * 测试资源1 * 拥有p1权限才可以访问 * * @return */ @GetMapping(value = "/r1", produces = { 
   "text/plain;charset=UTF-8"}) @PreAuthorize("hasAuthority('p1')") public String resource1() { 
    //获取当前线程的SecurityContext Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); //获取当前线程的名称 return SecurityUtil.getUserNameByAuthentication(authentication) + " 访问资源1"; } / * 测试资源2 * 拥有p2权限才可以访问 * * @return */ @GetMapping(value = "/r2", produces = { 
   "text/plain;charset=UTF-8"}) @PreAuthorize("hasAuthority('p2')") public String resource2() { 
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); return SecurityUtil.getUserNameByAuthentication(authentication) + " 访问资源2"; } / * 测试资源3 * 拥有p3权限才可以访问 * * @return */ @GetMapping(value = "/r3", produces = { 
   "text/plain;charset=UTF-8"}) @PreAuthorize("hasAuthority('p3')") public String resource3() { 
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); return SecurityUtil.getUserNameByAuthentication(authentication) + " 访问资源3"; } } 

附:实体类与帮助类

实体类

UserDto.java

package com.aaa.entity; / * DTO:与数据库保持一致<br> * 用户信息 * @author 淮南King */ public class UserDto { 
    / * 用户id */ private String id; / * 用户名 */ private String username; / * 用户密码 */ private String password; / * 用户角色ID */ private Integer roleId; public String getId() { 
    return id; } public void setId(String id) { 
    this.id = id; } public String getUsername() { 
    return username; } public void setUsername(String username) { 
    this.username = username; } public String getPassword() { 
    return password; } public void setPassword(String password) { 
    this.password = password; } public Integer getRoleId() { 
    return roleId; } public void setRoleId(Integer roleId) { 
    this.roleId = roleId; } } 

PermissionDto.java

package com.aaa.entity; / * 权限信息 * @author 淮南King */ public class PermissionDto { 
    / * 权限id */ private String id; / * 权限代号 */ private String code; / * 权限描述 */ private String description; / * 路径 */ private String url; public String getId() { 
    return id; } public void setId(String id) { 
    this.id = id; } public String getCode() { 
    return code; } public void setCode(String code) { 
    this.code = code; } public String getDescription() { 
    return description; } public void setDescription(String description) { 
    this.description = description; } public String getUrl() { 
    return url; } public void setUrl(String url) { 
    this.url = url; } } 

帮助类

SecurityUtil.java

package com.aaa.util; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.UserDetails; / * Security帮助类 * @author 淮南King * @date 2020-07-21 */ public class SecurityUtil { 
    / * 根据当前执行线程的SecurityContext获取用户名称 * @param authentication 当前认证通过的用户身份 * @return 用户名 */ public static String getUserNameByAuthentication(Authentication authentication){ 
    String username = null; //用户身份 Object principal = authentication.getPrincipal(); if(principal == null){ 
    return "匿名"; } if(principal instanceof UserDetails){ 
    UserDetails userDetails = (UserDetails)principal; username = userDetails.getUsername(); }else{ 
    username = principal.toString(); } return username; } } 

项目已完整上传到GitHub传送门

end…

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/118047.html

(0)
上一篇 2025-11-16 10:15
下一篇 2025-11-16 10:27

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信