Springboot+Shiro+Mybatis+mysql实现权限安全认证的示例代码
Shiro是Apache 的一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。Shiro 主要分为两个部分就是认证和授权两部分
一、介绍
- Subject代表了当前用户的安全操作
- SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
- Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。
- Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。
- Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
- sessionManager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上。
Shiro相关类介绍
- (1)Authentication 认证 —— 用户登录
- (2)Authorization 授权 —- 用户具有哪些权限
- (3)Cryptography 安全数据加密
- (4)Session Management 会话管理
- (5)Web Integration web系统集成
- (6)Interations 集成其它应用,spring、缓存框架
二、依赖引入
完整的pom文件如下:
<?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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.1</version> <relativePath></relativePath> <!-- lookup parent from repository --> </parent> <groupId>com.gt.shiro</groupId> <artifactId>com.sunyue.shiro</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <properties> <java.version>1.8</java.version> <druid.verzion>1.1.10</druid.verzion> <pagehelper.version>1.2.10</pagehelper.version> <mybatis.version>2.1.4</mybatis.version> <thymeleaf-layout-dialect.version>2.0.4</thymeleaf-layout-dialect.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!-- 排除默认的tomcat --> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <!-- 重新依赖Jetty的starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency> <!--shiro整合spring--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>${druid.verzion}</version> </dependency> <!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper-spring-boot-starter --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>${pagehelper.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <!-- spring boot maven插件 --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>com.gt.shiro.SpringShiroApplication</mainClass> </configuration> </plugin> </plugins> </build> </project>
三、配置文件
application.yml配置文件: # 开发时关闭缓存,不然没法看到实时页面 spring.thymeleaf.cache=false # 用非严格的 HTML spring.thymeleaf.mode=HTML spring.thymeleaf.encoding=utf-8 spring.thymeleaf.servlet.content-type=text/html spring.datasource.druid.url=jdbc:mysql://localhost:3306/shiro?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC spring.datasource.druid.username=root spring.datasource.druid.password=admin spring.datasource.druid.initial-size=1 spring.datasource.druid.min-idle=1 spring.datasource.druid.max-active=20 spring.datasource.druid.test-on-borrow=true #springbootjdbc导入包不和以前一样 spring.datasource.druid.driver-class-name= com.mysql.cj.jdbc.Driver mybatis.type-aliases-package=com.gt.shiro.entity mybatis.mapper-locations=classpath:mapper/*.xml #打印数据库的操作 logging.level.com.example.springsecurity.dao=debug #redis缓存 ### 配置Redis mybatis.configuration.cache-enabled=true # Redis数据库索引(默认为0) spring.redis.database=0 # Redis服务器地址 spring.redis.host=... # Redis服务器连接端口 spring.redis.port=6379 # Redis服务器连接密码(默认为空) spring.redis.password=sunyue # 连接池最大连接数(使用负值表示没有限制) spring.redis.jedis.pool.max-idle=200 # 连接池最大阻塞等待时间(使用负值表示没有限制) spring.redis.jedis.pool.max-wait=-1 # 连接池中的最小空闲连接 spring.redis.jedis.pool.min-idle=0 # 连接超时时间(毫秒) spring.redis.timeout=1000
Shiro两个重要的配置类:
1.UserRealm
package com.gt.shiro.config; import com.gt.shiro.entity.TestUser; import com.gt.shiro.server.TestUserServer; import org.apache.shiro.SecurityUtils; 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.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class UserRealm extends AuthorizingRealm { @Autowired private TestUserServer testUserServer; /** * 执行授权逻辑 * * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("执行授权逻辑"); /*获取当前登录的用户信息*/ Subject subject = SecurityUtils.getSubject(); TestUser testUser = (TestUser) subject.getPrincipal(); //设置角色,多个角色 /*Set<String> rolesSet = new HashSet<>(); rolesSet.add(testUser.getRole());*/ //SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(rolesSet); //给资源进行授权 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); /*可以在以下list加入多个权限*/ /*List<String> roles = new ArrayList<>(); roles.add(testUser.getPerms()); info.addRoles(roles);*/ //设置权限 info.addRole(testUser.getRole()); //需要判断权限是否为空值(null是没有地址,""是有地址但是里面的内容是空的) if (testUser.getPerms() != null && !testUser.getPerms().equals("")) { info.addStringPermission(testUser.getPerms()); } return info; } /** * 执行认证逻辑 * * @param authenticationToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("执行认证逻辑"); /*获取令牌*/ UsernamePasswordToken passwordToken = (UsernamePasswordToken) authenticationToken; //取出用户名并且判断用户名是否和数据库一致 TestUser testUser = testUserServer.selectOneByName(passwordToken.getUsername()); if (testUser != null) { //进行认证,将正确数据给shiro处理 //密码不用自己比对,AuthenticationInfo认证信息对象,一个接口,new他的实现类对象SimpleAuthenticationInfo /* 第一个参数随便放,可以放user对象,程序可在任意位置获取 放入的对象 * 第二个参数必须放密码, * 第三个参数放 当前realm的名字,因为可能有多个realm*/ //若密码不正确则返回IncorrectCredentialsException异常 return new SimpleAuthenticationInfo(testUser, testUser.getPassword(), this.getName()); } //若用户名不存在则返回UnknownAccountException异常 return null; } }
2.ShiroConfig
package com.gt.shiro.config; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver; import java.util.LinkedHashMap; import java.util.Map; import java.util.Properties; @Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //设置安全管理器 shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager); //添加一些Shiro的内置过滤器 /** * Shiro 的内置过滤器可以实现权限的相关拦截 * 常用过滤器 * 1.anon:无需认证 * 2.authc:必须认证才能访问 * 3.user:如果使用rememberme功能可以访问 * 4.perms:对应权限才能访问 * 5.role:对应角色才能访问 */ //登录状态下才可以访问main页面,manage权限可访问manage页面,admin角色可访问admin页面 Map<String, String> filterMap = new LinkedHashMap<String, String>(); filterMap.put("/main", "authc"); filterMap.put("/manage", "perms[manage]"); filterMap.put("/admin", "roles[admin]"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap); //未登录状态下访问将跳转至login页面 // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面 shiroFilterFactoryBean.setLoginUrl("/login"); // 登录成功后要跳转的链接 shiroFilterFactoryBean.setSuccessUrl("/"); //无授限状态下访问将请求unauthor shiroFilterFactoryBean.setUnauthorizedUrl("/unAuth"); return shiroFilterFactoryBean; } @Bean(name = "securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) { DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(); //DefaultWebSecurityManager需要关联一个Realm defaultWebSecurityManager.setRealm(userRealm); return defaultWebSecurityManager; } /** * 创建realm */ @Bean(name = "userRealm") public UserRealm getRealm() { return new UserRealm(); } @Bean public ShiroDialect shiroDialect() { return new ShiroDialect(); } /** * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions) * 配置以下两个bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)即可实现此功能 * * @return */ @Bean public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); advisorAutoProxyCreator.setProxyTargetClass(true); return advisorAutoProxyCreator; } /** * 开启 shiro 的@RequiresPermissions注解 * * @param securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } /** * shiro出现权限异常可通过此异常实现制定页面的跳转(或接口跳转) * * @return */ @Bean public SimpleMappingExceptionResolver simpleMappingExceptionResolver() { SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver(); Properties properties = new Properties(); /*未授权处理页*/ properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "/error.html"); /*身份没有验证*/ properties.setProperty("org.apache.shiro.authz.UnauthenticatedException", "/error.html"); resolver.setExceptionMappings(properties); return resolver; } }
四、数据连接和业务逻辑
1.实体类
package com.gt.shiro.entity; import lombok.Data; import lombok.experimental.Accessors; import java.io.Serializable; import java.util.Date; @Data @Accessors(chain = true) public class TestUser implements Serializable { private Integer id; private String username; private String password; /*权限*/ private String perms; /*角色*/ private String role; /*加盐密码*/ private String salt; }
2.Dao和Mapper
package com.gt.shiro.dao; import com.gt.shiro.entity.TestUser; import org.apache.ibatis.annotations.Mapper; import java.util.List; @Mapper public interface TestUserMapper { List<TestUser> findAll(); TestUser selectOne(Integer id); TestUser selectOneByName(String username); void insert(TestUser testUser); void update(TestUser testUser); void delete(Integer id); }
<?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.gt.shiro.dao.TestUserMapper"> <select id="findAll" resultType="TestUser"> select * from test_user </select> <select id="selectOne" resultType="TestUser"> select * from test_user where id=#{id} </select> <select id="selectOneByName" resultType="TestUser"> select * from test_user where username=#{username} </select> <insert id="insert"> insert into test_user (id,username,password,perms,role,salt) value (#{id},#{username},#{password},#{perms},#{role},#{salt}) </insert> <update id="update"> update test_user set username = #{username},password=#{password},perms=#{perms},role=#{role},salt=#{salt} where id = #{id} </update> <delete id="delete"> delete from test_user where id = #{id} </delete> </mapper>
3.业务层及其实现
package com.gt.shiro.server; import com.gt.shiro.entity.TestUser; import org.springframework.stereotype.Service; import java.util.List; @Service public interface TestUserServer { /*查询所有*/ List<TestUser> selectAll(); /*查询一个用户*/ TestUser selectByOne(Integer id); /*通过名字查询一个用户*/ TestUser selectOneByName(String name); /*增加一个用户*/ void insert(TestUser testUser); /*删除一个用户*/ void delete(Integer id); /*更新一个用户*/ void update(TestUser testUser); }
package com.gt.shiro.server.serverImpl; import com.gt.shiro.dao.TestUserMapper; import com.gt.shiro.entity.TestUser; import org.apache.shiro.crypto.SecureRandomNumberGenerator; import org.apache.shiro.crypto.hash.SimpleHash; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.sunyue.shiro.server.TestUserServer; import java.util.List; @Service public class TestUserServerImpl implements TestUserServer { @Autowired private TestUserMapper testUserMapper; @Override public List<TestUser> selectAll() { return testUserMapper.findAll(); } @Override public TestUser selectByOne(Integer id) { return testUserMapper.selectOne(id); } @Override public TestUser selectOneByName(String name) { return testUserMapper.selectOneByName(name); } @Override public void insert(TestUser testUser) { //加密写法 String salt = new SecureRandomNumberGenerator().nextBytes().toString(); String password= new SimpleHash("md5",testUser.getPassword(),salt,2).toString(); testUser.setPassword(password); testUser.setSalt(salt); testUserMapper.insert(testUser); } @Override public void delete(Integer id) { testUserMapper.delete(id); } @Override public void update(TestUser testUser) { testUserMapper.update(testUser); } }
4.控制层
package com.gt.shiro.controller; import com.gt.shiro.entity.TestUser; import com.gt.shiro.server.TestUserServer; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.crypto.hash.SimpleHash; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; @Controller public class indexController { @Autowired private TestUserServer testUserServer; @GetMapping("/{url}") public String redirect(@PathVariable("url") String url) { return url; } @RequestMapping(value = {"/", "/index"}, method = RequestMethod.GET) private String index() { return "index"; } @PostMapping("/login") public String login(String username, String password, Model model) { Subject subject = SecurityUtils.getSubject(); TestUser testUser = testUserServer.selectOneByName(username); if (testUser != null) { //根据salt值和用户输入的密码计算加密后的密码 String salt = testUser.getSalt(); password = new SimpleHash("md5", password, salt, 2).toString(); System.out.println(password); } UsernamePasswordToken token = new UsernamePasswordToken(username, password); //UsernamePasswordToken token = new UsernamePasswordToken(username, testUser.getPassword());(不加密写法) try { //将用户名和密码通过token传给shiro进行认证 subject.login(token); TestUser user = (TestUser) subject.getPrincipal(); subject.getSession().setAttribute("testUser", user); return "index"; } catch (UnknownAccountException e) { e.printStackTrace(); model.addAttribute("msg", "用户名不存在"); return "login"; } catch (IncorrectCredentialsException e) { e.printStackTrace(); model.addAttribute("msg", "密码有误"); return "login"; } } @ResponseBody @GetMapping("/unauthor") public String unauthor() { return "权限不足,无法访问"; } @GetMapping("/logout") public String logout() { Subject subject = SecurityUtils.getSubject(); subject.logout(); return "login"; } @PostMapping("/register") public String register(TestUser testUser, Model model) { String username = testUser.getUsername(); String password = testUser.getPassword(); if (username ** null || username.equals("")) { model.addAttribute("msg", "用户名不能为空"); return "register"; } else if (password ** null || password.equals("")) { model.addAttribute("msg", "密码不能为空"); return "register"; } else if (testUserServer.selectOneByName(username) != null) { model.addAttribute("msg", "用户名已被占用"); return "register"; } else { testUserServer.insert(testUser); return "login"; } } }
5.前端页面
(1)index.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.thymrleaf.org/thymeleaf-extras-shiro"> <head> <meta charset="UTF-8"> <title>Insert title here</title> <link rel="shortcut icon" href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" /> </head> <body> <div th:if="${session.testUser != null}"> <span th:text="'欢迎回来 '+${session.testUser.username}+'! '"> </span><a href="/logout" rel="external nofollow" >退出</a> </div> <a href="/main" rel="external nofollow" >main</a> <span shiro:hasPermission="manage"> | <a href="/manage" rel="external nofollow" >manage</a></span> <span shiro:hasRole="admin"> | <a href="/admin" rel="external nofollow" >admin</a></span> <br> </body> </html>
(2)login.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Insert title here</title> <link rel="shortcut icon" href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" /> </head> <body> <form action="/login" method="post"> <span th:text="${msg}" style="color: red"></span> <table> <tr> <td>用户名:</td> <td><input type="text" name="username"/></td> </tr> <tr> <td>密码:</td> <td><input type="password" name="password"/></td> </tr> <tr> <td><input type="submit" value="登录"/></td> <td><a href="/register" rel="external nofollow" > <button type="button" value="注册">注册</button> </a> </td> </tr> </table> </form> </body> </html>
(3)register.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Insert title here</title> <link rel="shortcut icon" href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" /> </head> <body> <form action="/register" method="post"> <span th:text="${msg}" style="color: red"></span> <table> <tr> <td>用户名:</td> <td><input type="text" name="username"/></td> </tr> <tr> <td>密码:</td> <td><input type="password" name="password"/></td> </tr> <tr> <td><input type="submit" value="注册"/></td> </tr> </table> </form> </body> </html>
(4)main.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <link rel="shortcut icon" href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" /> </head> <body> <h1>main</h1> </body> </html>
(5)manage.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <link rel="shortcut icon" href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" /> </head> <body> <h1>manage</h1> </body> </html>
(6)admin.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <link rel="shortcut icon" href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" /> </head> <body> <h1>admin</h1> </body> </html>
6.数据库文件
/* Navicat MySQL Data Transfer Source Server : sunyue Source Server Version : 50724 Source Host : localhost:3306 Source Database : shiro Target Server Type : MYSQL Target Server Version : 50724 File Encoding : 65001 Date: 2021-01-11 22:00:47 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for test_user -- ---------------------------- DROP TABLE IF EXISTS `test_user`; CREATE TABLE `test_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(120) DEFAULT NULL, `password` varchar(120) DEFAULT NULL, `perms` varchar(120) DEFAULT NULL, `role` varchar(120) DEFAULT NULL, `salt` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of test_user -- ---------------------------- INSERT INTO `test_user` VALUES ('4', 'admin', '4867df2e009d0096c4cd8d9be8cc104c', 'manage', 'admin', 'GQR2m1N1o3nSLjtOzMITRQ**'); INSERT INTO `test_user` VALUES ('5', 'user', '636502f40cf197dd2f4b19f56f475b24', '', '', 'Kxw3HZiFmgnlUu8fmjMY7Q**'); INSERT INTO `test_user` VALUES ('6', 'user1', '43f3133aa7e0ef9cf8373521dff8d8e8', 'manage', null, 'J8fn4HpauvNOrlUaRl/Spg**'); INSERT INTO `test_user` VALUES ('7', '1', '1', 'manage', null, null);
到此这篇关于Springboot+Shiro+Mybatis+mysql实现权限安全认证的文章就介绍到这了,更多相关Springboot Shiro Mybatis mysql权限安全认证内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
tomcat启动完成执行 某个方法 定时任务(Spring)操作
这篇文章主要介绍了tomcat启动完成执行 某个方法 定时任务(Spring)操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧2020-09-09Spring注解开发@Bean和@ComponentScan使用案例
这篇文章主要介绍了Spring注解开发@Bean和@ComponentScan使用案例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下2020-09-09java 使用BeanFactory实现service与dao层解耦合详解
这篇文章主要介绍了java 使用BeanFactory实现service与dao层解耦合详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2021-12-12Java 8 中 Map 骚操作之 merge() 的使用方法
本文简单介绍了一下Map.merge()的方法,除此之外,Java 8 中的HashMap实现方法使用了TreeNode和 红黑树,原理很相似,今天通过本文给大家介绍Java 8 中 Map 骚操作之 merge() 的用法 ,需要的朋友参考下吧2021-07-07
最新评论