wayne_liberary / Shiro / SpringMVC+Apache Shiro+JPA(hibernate)...

分享

   

SpringMVC+Apache Shiro+JPA(hibernate)案例教学(一)整合配置

2014-08-07  wayne_lib...

序:

关于标题:

  说是教学,实在愧不敢当,但苦与本人文笔有限,实在找不到更合理,谦逊的词语表达,只能先这样定义了。
  其实最真实的想法,只是希望这个关键词能让更多的人浏览到这篇文章,也算是对于自己写文章的一个肯定吧。^_^!

关于内容:

  再写这系列文章之前,本人和许多人一样都是伸手党,并深深的了解咱伸手党且英文较差的朋友对于新知识的学习及获取中文资料少的痛苦。所以本着“取之于民,共享与民”的原则,记录下实际工作中对SpringMVC+Shiro整合应用的部分心得。本人技术水平有限,仅希望文章对他人有一定的参考价值,足矣。

关于拍砖:

  请轻拍,很痛的。且最好附上您的高见。

另:Shiro基础及原理,推荐学习,同时感谢他的博客,在我学习Shiro的过程中,给予很大帮助!


 

教学:

一、SpringMVC+Apache Shiro+JPA(hibernate)整合配置

(1)新建Web工程,且导入所需Jar包。(以下截图为真实项目中删减后保留,如有不需要的JAR包,请自行删除)

(2)配置web.xml,applicationContext.xml, spring-mvc.xml, log4j.properties

web.xml

