任何一个企业级系统,权限必不可少html
早年写的关于shiro(基本上是基于SSM框架(即Spring+SpringMVC+MyBatis)文章以下(仅供参考):
shiro实战系列\(一\)之入门实战
Spring\(二\)之入门示例
shiro实战系列\(二\)之入门实战续
shiro实战系列\(三\)之架构
shiro实战系列\(四\)之配置
shiro实战系列\(五\)之Authentication\(身份验证\)
shiro实战系列\(六\)之Authorization\(受权\)
shiro实战系列\(七\)之Realm
shiro实战系列\(八\)之安全管理器
shiro实战系列\(九\)之Web
shiro实战系列\(十\)之Subject
shiro实战系列\(十一\)之Caching
shiro实战系列\(十二\)之经常使用专业术语
shiro实战系列\(十三\)之单元测试
shiro实战系列\(十四\)之配置
shiro实战系列\(十五\)之Spring集成Shiro前端
上面一共十五篇文章是早年在创业公司作相关的技术调研整理而成的,代码例子较少,偏理论性比较强,因此本篇文章再也不赘述一些理论性内容,接下来开始进入实战。java
这里列举的是子模块pom.xmlmysql
<properties> <java.version>1.8</java.version> <druid-spring-boot-starter.version>1.1.13</druid-spring-boot-starter.version> </properties> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.20</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.1</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-extension</artifactId> <version>3.4.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>${druid-spring-boot-starter.version}</version> </dependency> <!-- SpringBoot Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- AOP依赖,必定要加,不然权限拦截验证不生效 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!-- Mysql Connector --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.19</version> </dependency> <!-- Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis-reactive</artifactId> </dependency> <!-- Shiro 核心依赖 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> <!-- Shiro-redis插件 --> <dependency> <groupId>org.crazycake</groupId> <artifactId>shiro-redis</artifactId> <version>3.1.0</version> </dependency> <!-- StringUtilS工具 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.5</version> </dependency> <!-- json 转换工具 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
父pom.xml(主要针对SpringBoot版本):react
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.5.RELEASE</version> <relativePath/> </parent>
版本必定要对,不然会有各类奇葩问题。git
例如(若是版本不对会出现这样的问题,启动正常,在请求登陆接口就会报这样的错误):
错误信息:github
java.lang.NoSuchMethodError: redis.clients.jedis.ScanResult.getStringCursor()...
server: port: 5050 spring: # Redis数据源 redis: host: localhost port: 6379 timeout: 6000 password: 123456 jedis: pool: max-active: 1000 # 链接池最大链接数(使用负值表示没有限制) max-wait: -1 # 链接池最大阻塞等待时间(使用负值表示没有限制) max-idle: 10 # 链接池中的最大空闲链接 min-idle: 5 # 链接池中的最小空闲链接 # 配置数据源 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/wordpress?useUnicode=true&characterEncoding=utf-8&serverTimeZone=GMT username: root password: 1234 type: com.alibaba.druid.pool.DruidDataSource # mybatis-plus相关配置 mybatis-plus: # xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置) mapper-locations: classpath:mapper/*.xml # 如下配置均有默认值,能够不设置 global-config: db-config: #主键类型 AUTO:"数据库ID自增" INPUT:"用户输入ID",ID_WORKER:"全局惟一ID (数字类型惟一ID)", UUID:"全局惟一ID UUID"; id-type: auto #字段策略 IGNORED:"忽略判断" NOT_NULL:"非 NULL 判断") NOT_EMPTY:"非空判断" field-strategy: NOT_EMPTY #数据库类型 db-type: MYSQL configuration: # 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的相似映射 map-underscore-to-camel-case: true # 若是查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段 call-setters-on-nulls: true # 这个配置会将执行的sql打印出来,在开发或测试的时候能够用 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
核心配置类特别注意的是接口放行,不然访问接口会出现404。web
package com.blog.tutorial07.shiro.config; import com.blog.tutorial07.shiro.shiro.ShiroRealm; import com.blog.tutorial07.shiro.shiro.ShiroSessionIdGenerator; import com.blog.tutorial07.shiro.shiro.ShiroSessionManager; import com.blog.tutorial07.shiro.utils.SHA256Util; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.mgt.SessionManager; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.crazycake.shiro.RedisCacheManager; import org.crazycake.shiro.RedisManager; import org.crazycake.shiro.RedisSessionDAO; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; /** * @Description Shiro配置类 * @Author youcong */@Configuration public class ShiroConfig { private final String CACHE_KEY = "shiro:cache:"; private final String SESSION_KEY = "shiro:session:"; private final int EXPIRE = 1800; //Redis配置 @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private int port; @Value("${spring.redis.timeout}") private int timeout; @Value("${spring.redis.password}") private String password; /** * 开启Shiro-aop注解支持 * @Attention 使用代理方式因此须要开启代码支持 * @Author youcong */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } /** * Shiro基础配置 * @Author youcong */ @Bean public ShiroFilterFactoryBean shiroFilterFactory(SecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); // 注意过滤器配置顺序不能颠倒 // 配置过滤:不会被拦截的连接 filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put("/user/**", "anon"); filterChainDefinitionMap.put("/**", "authc"); // 配置shiro默认登陆界面地址,先后端分离中登陆界面跳转应由前端路由控制,后台仅返回json数据 shiroFilterFactoryBean.setLoginUrl("/userLogin/unauth"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } /** * 安全管理器 * @Author youcong */ @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 自定义Ssession管理 securityManager.setSessionManager(sessionManager()); // 自定义Cache实现 securityManager.setCacheManager(cacheManager()); // 自定义Realm验证 securityManager.setRealm(shiroRealm()); return securityManager; } /** * 身份验证器 * @Author youcong */ @Bean public ShiroRealm shiroRealm() { ShiroRealm shiroRealm = new ShiroRealm(); shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return shiroRealm; } /** * 凭证匹配器 * 将密码校验交给Shiro的SimpleAuthenticationInfo进行处理,在这里作匹配配置 * @Author youcong */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher shaCredentialsMatcher = new HashedCredentialsMatcher(); // 散列算法:这里使用SHA256算法; shaCredentialsMatcher.setHashAlgorithmName(SHA256Util.HASH_ALGORITHM_NAME); // 散列的次数,好比散列两次,至关于 md5(md5("")); shaCredentialsMatcher.setHashIterations(SHA256Util.HASH_ITERATIONS); return shaCredentialsMatcher; } /** * 配置Redis管理器 * @Attention 使用的是shiro-redis开源插件 * @Author youcong */ @Bean public RedisManager redisManager() { RedisManager redisManager = new RedisManager(); redisManager.setHost(host); redisManager.setPort(port); redisManager.setTimeout(timeout); redisManager.setPassword(password); return redisManager; } /** * 配置Cache管理器 * 用于往Redis存储权限和角色标识 * @Attention 使用的是shiro-redis开源插件 * @Author youcong */ @Bean public RedisCacheManager cacheManager() { RedisCacheManager redisCacheManager = new RedisCacheManager(); redisCacheManager.setRedisManager(redisManager()); redisCacheManager.setKeyPrefix(CACHE_KEY); // 配置缓存的话要求放在session里面的实体类必须有个id标识 redisCacheManager.setPrincipalIdFieldName("id"); return redisCacheManager; } /** * SessionID生成器 * @Author youcong */ @Bean public ShiroSessionIdGenerator sessionIdGenerator(){ return new ShiroSessionIdGenerator(); } /** * 配置RedisSessionDAO * @Attention 使用的是shiro-redis开源插件 * @Author youcong */ @Bean public RedisSessionDAO redisSessionDAO() { RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); redisSessionDAO.setRedisManager(redisManager()); redisSessionDAO.setSessionIdGenerator(sessionIdGenerator()); redisSessionDAO.setKeyPrefix(SESSION_KEY); redisSessionDAO.setExpire(EXPIRE); return redisSessionDAO; } /** * 配置Session管理器 * @Author youcong */ @Bean public SessionManager sessionManager() { ShiroSessionManager shiroSessionManager = new ShiroSessionManager(); shiroSessionManager.setSessionDAO(redisSessionDAO()); return shiroSessionManager; } }
ShiroRealm.javaredis
package com.blog.tutorial07.shiro.shiro; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.blog.tutorial07.shiro.entity.Usermeta; import com.blog.tutorial07.shiro.entity.Users; import com.blog.tutorial07.shiro.service.UsermetaService; import com.blog.tutorial07.shiro.service.UsersService; import com.blog.tutorial07.shiro.utils.ShiroUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; import java.util.HashSet; import java.util.List; import java.util.Set; /** * @Description Shiro权限匹配和帐号密码匹配 * @Author Sans * @CreateTime 2019/6/15 11:27 */public class ShiroRealm extends AuthorizingRealm { @Autowired private UsersService sysUserService; @Autowired private UsermetaService sysRoleService; /** * 受权权限 * 用户进行权限验证时候Shiro会去缓存中找,若是查不到数据,会执行这个方法去查权限,并放入缓存中 * * @Author Sans * @CreateTime 2019/6/12 11:44 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //获取用户ID SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); Users user = (Users) principalCollection.getPrimaryPrincipal(); Long userId = user.getId(); //这里能够进行受权和处理 Set<String> rolesSet = new HashSet<>(); QueryWrapper<Usermeta> roleWrapper = new QueryWrapper<>(); roleWrapper.eq("user_id", user.getId()); roleWrapper.eq("meta_key", "wp_user_level"); List<Usermeta> roleList = sysRoleService.list(roleWrapper); for (Usermeta role : roleList) { rolesSet.add(role.getMetaValue()); } authorizationInfo.setRoles(rolesSet); return authorizationInfo; } /** * 身份认证 * * @Author Sans * @CreateTime 2019/6/12 12:36 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //获取用户的输入的帐号. String username = (String) authenticationToken.getPrincipal(); //经过username从数据库中查找 User对象,若是找到进行验证 //实际项目中,这里能够根据实际状况作缓存,若是不作,Shiro本身也是有时间间隔机制,2分钟内不会重复执行该方法 QueryWrapper<Users> wrapper = new QueryWrapper<>(); wrapper.eq("user_login", username); Users user = sysUserService.getOne(wrapper); //判断帐号是否存在 if (user == null) { throw new AuthenticationException(); } //判断帐号是否被冻结 if (user.getUserStatus() == null || user.getUserStatus().equals("1")) { throw new LockedAccountException(); } //进行验证 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( user, //用户名 user.getUserPass(), //密码 ByteSource.Util.bytes(user.getUserActivationKey()), //设置盐值 getName() ); //验证成功开始踢人(清除缓存和Session) ShiroUtils.deleteCache(username, true); return authenticationInfo; } }
ShiroSessionIdGenerator.java算法
package com.blog.tutorial07.shiro.shiro; import com.blog.tutorial07.shiro.constant.RedisConstant; import org.apache.shiro.session.Session; import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator; import org.apache.shiro.session.mgt.eis.SessionIdGenerator; import java.io.Serializable; /** * @Description 自定义SessionId生成器 * @Author youcong */public class ShiroSessionIdGenerator implements SessionIdGenerator { @Override public Serializable generateId(Session session) { Serializable sessionId = new JavaUuidSessionIdGenerator().generateId(session); return String.format(RedisConstant.REDIS_PREFIX_LOGIN, sessionId); } }
ShiroSessionManager.java
package com.blog.tutorial07.shiro.shiro; import org.apache.commons.lang3.StringUtils; import org.apache.shiro.web.servlet.ShiroHttpServletRequest; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.apache.shiro.web.util.WebUtils; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.io.Serializable; /** * @Description 自定义获取Token * @Author youcong */public class ShiroSessionManager extends DefaultWebSessionManager { //定义常量 private static final String AUTHORIZATION = "Authorization"; private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request"; //重写构造器 public ShiroSessionManager() { super(); this.setDeleteInvalidSessions(true); } /** * 重写方法实现从请求头获取Token便于接口统一 * 每次请求进来,Shiro会去从请求头找Authorization这个key对应的Value(Token) * @Author youcong */ @Override public Serializable getSessionId(ServletRequest request, ServletResponse response) { String token = WebUtils.toHttp(request).getHeader(AUTHORIZATION); //若是请求头中存在token 则从请求头中获取token if (!StringUtils.isEmpty(token)) { request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, token); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); return token; } else { // 这里禁用掉Cookie获取方式 // 按默认规则从Cookie取Token // return super.getSessionId(request, response); return null; } } }
SHA256Util.java
package com.blog.tutorial07.shiro.utils; import org.apache.shiro.crypto.hash.SimpleHash; /** * @Description Sha-256加密工具 * @Author youcong */public class SHA256Util { /** 私有构造器 **/ private SHA256Util(){}; /** 加密算法 **/ public final static String HASH_ALGORITHM_NAME = "SHA-256"; /** 循环次数 **/ public final static int HASH_ITERATIONS = 15; /** 执行加密-采用SHA256和盐值加密 **/ public static String sha256(String password, String salt) { return new SimpleHash(HASH_ALGORITHM_NAME, password, salt, HASH_ITERATIONS).toString(); } }
ShiroUtils.java
package com.blog.tutorial07.shiro.utils; import com.blog.tutorial07.shiro.entity.Users; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.Authenticator; import org.apache.shiro.authc.LogoutAware; import org.apache.shiro.session.Session; import org.apache.shiro.subject.SimplePrincipalCollection; import org.apache.shiro.subject.support.DefaultSubjectContext; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.crazycake.shiro.RedisSessionDAO; import java.util.Collection; import java.util.Objects; /** * @Description Shiro工具类 * @Author youcong */public class ShiroUtils { /** * 私有构造器 **/ private ShiroUtils() { } private static RedisSessionDAO redisSessionDAO = SpringUtil.getBean(RedisSessionDAO.class); /** * 获取当前用户Session * * @Author youcong * @Return SysUserEntity 用户信息 */ public static Session getSession() { return SecurityUtils.getSubject().getSession(); } /** * 用户登出 * * @Author youcong */ public static void logout() { SecurityUtils.getSubject().logout(); } /** * 获取当前用户信息 * * @Author youcong * @Return SysUserEntity 用户信息 */ public static Users getUserInfo() { return (Users) SecurityUtils.getSubject().getPrincipal(); } /** * 删除用户缓存信息 * * @Author youcong * @Param username 用户名称 * @Param isRemoveSession 是否删除Session * @Return void */ public static void deleteCache(String username, boolean isRemoveSession) { //从缓存中获取Session Session session = null; Collection<Session> sessions = redisSessionDAO.getActiveSessions(); Users sysUserEntity; Object attribute = null; for (Session sessionInfo : sessions) { //遍历Session,找到该用户名称对应的Session attribute = sessionInfo.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY); if (attribute == null) { continue; } sysUserEntity = (Users) ((SimplePrincipalCollection) attribute).getPrimaryPrincipal(); if (sysUserEntity == null) { continue; } if (Objects.equals(sysUserEntity.getUserLogin(), username)) { session = sessionInfo; break; } } if (session == null || attribute == null) { return; } //删除session if (isRemoveSession) { redisSessionDAO.delete(session); } //删除Cache,在访问受限接口时会从新受权 DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager(); Authenticator authc = securityManager.getAuthenticator(); ((LogoutAware) authc).onLogout((SimplePrincipalCollection) attribute); } }
SpringUtil.java
package com.blog.tutorial07.shiro.utils; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** * @Description Spring上下文工具类 * @Author youcong */@Component public class SpringUtil implements ApplicationContextAware { private static ApplicationContext context; /** * Spring在bean初始化后会判断是否是ApplicationContextAware的子类 * 若是该类是,setApplicationContext()方法,会将容器中ApplicationContext做为参数传入进去 * @Author youcong */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { context = applicationContext; } /** * 经过Name返回指定的Bean * @Author youcong */ public static <T> T getBean(Class<T> beanClass) { return context.getBean(beanClass); } }
package com.blog.tutorial07.shiro.controller; import com.blog.tutorial07.shiro.entity.Users; import com.blog.tutorial07.shiro.service.UsersService; import com.blog.tutorial07.shiro.utils.ShiroUtils; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.LockedAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authz.annotation.RequiresRoles; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; /** * @description: * @author: youcong * @time: 2020/11/14 13:27 */@RestController @RequestMapping("/user") public class UserController { @Autowired private UsersService usersService; @Autowired private RedisTemplate redisTemplate; /** * 登陆 * * @Author youcong */ @PostMapping("/login") public Map<String, Object> login(@RequestParam String username, @RequestParam String password) { Map<String, Object> map = new HashMap<>(); //进行身份验证 try { //验证身份和登录 Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, password); //进行登陆操做 subject.login(token); } catch (IncorrectCredentialsException e) { map.put("code", 500); map.put("msg", "用户不存在或者密码错误"); return map; } catch (LockedAccountException e) { map.put("code", 500); map.put("msg", "登陆失败,该用户已被冻结"); return map; } catch (AuthenticationException e) { map.put("code", 500); map.put("msg", "该用户不存在"); return map; } catch (Exception e) { map.put("code", 500); map.put("msg", "未知异常"); return map; } map.put("code", 0); map.put("msg", "登陆成功"); map.put("token", ShiroUtils.getSession().getId().toString()); return map; } /** * 未登陆 * * @Author youcong */ @RequestMapping("/unauth") public Map<String, Object> unauth() { Map<String, Object> map = new HashMap<>(); map.put("code", 500); map.put("msg", "未登陆"); return map; } @PostMapping("/list") @RequiresRoles("1") public String list() { System.out.println("list:" + redisTemplate.opsForValue().get("list")); if (StringUtils.isEmpty(redisTemplate.opsForValue().get("list"))) { redisTemplate.opsForValue().set("list", usersService.list(), 360, TimeUnit.MINUTES); } return redisTemplate.opsForValue().get("list").toString(); } }
查看redis,如图:
不管是SpringSecurity仍是Shiro,基本上整合很是类似,也很简单。
若是有朋友看完这篇文章仍是不明白的话,能够访问以下地址:
https://github.com/developers...
将项目克隆到本地运行。这个git仓库,sql脚本什么的都有。
我本次用到的类基本上是基于这个的,只不过数据表不同,我本次所使用的是wordpress的数据库。