<!--导入shiro依赖--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> <!--添加ehcache--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.4.0</version> </dependency>
Shiroconfig:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import lombok.extern.log4j.Log4j2; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.cache.CacheManager; import org.apache.shiro.cache.MemoryConstrainedCacheManager; import org.apache.shiro.cache.ehcache.EhCacheManager; import org.apache.shiro.codec.Base64; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.CookieRememberMeManager; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.servlet.SimpleCookie; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.config.MethodInvokingFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; @Configuration @Log4j2 public class ShiroConfig { /** * ShiroFilterFactoryBean */ @Bean public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager){ log.info("进入【shiroFilter】"); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); //拦截器 Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put("/user/login", "anon"); filterChainDefinitionMap.put("/user/logout", "logout"); //根据用户的角色赋予相应的权限 // filterChainDefinitionMap.put("/add", "roles[admin]"); // filterChainDefinitionMap.put("/delete", "roles[admin]"); // filterChainDefinitionMap.put("/delete", "roles[author]"); filterChainDefinitionMap.put("/addPermission", "roles[author]"); filterChainDefinitionMap.put("/add", "perms[user:add]"); filterChainDefinitionMap.put("/delete", "perms[user:delete]"); filterChainDefinitionMap.put("/userList", "perms[user:list]"); // /** 匹配所有的路径 // 通过Map集合组成了一个拦截器链 ,自顶向下过滤,一旦匹配,则不再执行下面的过滤 // 如果下面的定义与上面冲突,那按照了谁先定义谁说了算 // /** 一定要配置在最后 filterChainDefinitionMap.put("/**", "authc"); // 将拦截器链设置到shiro中 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面 shiroFilterFactoryBean.setLoginUrl("/login"); // 登录成功后要跳转的链接 shiroFilterFactoryBean.setSuccessUrl("/index"); //未授权页面 shiroFilterFactoryBean.setUnauthorizedUrl("/403"); return shiroFilterFactoryBean; } /** * securityManager */ @Bean(name = "securityManager") public SecurityManager securityManager( @Qualifier("myShiroRealm") MyShiroRealm myShiroRealm, @Qualifier("rememberMeManager") CookieRememberMeManager rememberMeManager, // @Qualifier("cacheManager") CacheManager cacheManager, @Qualifier("ehCacheManager")EhCacheManager ehCacheManager){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // securityManager.setCacheManager(cacheManager); securityManager.setCacheManager(ehCacheManager); securityManager.setRememberMeManager(rememberMeManager); securityManager.setRealm(myShiroRealm); return securityManager; } /** * shiroRealm */ @Bean(name = "myShiroRealm") public MyShiroRealm myShiroRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher hashedCredentialsMatcher){ MyShiroRealm myShiroRealm = new MyShiroRealm(); myShiroRealm.setCachingEnabled(true); //启用身份验证缓存,即缓存AuthenticationInfo信息,默认false myShiroRealm.setAuthenticationCachingEnabled(true); //缓存AuthenticationInfo信息的缓存名称 在ehcache-shiro.xml中有对应缓存的配置 myShiroRealm.setAuthenticationCacheName("authenticationCache"); //启用授权缓存,即缓存AuthorizationInfo信息,默认false myShiroRealm.setAuthorizationCachingEnabled(true); //缓存AuthorizationInfo信息的缓存名称 在ehcache-shiro.xml中有对应缓存的配置 myShiroRealm.setAuthorizationCacheName("authorizationCache"); myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher); return new MyShiroRealm(); } /** * 密码匹配凭证管理器 * * @return */ @Bean(name = "hashedCredentialsMatcher") public HashedCredentialsMatcher hashedCredentialsMatcher() { log.info("hashedCredentialsMatcher()"); HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName("MD5");// 散列算法:这里使用MD5算法; hashedCredentialsMatcher.setHashIterations(2);// 散列的次数,比如散列两次,相当于md5(md5("")); return hashedCredentialsMatcher; } /** * cookie对象 * @return */ @Bean(name = "rememberMeCookie") public SimpleCookie rememberMeCookie(){ SimpleCookie simpleCookie = new SimpleCookie("rememberMe"); simpleCookie.setHttpOnly(true); //设置有效期时间30天 simpleCookie.setMaxAge(259200); return simpleCookie; } @Bean(name = "rememberMeManager") public CookieRememberMeManager rememberMeManager(@Qualifier("rememberMeCookie")SimpleCookie rememberMeCookie){ CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); cookieRememberMeManager.setCookie(rememberMeCookie); //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位) cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag==")); return cookieRememberMeManager; } /** * 开启shiro aop注解支持 * 使用代理方式;所以需要开启代码支持 * @param securityManager */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){ AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } /** * 开启cglib代理 */ @Bean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator(); creator.setProxyTargetClass(true); return creator; } // 缓存配置 //shiro自带的MemoryConstrainedCacheManager作缓存 // 但是只能用于本机,在集群时就无法使用,需要使用ehcache @Bean(name = "cacheManager") public CacheManager cacheManager() { MemoryConstrainedCacheManager cacheManager=new MemoryConstrainedCacheManager();//使用内存缓存 return cacheManager; } //配置ehcache @Bean(name = "ehCacheManager") public EhCacheManager ehCacheManager(){ EhCacheManager ehCacheManager = new EhCacheManager(); ehCacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml"); return ehCacheManager; } //在用spring管理我们的类的时候有时候希望有些属性值是来源于一些配置文件,系统属性,或者一些方法调用的结果, // 对于前两种使用方式可以使用spring的PropertyPlaceholderConfigurer类来注入, // 对于后一种则可以使用org.springframework.beans.factory.config.MethodInvokingFactoryBean类来生成需要注入的bean的属性。 // 通过MethodInvokingFactory Bean类,可注入方法返回值。 // MethodInvokingFactoryBean用来获得某个方法的返回值,该方法既可以是静态方法,也可以是实例方法。 // 该方法的返回值可以注入bean实例属性,也可以直接定义成bean实例 //可查看http://blog.sina.com.cn/s/blog_72ef7bea0102wa0v.html /** * 让某个实例的某个方法的返回值注入为Bean的实例 * Spring静态注入 * @param myShiroRealm * @param rememberMeManager * @param ehCacheManager * @return */ @Bean(name = "methodInvokingFactoryBean") public MethodInvokingFactoryBean methodInvokingFactoryBean( @Qualifier("myShiroRealm") MyShiroRealm myShiroRealm, @Qualifier("rememberMeManager") CookieRememberMeManager rememberMeManager, @Qualifier("ehCacheManager")EhCacheManager ehCacheManager){ MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean(); factoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager"); factoryBean.setArguments(new Object[]{securityManager(myShiroRealm, rememberMeManager,ehCacheManager)}); return factoryBean; } }
自定义realm:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import cn.hutool.core.lang.Validator; import com.example.springbootshiro.entity.Permission; import com.example.springbootshiro.entity.Role; import com.example.springbootshiro.entity.User; import com.example.springbootshiro.service.PermissionService; import com.example.springbootshiro.service.RoleService; import com.example.springbootshiro.service.UserService; import lombok.extern.log4j.Log4j2; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.crypto.hash.SimpleHash; 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.List; @Log4j2 public class MyShiroRealm extends AuthorizingRealm { @Autowired private UserService userService; @Autowired private RoleService roleService; @Autowired private PermissionService permissionService; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { log.info("进入授权【doGetAuthorizationInfo】"); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); User user = (User) principals.getPrimaryPrincipal(); List<Role> roleList = roleService.getRoleListByUserId(user.getId()); for (Role role : roleList) { //添加角色 authorizationInfo.addRole(role.getRole()); List<Permission> permissionList = permissionService.getPermissionListByRoleId(role.getId()); for (Permission permission : permissionList) { //添加权限 authorizationInfo.addStringPermission(permission.getPermission()); } } return authorizationInfo; } /** * 登录认证 * @param authenticationToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //加入这一步的目的是在post请求的时候会先认证,然后再请求 if (authenticationToken.getPrincipal() == null){ return null; } log.info("进入认证【doGetAuthenticationInfo】"); UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; String username = token.getUsername(); User user = null; if (Validator.isEmail(username)){ user = userService.getUserByEmail(username); }else { user = userService.getUserByUserName(username); } if (user == null){ log.info("用户不存在!"); return null; } //封装AuthenticationInfo,准备验证 SimpleAuthenticationInfo info = new SimpleAuthenticationInfo( user, user.getPassword(), ByteSource.Util.bytes(user.getSalt()), getName()); return info; } /** * 重写方法,清除当前用户的的 授权缓存 * @param principals */ @Override public void clearCachedAuthorizationInfo(PrincipalCollection principals) { super.clearCachedAuthorizationInfo(principals); } /** * 重写方法,清除当前用户的 认证缓存 * @param principals */ @Override public void clearCachedAuthenticationInfo(PrincipalCollection principals) { super.clearCachedAuthenticationInfo(principals); } @Override public void clearCache(PrincipalCollection principals) { super.clearCache(principals); } /** * 自定义方法:清除所有 授权缓存 */ public void clearAllCachedAuthorizationInfo() { getAuthorizationCache().clear(); } /** * 自定义方法:清除所有 认证缓存 */ public void clearAllCachedAuthenticationInfo() { getAuthenticationCache().clear(); } /** * 自定义方法:清除所有的 认证缓存 和 授权缓存 */ public void clearAllCache() { clearAllCachedAuthenticationInfo(); clearAllCachedAuthorizationInfo(); } /** * 加密 * @param args */ public static void main(String[] args) { String hashAlgorithName = "MD5"; String password = "123"; int hashIterations = 1024; ByteSource credentialsSalt = ByteSource.Util.bytes("tom"); Object obj = new SimpleHash(hashAlgorithName, password, credentialsSalt, hashIterations); log.info(obj); } }
ehcache-shiro.xml:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<?xml version="1.0" encoding="UTF-8"?> <ehcache name="es"> <!-- 缓存对象存放路径 java.io.tmpdir:默认的临时文件存放路径。 user.home:用户的主目录。 user.dir:用户的当前工作目录,即当前程序所对应的工作路径。 其它通过命令行指定的系统属性,如“java –DdiskStore.path=D:\abc ……”。 --> <diskStore path="java.io.tmpdir"/> <!-- name:缓存名称。 maxElementsOnDisk:硬盘最大缓存个数。0表示不限制 maxEntriesLocalHeap:指定允许在内存中存放元素的最大数量,0表示不限制。 maxBytesLocalDisk:指定当前缓存能够使用的硬盘的最大字节数,其值可以是数字加单位,单位可以是K、M或者G,不区分大小写, 如:30G。当在CacheManager级别指定了该属性后,Cache级别也可以用百分比来表示, 如:60%,表示最多使用CacheManager级别指定硬盘容量的60%。该属性也可以在运行期指定。当指定了该属性后会隐式的使当前Cache的overflowToDisk为true。 maxEntriesInCache:指定缓存中允许存放元素的最大数量。这个属性也可以在运行期动态修改。但是这个属性只对Terracotta分布式缓存有用。 maxBytesLocalHeap:指定当前缓存能够使用的堆内存的最大字节数,其值的设置规则跟maxBytesLocalDisk是一样的。 maxBytesLocalOffHeap:指定当前Cache允许使用的非堆内存的最大字节数。当指定了该属性后,会使当前Cache的overflowToOffHeap的值变为true, 如果我们需要关闭overflowToOffHeap,那么我们需要显示的指定overflowToOffHeap的值为false。 overflowToDisk:boolean类型,默认为false。当内存里面的缓存已经达到预设的上限时是否允许将按驱除策略驱除的元素保存在硬盘上,默认是LRU(最近最少使用)。 当指定为false的时候表示缓存信息不会保存到磁盘上,只会保存在内存中。 该属性现在已经废弃,推荐使用cache元素的子元素persistence来代替,如:<persistence strategy=”localTempSwap”/>。 diskSpoolBufferSizeMB:当往磁盘上写入缓存信息时缓冲区的大小,单位是MB,默认是30。 overflowToOffHeap:boolean类型,默认为false。表示是否允许Cache使用非堆内存进行存储,非堆内存是不受Java GC影响的。该属性只对企业版Ehcache有用。 copyOnRead:当指定该属性为true时,我们在从Cache中读数据时取到的是Cache中对应元素的一个copy副本,而不是对应的一个引用。默认为false。 copyOnWrite:当指定该属性为true时,我们在往Cache中写入数据时用的是原对象的一个copy副本,而不是对应的一个引用。默认为false。 timeToIdleSeconds:单位是秒,表示一个元素所允许闲置的最大时间,也就是说一个元素在不被请求的情况下允许在缓存中待的最大时间。默认是0,表示不限制。 timeToLiveSeconds:单位是秒,表示无论一个元素闲置与否,其允许在Cache中存在的最大时间。默认是0,表示不限制。 eternal:boolean类型,表示是否永恒,默认为false。如果设为true,将忽略timeToIdleSeconds和timeToLiveSeconds,Cache内的元素永远都不会过期,也就不会因为元素的过期而被清除了。 diskExpiryThreadIntervalSeconds :单位是秒,表示多久检查元素是否过期的线程多久运行一次,默认是120秒。 clearOnFlush:boolean类型。表示在调用Cache的flush方法时是否要清空MemoryStore。默认为true。 diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false. maxElementsInMemory:缓存最大数目 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。 memoryStoreEvictionPolicy: Ehcache的三种清空策略; FIFO,first in first out,这个是大家最熟的,先进先出。 LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。 LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。 --> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="0" timeToLiveSeconds="0" overflowToDisk="false" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" /> <!-- 授权缓存 --> <cache name="authorizationCache" maxEntriesLocalHeap="2000" eternal="false" timeToIdleSeconds="0" timeToLiveSeconds="0" overflowToDisk="false" statistics="true"> </cache> <!-- 认证缓存 --> <cache name="authenticationCache" maxEntriesLocalHeap="2000" eternal="false" timeToIdleSeconds="0" timeToLiveSeconds="0" overflowToDisk="false" statistics="true"> </cache> </ehcache>
mysql建表sql:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for permission -- ---------------------------- DROP TABLE IF EXISTS `permission`; CREATE TABLE `permission` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '权限id', `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '权限名称', `permission` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '权限', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of permission -- ---------------------------- INSERT INTO `permission` VALUES (1, '用户管理', 'user:list'); INSERT INTO `permission` VALUES (2, '用户添加/更新', 'user:add'); INSERT INTO `permission` VALUES (3, '用户删除', 'user:delete'); INSERT INTO `permission` VALUES (4, '用户编辑页面', 'user:edit'); INSERT INTO `permission` VALUES (5, '文章管理', 'post:list'); INSERT INTO `permission` VALUES (6, '文章查询', 'post:search'); INSERT INTO `permission` VALUES (7, '创建文章页面', 'post:new'); INSERT INTO `permission` VALUES (8, '添加/修改文章', 'post:save'); INSERT INTO `permission` VALUES (9, '编辑文章页面', 'post:edit'); -- ---------------------------- -- Table structure for role -- ---------------------------- DROP TABLE IF EXISTS `role`; CREATE TABLE `role` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '角色id', `role` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '角色', `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '角色描述', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of role -- ---------------------------- INSERT INTO `role` VALUES (1, 'admin', '管理员'); INSERT INTO `role` VALUES (2, 'author', '作者'); INSERT INTO `role` VALUES (3, 'subscriber', '订阅者'); -- ---------------------------- -- Table structure for role_permission_ref -- ---------------------------- DROP TABLE IF EXISTS `role_permission_ref`; CREATE TABLE `role_permission_ref` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '关联表role_permission_id', `role_id` int(11) NOT NULL COMMENT '角色id', `permission_id` int(11) NOT NULL COMMENT '权限id', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of role_permission_ref -- ---------------------------- INSERT INTO `role_permission_ref` VALUES (1, 1, 1); INSERT INTO `role_permission_ref` VALUES (2, 1, 2); INSERT INTO `role_permission_ref` VALUES (3, 1, 3); INSERT INTO `role_permission_ref` VALUES (4, 1, 4); INSERT INTO `role_permission_ref` VALUES (5, 1, 5); INSERT INTO `role_permission_ref` VALUES (6, 1, 6); INSERT INTO `role_permission_ref` VALUES (7, 1, 7); INSERT INTO `role_permission_ref` VALUES (8, 1, 8); INSERT INTO `role_permission_ref` VALUES (9, 1, 9); INSERT INTO `role_permission_ref` VALUES (10, 2, 1); -- ---------------------------- -- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户id', `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名', `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码', `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '姓名', `salt` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '盐值', `email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱', `create_time` timestamp(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '创建时间', `update_time` timestamp(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES (1, 'tom', '9ade9a62fe19ed021b87f8dff0236ad7', '汤姆', 'n3R2XG', 'tom@tom.com', '2019-05-05 10:38:49', '2019-05-05 10:38:49'); INSERT INTO `user` VALUES (2, 'cat', '5b345ca8ab78e8728cc8448203246496', 'CAT', '1gvEVM', 'cat@cat.com', '2019-05-05 10:39:11', '2019-05-05 10:39:11'); -- ---------------------------- -- Table structure for user_role_ref -- ---------------------------- DROP TABLE IF EXISTS `user_role_ref`; CREATE TABLE `user_role_ref` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '关联表user_role_id', `user_id` int(11) NOT NULL COMMENT '用户id', `role_id` int(11) NOT NULL COMMENT '角色id', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of user_role_ref -- ---------------------------- INSERT INTO `user_role_ref` VALUES (1, 1, 1); INSERT INTO `user_role_ref` VALUES (2, 1, 2); INSERT INTO `user_role_ref` VALUES (3, 1, 3); INSERT INTO `user_role_ref` VALUES (4, 2, 2); SET FOREIGN_KEY_CHECKS = 1;