复制代码
  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
  3 
  4     <context-param>
  5         <param-name>contextConfigLocation</param-name>
  6         <param-value>classpath:applicationContext.xml</param-value>
  7     </context-param>
  8 
  9     <listener>
 10         <listener-class>
 11             org.springframework.web.context.ContextLoaderListener
 12         </listener-class>
 13     </listener>
 14 
 15     <!-- 配置spring管理OpenEntityManagerInViewFilter-->
 16     <filter>
 17         <filter-name>hibernateFilter</filter-name>
 18         <filter-class>
 19             org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter
 20         </filter-class>
 21     </filter>
 22     <filter-mapping>
 23         <filter-name>hibernateFilter</filter-name>
 24         <url-pattern>/*</url-pattern>
 25     </filter-mapping>
 26 
 27     <!-- Shiro filter -->
 28     <filter>
 29         <filter-name>shiroFilter</filter-name>
 30         <filter-class>
 31             org.springframework.web.filter.DelegatingFilterProxy
 32         </filter-class>
 33         <init-param>
 34             <param-name>targetFilterLifecycle</param-name>
 35             <param-value>true</param-value>
 36         </init-param>
 37     </filter>
 38     <filter-mapping>
 39         <filter-name>shiroFilter</filter-name>
 40         <url-pattern>/*</url-pattern>
 41     </filter-mapping>
 42 
 43     <!-- 配置Log4j -->
 44     <context-param>
 45         <param-name>webAppRootKey</param-name>
 46         <param-value>spring_springmvc_jpa.root</param-value>
 47     </context-param>
 48     <context-param>
 49         <param-name>log4jConfigLocation</param-name>
 50         <param-value>classpath:log4j.properties</param-value>
 51     </context-param>
 52     <listener>
 53         <listener-class>
 54             org.springframework.web.util.Log4jConfigListener
 55         </listener-class>
 56     </listener>
 57 
 58     <!-- 配置编码过滤器 -->
 59     <filter>
 60         <filter-name>characterEncodingFilter</filter-name>
 61         <filter-class>
 62             org.springframework.web.filter.CharacterEncodingFilter
 63         </filter-class>
 64         <init-param>
 65             <param-name>encoding</param-name>
 66             <param-value>UTF-8</param-value>
 67         </init-param>
 68         <init-param>
 69             <param-name>forceEncoding</param-name>
 70             <param-value>true</param-value>
 71         </init-param>
 72     </filter>
 73     <filter-mapping>
 74         <filter-name>characterEncodingFilter</filter-name>
 75         <url-pattern>/*</url-pattern>
 76     </filter-mapping>
 77 
 78     <filter>
 79         <filter-name>HiddenHttpMethodFilter</filter-name>
 80         <filter-class>
 81             org.springframework.web.filter.HiddenHttpMethodFilter
 82         </filter-class>
 83     </filter>
 84     <filter-mapping>
 85         <filter-name>HiddenHttpMethodFilter</filter-name>
 86         <servlet-name>dispatcherServlet</servlet-name>
 87     </filter-mapping>
 88 
 89     <!-- Spring 刷新Introspector防止内存泄露 -->
 90     <listener>
 91         <listener-class>
 92             org.springframework.web.util.IntrospectorCleanupListener
 93         </listener-class>
 94     </listener>
 95 
 96 
 97     <!-- SpringMVC核心分发器 -->
 98     <servlet>
 99         <servlet-name>dispatcherServlet</servlet-name>
100         <servlet-class>
101             org.springframework.web.servlet.DispatcherServlet
102         </servlet-class>
103         <init-param>
104             <param-name>contextConfigLocation</param-name>
105             <param-value>classpath:spring-mvc.xml</param-value>
106         </init-param>
107         <load-on-startup>1</load-on-startup>
108     </servlet>
109     <!-- 覆盖default servlet的/, springmvc servlet将处理原来处理静态资源的映射 -->
110     <servlet-mapping>
111         <servlet-name>dispatcherServlet</servlet-name>
112         <url-pattern>/</url-pattern>
113     </servlet-mapping>
114 
115     <jsp-config>
116         <jsp-property-group>
117             <display-name>JSPConfiguration</display-name>
118             <url-pattern>*.jsp</url-pattern>
119             <el-ignored>true</el-ignored>
120             <page-encoding>utf-8</page-encoding>
121             <scripting-invalid>false</scripting-invalid>
122         </jsp-property-group>
123     </jsp-config>
124 
125     <welcome-file-list>
126         <welcome-file>login.jsp</welcome-file>
127     </welcome-file-list>
128     
129 </web-app>
复制代码

 

applicationContext.xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:cache="http://www.springframework.org/schema/cache"
    xmlns:jaxws="http://cxf.apache.org/jaxws"
    xsi:schemaLocation="
                    http://www.springframework.org/schema/beans
                    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                    http://www.springframework.org/schema/tx 
                    http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
                    http://www.springframework.org/schema/aop 
                    http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
                    http://www.springframework.org/schema/context      
                    http://www.springframework.org/schema/context/spring-context-3.1.xsd
                    http://www.springframework.org/schema/cache 
                    http://www.springframework.org/schema/cache/spring-cache-3.1.xsd
                    http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd ">
    <!-- 注解支持 -->
    <context:annotation-config />

    <!-- 启动组件扫描,排除@Controller组件,该组件由SpringMVC配置文件扫描 -->
    <context:component-scan base-package="org.shiro.demo">
        <context:exclude-filter type="annotation"
            expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

    <!-- 属性文件位置 -->
    <context:property-placeholder location="classpath:jdbc.properties" />

    <!-- 数据源 -->
    <bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource"
        destroy-method="close">
        <!-- 数据库驱动 -->
        <property name="driverClass" value="${jdbc.driverClassName}" />
        <!-- 相应驱动的jdbcUrl-->
        <property name="jdbcUrl" value="${jdbc.url}" />
        <!-- 数据库的用户名 -->
        <property name="username" value="${jdbc.username}" />
        <!-- 数据库的密码 -->
        <property name="password" value="${jdbc.password}" />
        <!-- 检查数据库连接池中空闲连接的间隔时间,单位是分,默认值:240,如果要取消则设置为0 -->
        <property name="idleConnectionTestPeriod"
            value="${BoneCP.idleConnectionTestPeriod}" />
        <!-- 连接池中未使用的链接最大存活时间,单位是分,默认值:60,如果要永远存活设置为0 -->
        <property name="idleMaxAge" value="${BoneCP.idleMaxAge}" />
        <!-- 每个分区最大的连接数 -->
        <property name="maxConnectionsPerPartition"
            value="${BoneCP.maxConnectionsPerPartition}" />
        <!-- 每个分区最小的连接数 -->
        <property name="minConnectionsPerPartition"
            value="${BoneCP.minConnectionsPerPartition}" />
        <!-- 分区数 ,默认值2,最小1,推荐3-4,视应用而定 -->
        <property name="partitionCount"
            value="${BoneCP.partitionCount}" />
        <!-- 每次去拿数据库连接的时候一次性要拿几个,默认值:2 -->
        <property name="acquireIncrement"
            value="${BoneCP.acquireIncrement}" />
        <!-- 缓存prepared statements的大小,默认值:0 -->
        <property name="statementsCacheSize"
            value="${BoneCP.statementsCacheSize}" />
        <!-- 每个分区释放链接助理进程的数量,默认值:3,除非你的一个数据库连接的时间内做了很多工作,不然过多的助理进程会影响你的性能 -->
        <property name="releaseHelperThreads"
            value="${BoneCP.releaseHelperThreads}" />
    </bean>

    <!-- JPA实体管理器工厂 -->
    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="persistenceProvider" ref="persistenceProvider" />
        <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
        <property name="jpaDialect" ref="jpaDialect" />

        <property name="packagesToScan" value="org.shiro.demo.entity" />

        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">
                    org.hibernate.dialect.MySQL5Dialect
                </prop>
                <prop key="hibernate.connection.driver_class">
                    com.mysql.jdbc.Driver
                </prop>
                <prop key="hibernate.max_fetch_depth">3</prop>
                <prop key="hibernate.jdbc.fetch_size">18</prop>
                <prop key="hibernate.jdbc.batch_size">10</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop><!-- validate/update/create -->
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">false</prop>
                <prop key="javax.persistence.validation.mode">none</prop>
            </props>
        </property>
    </bean>
    
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">        
        <property name="defaultEncoding" value="utf-8"></property>        
    </bean>
    
    <!-- 用于指定持久化实现厂商类 -->
    <bean id="persistenceProvider"
        class="org.hibernate.ejb.HibernatePersistence" />
        
    <!-- 用于设置JPA实现厂商的特定属性 -->
    <bean id="jpaVendorAdapter"
        class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        <property name="database" value="MYSQL" />
    </bean>
    
    <!-- 用于指定一些高级特性 -->
    <bean id="jpaDialect"
        class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />

    <!-- 事务管理器 -->
    <bean id="txManager"
        class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory"
            ref="entityManagerFactory" />
    </bean>
    
    <!-- 注解式事务 -->
    <tx:annotation-driven transaction-manager="txManager" />
    
    <bean id="securityManager"
        class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="shiroDbRealm" />
    </bean>
    
    <!-- 項目自定义的Realm -->
    <bean id="shiroDbRealm" class="org.shiro.demo.service.realm.ShiroDbRealm" ></bean>
    
    <!-- Shiro Filter -->
    <bean id="shiroFilter"
        class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="/" />
        <property name="successUrl" value="/system/main" />
        <property name="unauthorizedUrl" value="/system/error" />
        <property name="filterChainDefinitions">
            <value>
            /login = anon
            /validateCode = anon
            /** = authc
            </value>
        </property>
    </bean>
</beans>
复制代码

spring-mvc.xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation=" 
                http://www.springframework.org/schema/beans 
                http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 
                http://www.springframework.org/schema/context 
                http://www.springframework.org/schema/context/spring-context-3.1.xsd 
                http://www.springframework.org/schema/mvc 
                http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
    
    <mvc:annotation-driven />

    <!-- 默认访问跳转到登录页面 -->
    <mvc:view-controller path="/" view-name="forward:/login" />
    
    <context:component-scan base-package="org.shiro.demo.controller" />
    
    <mvc:resources mapping="/resources/**" location="/resources/" />

    <!-- 采用SpringMVC自带的JSON转换工具,支持@ResponseBody注解 -->
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
            </list>
        </property>
    </bean>

    <!-- 避免IE执行AJAX时,返回JSON出现下载文件 -->
    <bean id="mappingJacksonHttpMessageConverter"
        class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
        <property name="supportedMediaTypes">
            <list>
                <value>text/html;charset=UTF-8</value>
            </list>
        </property>
    </bean>
    
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/" />
        <property name="suffix" value=".jsp" />
    </bean>
    
    <!-- 开启Shiro注解的Spring配置方式的beans。在lifecycleBeanPostProcessor之后运行 -->
    <bean
        class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
        depends-on="lifecycleBeanPostProcessor" />
    <bean
        class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager" />
    </bean>
    
    <bean id="lifecycleBeanPostProcessor"
        class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

    <!-- shiro为集成spring -->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <prop key="org.apache.shiro.authz.UnauthorizedException">/system/error</prop>
            </props>
        </property>
    </bean>
    
</beans>
复制代码

jdbc.properties

复制代码
jdbc.driverClassName=com.mysql.jdbc.Driver

jdbc.url=jdbc:mysql://localhost:3306/shirodemo?useUnicode=true&characterEncoding=UTF-8

jdbc.username=root

jdbc.password=root

BoneCP.idleConnectionTestPeriod=60

BoneCP.idleMaxAge=60

BoneCP.maxConnectionsPerPartition=5

BoneCP.minConnectionsPerPartition=1

BoneCP.partitionCount=3

BoneCP.acquireIncrement=2  

BoneCP.statementsCacheSize=0 
  
BoneCP.releaseHelperThreads=3
复制代码

log4j.properties

复制代码
log4j.rootLogger=INFO,stdout,file

log4j.appender.stdout=org.apache.log4j.ConsoleAppender 
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout.ConversionPattern=%-d{yyyy-MM-dd HH\:mm\:ss,SSS} [%c]-[%p] %m%n 

log4j.appender.file=org.apache.log4j.DailyRollingFileAppender 
log4j.appender.file.File=${spring_springmvc_jpa.root}/shirodemo.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout 
log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c] [%p] - %m%n

log4j.logger.org.hibernate.tool.hbm2ddl=info
复制代码

 

(3)建立JavaBean,User.java,Role.java,Permisson.java

User.java

复制代码
package org.shiro.demo.entity;

import java.io.Serializable;
import java.util.Collection;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

import org.codehaus.jackson.annotate.JsonIgnore;

@Entity
@Table(name = "CMSUser")
public class User implements Serializable {

    private static final long serialVersionUID = 7419229779731522702L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "userid")
    private Long id;
    @Column(length = 50, unique = true)
    private String account;
    @Column(length = 100)
    @JsonIgnore    //springmvc生成json不包含此字段
    private String password;
    @Column(length = 50)
    private String nickname;
    
    @ManyToMany(cascade = {CascadeType.PERSIST })
    @JsonIgnore
    @JoinTable(name = "CMSUserRole", joinColumns = { @JoinColumn(name = "userid", referencedColumnName = "userid") }, 
        inverseJoinColumns = { @JoinColumn(name = "roleid", referencedColumnName = "roleid") })
    private Collection<Role> roles;
    
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public Collection<Role> getRoles() {
        return roles;
    }

    public void setRoles(Collection<Role> roles) {
        this.roles = roles;
    }

    /**
     * 本函数输出将作为默认的<shiro:principal/>输出.
     */
    public String toString() {
        return account;
    }
}
复制代码

 

Role.java

复制代码
package org.shiro.demo.entity;

import java.io.Serializable;
import java.util.Collection;

import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

@Entity
@Table(name = "CMSRole")
public class Role implements Serializable{

    private static final long serialVersionUID = 6177417450707400228L;
    
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="roleid")
    private Long id;
    @Column(length=50)
    private String name;
    @Column(length=50)
    private String description;
    
    @ManyToMany(mappedBy = "roles")
    @Basic(fetch = FetchType.LAZY)
    private Collection<User> users;
    
    @ManyToMany(cascade={CascadeType.PERSIST,CascadeType.REFRESH,CascadeType.MERGE}, fetch = FetchType.LAZY)
    @JoinTable(name = "CMSRolePms", 
            joinColumns = { @JoinColumn(name = "roleid", updatable = false) }, 
            inverseJoinColumns = { @JoinColumn(name = "pmsid", updatable = false) })
    private Collection<Permission> pmss;
    
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public Collection<User> getUsers() {
        return users;
    }
    public void setUsers(Collection<User> users) {
        this.users = users;
    }
    public Collection<Permission> getPmss() {
        return pmss;
    }
    public void setPmss(Collection<Permission> pmss) {
        this.pmss = pmss;
    }
}
复制代码

 

Permisson.java

复制代码
package org.shiro.demo.entity;

import java.io.Serializable;
import java.util.Collection;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="CMSPermission")
public class Permission implements Serializable{

    private static final long serialVersionUID = -8792590494605747957L;
    
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="pmsid")
    private Long id;
    @Column(length=50)
    private String name;
    @Column(length=100)
    private String description;
    @Column(length=50)
    private String permission;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parentid")
    private Permission parent;
    
    @OneToMany(mappedBy = "parent",fetch = FetchType.LAZY,cascade={CascadeType.ALL})
    private Collection<Permission> children;
    
    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "pmss")
    private Collection<Role> roles;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getPermission() {
        return permission;
    }

    public void setPermission(String permission) {
        this.permission = permission;
    }

    public Permission getParent() {
        return parent;
    }

    public void setParent(Permission parent) {
        this.parent = parent;
    }

    public Collection<Permission> getChildren() {
        return children;
    }

    public void setChildren(Collection<Permission> children) {
        this.children = children;
    }

    public Collection<Role> getRoles() {
        return roles;
    }

    public void setRoles(Collection<Role> roles) {
        this.roles = roles;
    }
}
复制代码

 

至此,配置基本完成,请继续学习下一篇文章:

SpringMVC+Apache Shiro+JPA(hibernate)案例教学(二)基于SpringMVC+Shiro的用户登录权限验证

SpringMVC+Apache Shiro+JPA(hibernate)案例教学(三)给Shiro登录验证加上验证码

SpringMVC+Apache Shiro+JPA(hibernate)案例教学(四)基于Shiro验证用户权限,且给用户授权。

SpringMVC+Apache Shiro+JPA(hibernate)案例教学(五)关于源码及下载地址

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多
    喜欢该文的人也喜欢 更多

    ×
    ×

    ¥.00

    微信或支付宝扫码支付:

    开通即同意《个图VIP服务协议》

    全部>>